| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package MarpaX::Languages::PowerBuilder::SRQ; | 
| 2 | 1 |  |  | 1 |  | 1183 | no if $] >= 5.018, warnings => "experimental::smartmatch"; | 
|  | 1 |  |  |  |  | 11 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 3 | 1 |  |  | 1 |  | 53 | use base qw(MarpaX::Languages::PowerBuilder::base); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1581 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | #a SRQ parser and compiler to SQL by Nicolas Georges | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | sub unref{ | 
| 8 | 0 | 0 |  | 0 | 0 |  | return unless defined wantarray; | 
| 9 | 0 |  |  |  |  |  | my $val = shift; | 
| 10 | 0 |  |  |  |  |  | my $ref = ref $val; | 
| 11 | 0 | 0 |  |  |  |  | return unless $ref; | 
| 12 |  |  |  |  |  |  | my $unref={ | 
| 13 | 0 |  |  | 0 |  |  | ARRAY  => sub{ @$val }, | 
| 14 | 0 |  |  | 0 |  |  | HASH   => sub{ %$val }, | 
| 15 | 0 |  |  | 0 |  |  | SCALAR => sub{ $$val }, | 
| 16 | 0 |  |  | 0 |  |  | GLOB   => sub{ $$val }, | 
| 17 | 0 |  |  | 0 |  |  | REF    => sub{ $$val }, | 
| 18 | 0 |  |  | 0 |  |  | Regexp => sub{ $val  },   #don't unref a regexp. | 
| 19 | 0 |  |  |  |  |  | }; | 
| 20 | 0 | 0 |  |  |  |  | return $unref->{$ref}() if exists $unref->{$ref}; | 
| 21 | 0 |  |  |  |  |  | for(keys %$unref){ | 
| 22 | 0 | 0 |  |  |  |  | return $unref->{$_}() | 
| 23 |  |  |  |  |  |  | if $val->isa($_); | 
| 24 |  |  |  |  |  |  | } | 
| 25 | 0 |  |  |  |  |  | return; | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | sub value{ | 
| 29 | 0 |  |  | 0 | 0 |  | my $self =shift; | 
| 30 |  |  |  |  |  |  | #lazzy retrieve of value | 
| 31 | 0 | 0 |  |  |  |  | $self->{value} = $self->{recce}->value unless exists $self->{value}; | 
| 32 | 0 |  |  |  |  |  | $self->{value}; | 
| 33 |  |  |  |  |  |  | } | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | sub sql{ | 
| 36 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 37 | 0 |  |  |  |  |  | my $val = $self->value(); | 
| 38 | 0 |  |  |  |  |  | return _compile( $$val ); | 
| 39 |  |  |  |  |  |  | } | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | sub _compile{ | 
| 42 | 0 |  |  | 0 |  |  | my $ast = shift; | 
| 43 | 0 |  | 0 |  |  |  | my $level = shift // 1; | 
| 44 | 0 |  |  |  |  |  | my $tabs = "\t" x $level; | 
| 45 | 0 | 0 |  |  |  |  | my $select = exists $ast->{select} ? $ast->{select} : $ast; | 
| 46 | 0 |  |  |  |  |  | my $sql; | 
| 47 |  |  |  |  |  |  | #arguments | 
| 48 | 0 |  |  |  |  |  | foreach my $arg(unref $ast->{arguments}){ | 
| 49 | 0 |  |  |  |  |  | $sql .= "// argument $arg->{name} ($arg->{type})\n"; | 
| 50 |  |  |  |  |  |  | } | 
| 51 | 0 |  |  |  |  |  | $sql .= "SELECT"; | 
| 52 | 0 | 0 |  |  |  |  | $sql .= ' DISTINCT ' if exists $select->{distinct}; | 
| 53 | 0 |  |  |  |  |  | $sql .= "\n\t"; | 
| 54 | 0 |  | 0 |  |  |  | $sql .= join ",\n\t", map{ $$_ } unref $select->{selection}//[]; | 
|  | 0 |  |  |  |  |  |  | 
| 55 | 0 |  |  |  |  |  | $sql .= "\n\tFROM "; | 
| 56 | 0 |  | 0 |  |  |  | $sql .= join ",\n\t", unref $select->{tables}//[]; | 
| 57 |  |  |  |  |  |  | #joins are threated like where clause | 
| 58 | 0 | 0 | 0 |  |  |  | if(unref $select->{wheres}//[] + unref $select->{joins}//[]){ | 
|  |  |  | 0 |  |  |  |  | 
| 59 | 0 |  |  |  |  |  | $sql .= "\n\tWHERE "; | 
| 60 | 0 |  |  |  |  |  | my $where = "("; | 
| 61 | 0 |  |  |  |  |  | foreach( unref $select->{wheres} ){ | 
| 62 | 0 |  |  |  |  |  | $where .= "\t"; | 
| 63 | 0 |  |  |  |  |  | $where .= "($_->{exp1} " . uc($_->{op})." "; | 
| 64 | 0 | 0 |  |  |  |  | if(ref $_->{exp2}){ | 
| 65 | 0 |  |  |  |  |  | $where .= "(" . _compile($_->{exp2}, $level+1) . ")"; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  | else{ | 
| 68 | 0 |  |  |  |  |  | $where .= "$_->{exp2}"; | 
| 69 |  |  |  |  |  |  | } | 
| 70 | 0 |  |  |  |  |  | $where .= ")"; | 
| 71 | 0 | 0 |  |  |  |  | $where .= uc " $_->{logic}\n" if exists $_->{logic}; | 
| 72 |  |  |  |  |  |  | } | 
| 73 | 0 |  |  |  |  |  | $where .=")\n"; | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 0 |  |  |  |  |  | my @joins = map{ "\t(" . join(" ", $_->{left}, uc($_->{op}), $_->{right}).")" } unref $select->{joins}; | 
|  | 0 |  |  |  |  |  |  | 
| 76 | 0 |  |  |  |  |  | $sql .= join " AND\n", @joins, $where; | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  | #groups | 
| 79 | 0 | 0 | 0 |  |  |  | if(unref $select->{groups}//[]){ | 
| 80 | 0 |  |  |  |  |  | $sql .= "\tGROUP BY "; | 
| 81 | 0 |  |  |  |  |  | $sql .= join ",\n\t", unref $select->{groups}; | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  | #havings | 
| 84 | 0 | 0 | 0 |  |  |  | if(unref $select->{havings}//[]){ | 
| 85 | 0 |  |  |  |  |  | $sql .= "\n\tHAVING "; | 
| 86 | 0 |  |  |  |  |  | foreach( unref $select->{havings} ){ | 
| 87 | 0 |  |  |  |  |  | $sql .= "\t"; | 
| 88 | 0 |  |  |  |  |  | $sql .= "($_->{exp1} " . uc($_->{op})." "; | 
| 89 | 0 |  |  |  |  |  | $sql .= "$_->{exp2})"; | 
| 90 | 0 | 0 |  |  |  |  | $sql .= uc " $_->{logic}\n" if exists $_->{logic}; | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | #unions | 
| 94 | 0 | 0 |  |  |  |  | $sql .= "\n" if exists $select->{unions}; | 
| 95 | 0 |  | 0 |  |  |  | foreach my $union ( unref $select->{unions}//[] ){ | 
| 96 | 0 |  |  |  |  |  | $sql .= "UNION(\n"; | 
| 97 | 0 |  |  |  |  |  | $sql .= _compile( $union, $level+1 ); | 
| 98 | 0 |  |  |  |  |  | $sql .= ")\n"; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  | #orders | 
| 101 | 0 | 0 |  |  |  |  | if(exists $ast->{orders}){ | 
| 102 | 0 |  |  |  |  |  | $sql .= "\tORDER BY "; | 
| 103 | 0 |  |  |  |  |  | $sql .= join ",\n\t" , map { $_->{name} . " " . uc $_->{dir} } unref $ast->{orders}; | 
|  | 0 |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 0 | 0 |  |  |  |  | if($level > 1){ | 
| 107 | 0 |  |  |  |  |  | $sql =~ s/^/$tabs/gm; | 
| 108 |  |  |  |  |  |  | } | 
| 109 | 0 |  |  |  |  |  | return $sql; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | sub version{ | 
| 113 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 114 | 0 |  |  |  |  |  | return { lc $name => $children[1] }; | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | sub table{ | 
| 118 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 119 | 0 |  |  |  |  |  | return $children[3]; | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | sub tables{ | 
| 123 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 124 | 0 |  |  |  |  |  | return { 'tables' => \@children }; | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  |  | 
| 127 | 0 | 0 |  | 0 | 0 |  | sub distinct{ { 'distinct' => @_>1?1:0 } } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | sub column{ | 
| 130 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 131 | 0 |  |  |  |  |  | return bless \$children[3], 'column'; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | sub selection{ | 
| 135 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 136 | 0 |  |  |  |  |  | return { 'selection' => \@children }; | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | sub compute{ | 
| 140 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 141 | 0 |  |  |  |  |  | return bless \$children[3], 'compute'; | 
| 142 |  |  |  |  |  |  | } | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | sub join{ | 
| 145 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 146 | 0 |  |  |  |  |  | return { left => $children[3], op => $children[6], right => $children[9] }; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | sub joins{ | 
| 150 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 151 | 0 |  |  |  |  |  | return { 'joins' => \@children }; | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | sub argument{ | 
| 155 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 156 | 0 |  |  |  |  |  | return { name => $children[3], type => $children[6] }; | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | sub arguments{ | 
| 160 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 161 | 0 |  |  |  |  |  | return { 'arguments' => \@children }; | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | sub where_logic{ | 
| 165 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 166 | 0 |  |  |  |  |  | return { exp1 => $children[3], op => $children[6], exp2 => $children[7], logic => $children[10] }; | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | sub where{ | 
| 170 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 171 | 0 |  |  |  |  |  | return { exp1 => $children[3], op => $children[6], exp2 => $children[7] }; | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | sub where_exp2{ | 
| 175 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 176 | 0 |  |  |  |  |  | return $children[1]; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | sub where_nest{ | 
| 180 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 181 | 0 |  |  |  |  |  | return $children[1]; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub wheres{ | 
| 185 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 186 | 0 |  |  |  |  |  | return { 'wheres' => \@children }; | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | sub group{ | 
| 190 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 191 | 0 |  |  |  |  |  | return $children[3]; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | sub groups{ | 
| 195 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 196 | 0 |  |  |  |  |  | return { 'groups' => \@children }; | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | sub having_logic{ | 
| 200 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 201 | 0 |  |  |  |  |  | return { exp1 => $children[3], op => $children[6], exp2 => $children[7], logic => $children[10] }; | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | sub havings{ | 
| 205 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 206 | 0 |  |  |  |  |  | return { 'havings' => \@children }; | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | sub order{ | 
| 210 | 0 |  |  | 0 | 0 |  | my (undef, $name, @children) = @_; | 
| 211 | 0 | 0 | 0 |  |  |  | return { name => $children[3], dir => (lc($children[6]//'no') eq 'yes')?'asc':'desc' }; | 
| 212 |  |  |  |  |  |  | } | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | sub orders{ | 
| 215 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 216 | 0 |  |  |  |  |  | return \@children; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub pbselect{ | 
| 220 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 221 | 0 |  |  |  |  |  | my %mixed; | 
| 222 | 0 | 0 |  |  |  |  | %mixed = (%mixed, %$_) for grep{ exists $_->{unions} ? not $_->{unions} ~~ [] : 1 } grep { ref eq 'HASH' } @children; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 223 | 0 |  |  |  |  |  | return \%mixed; | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  |  | 
| 226 | 0 |  |  | 0 | 0 |  | sub unions{ shift; { unions => [ @_ ] } } | 
|  | 0 |  |  |  |  |  |  | 
| 227 | 0 |  |  | 0 | 0 |  | sub union { $_[3] } | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | sub query{ | 
| 230 | 0 |  |  | 0 | 0 |  | my (undef, @children) = @_; | 
| 231 | 0 |  |  |  |  |  | my $h = { select => $children[0] }; | 
| 232 | 0 | 0 |  |  |  |  | $h->{orders} = $children[1] unless $children[1] ~~ []; | 
| 233 | 0 | 0 |  |  |  |  | $h->{arguments} = $children[2]->{arguments} unless $children[2]->{arguments} ~~ []; | 
| 234 | 0 |  |  |  |  |  | return $h; | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | sub selection_item{ | 
| 238 | 0 |  |  | 0 | 0 |  | my (undef, $item) = @_; | 
| 239 | 0 |  |  |  |  |  | return $item; | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | sub string{ | 
| 243 | 0 |  |  | 0 | 0 |  | my (undef, $string) = @_; | 
| 244 |  |  |  |  |  |  | #remove bounding quotes and escape chars. | 
| 245 | 0 |  |  |  |  |  | $string =~ s/^"|"$//g; | 
| 246 | 0 |  |  |  |  |  | $string =~ s/~(.)/$1/g; | 
| 247 | 0 |  |  |  |  |  | return $string; | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | sub quoted_db_identifier{ | 
| 251 | 0 |  |  | 0 | 0 |  | my (undef, $dbidentifier) = @_; | 
| 252 | 0 |  |  |  |  |  | return $dbidentifier; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | 1; |