1 #-------------------------------------------------------------------------------------------------
2 package OpenILS::Reporter::SQLBuilder;
6 $class = ref($class) || $class;
8 return bless { _sql => undef } => $class;
14 $self->{_params} = $p;
20 return $self->{_builder}->{_params}->{$p};
25 $self->{_builder} = shift;
31 return $self->{_builder};
37 $self->builder->{_relative_time} = $t if (defined $t);
38 return $self->builder->{_relative_time};
45 if (defined($val) && $val =~ /^::(.+)$/o) {
46 $val = $self->get_param($1);
49 if (defined($val) && !ref($val)) {
61 my $rs = OpenILS::Reporter::SQLBuilder::ResultSet->new;
63 if (!$report->{order_by} || @{$report->{order_by}} == 0) {
64 $report->{order_by} = $report->{select};
67 $rs->is_subquery( 1 ) if ( $report->{alias} );
69 $rs ->set_builder( $self )
70 ->set_subquery_alias( $report->{alias} )
71 ->set_select( $report->{select} )
72 ->set_from( $report->{from} )
73 ->set_where( $report->{where} )
74 ->set_having( $report->{having} )
75 ->set_order_by( $report->{order_by} )
76 ->set_pivot_data( $report->{pivot_data} )
77 ->set_pivot_label( $report->{pivot_label} )
78 ->set_pivot_default( $report->{pivot_default} );
84 #-------------------------------------------------------------------------------------------------
85 package OpenILS::Reporter::SQLBuilder::ResultSet;
86 use base qw/OpenILS::Reporter::SQLBuilder/;
91 $self->{_is_subquery} = $flag if (defined $flag);
92 return $self->{_is_subquery};
97 return $self->{_pivot_data};
102 return $self->{_pivot_label};
107 return $self->{_pivot_label};
110 sub set_pivot_default {
113 $self->{_pivot_default} = $p if (defined $p);
120 $self->{_pivot_data} = $p if (defined $p);
124 sub set_pivot_label {
127 $self->{_pivot_label} = $p if (defined $p);
131 sub set_subquery_alias {
134 $self->{_alias} = $alias if (defined $alias);
142 $self->{_select} = [];
144 return $self unless (@cols && defined($cols[0]));
145 @cols = @{ $cols[0] } if (@cols == 1 && ref($cols[0]) eq 'ARRAY');
147 push @{ $self->{_select} }, map { OpenILS::Reporter::SQLBuilder::Column::Select->new( $_ )->set_builder( $self->builder ) } @cols;
156 $self->{_from} = OpenILS::Reporter::SQLBuilder::Relation->parse( $f );
165 $self->{_where} = [];
167 return $self unless (@cols && defined($cols[0]));
168 @cols = @{ $cols[0] } if (@cols == 1 && ref($cols[0]) eq 'ARRAY');
170 push @{ $self->{_where} }, map { OpenILS::Reporter::SQLBuilder::Column::Where->new( $_ )->set_builder( $self->builder ) } @cols;
179 $self->{_having} = [];
181 return $self unless (@cols && defined($cols[0]));
182 @cols = @{ $cols[0] } if (@cols == 1 && ref($cols[0]) eq 'ARRAY');
184 push @{ $self->{_having} }, map { OpenILS::Reporter::SQLBuilder::Column::Having->new( $_ )->set_builder( $self->builder ) } @cols;
193 $self->{_order_by} = [];
195 return $self unless (@cols && defined($cols[0]));
196 @cols = @{ $cols[0] } if (@cols == 1 && ref($cols[0]) eq 'ARRAY');
198 push @{ $self->{_order_by} }, map { OpenILS::Reporter::SQLBuilder::Column::OrderBy->new( $_ )->set_builder( $self->builder ) } @cols;
203 sub column_label_list {
207 push @labels, $self->resolve_param( $_->{_alias} ) for ( @{ $self->{_select} } );
214 $base = 1 unless (defined $base);
218 for my $c ( @{ $self->{_select} } ) {
219 push @group_by, $gcount if (!$c->is_aggregate);
229 return $self->{_sql} if ($self->{_sql});
233 if ($self->is_subquery) {
237 $sql .= "SELECT\t" . join(",\n\t", map { $_->toSQL } @{ $self->{_select} }) . "\n" if (@{ $self->{_select} });
238 $sql .= " FROM\t" . $self->{_from}->toSQL . "\n" if ($self->{_from});
239 $sql .= " WHERE\t" . join("\n\tAND ", map { $_->toSQL } @{ $self->{_where} }) . "\n" if (@{ $self->{_where} });
241 my @group_by = $self->group_by_list;
243 $sql .= ' GROUP BY ' . join(', ', @group_by) . "\n" if (@group_by);
244 $sql .= " HAVING " . join("\n\tAND ", map { $_->toSQL } @{ $self->{_having} }) . "\n" if (@{ $self->{_having} });
245 $sql .= ' ORDER BY ' . join(', ', map { $_->toSQL } @{ $self->{_order_by} }) . "\n" if (@{ $self->{_order_by} });
247 if ($self->is_subquery) {
248 $sql .= ') '. $self->{_alias} . "\n";
251 return $self->{_sql} = $sql;
255 #-------------------------------------------------------------------------------------------------
256 package OpenILS::Reporter::SQLBuilder::Input;
257 use base qw/OpenILS::Reporter::SQLBuilder/;
261 my $self = $class->SUPER::new;
263 my $col_data = shift;
265 if (ref($col_data)) {
266 $self->{params} = $col_data->{params};
267 my $trans = $col_data->{transform} || 'Bare';
268 my $pkg = "OpenILS::Reporter::SQLBuilder::Input::Transform::$trans";
269 if (UNIVERSAL::can($pkg => 'toSQL')) {
270 $self->{_transform} = $trans;
272 $self->{_transform} = 'GenericTransform';
274 } elsif( defined($col_data) ) {
275 $self->{_transform} = 'Bare';
276 $self->{params} = $col_data;
278 $self->{_transform} = 'NULL';
288 my $type = $self->{_transform};
289 return $self->{_sql} if ($self->{_sql});
290 my $toSQL = "OpenILS::Reporter::SQLBuilder::Input::Transform::${type}::toSQL";
291 return $self->{_sql} = $self->$toSQL;
295 #-------------------------------------------------------------------------------------------------
296 package OpenILS::Reporter::SQLBuilder::Input::Transform::NULL;
303 #-------------------------------------------------------------------------------------------------
304 package OpenILS::Reporter::SQLBuilder::Input::Transform::Bare;
309 my $val = $self->{params};
310 $val = $$val[0] if (ref($val));
312 $val =~ s/\\/\\\\/go;
319 #-------------------------------------------------------------------------------------------------
320 package OpenILS::Reporter::SQLBuilder::Input::Transform::age;
325 my $val = $self->{params};
326 $val = $$val[0] if (ref($val));
328 $val =~ s/\\/\\\\/go;
331 return "AGE(NOW(),'" . $val . "'::TIMESTAMPTZ)";
334 sub is_aggregate { return 0 }
337 #-------------------------------------------------------------------------------------------------
338 package OpenILS::Reporter::SQLBuilder::Input::Transform::relative_year;
343 my $rtime = $self->relative_time || 'now';
345 $rtime =~ s/\\/\\\\/go;
346 $rtime =~ s/'/\\'/go;
348 my $val = $self->{params};
349 $val = $$val[0] if (ref($val));
351 $val =~ s/\\/\\\\/go;
354 return "EXTRACT(YEAR FROM '$rtime'::TIMESTAMPTZ + '$val years')";
358 #-------------------------------------------------------------------------------------------------
359 package OpenILS::Reporter::SQLBuilder::Input::Transform::relative_month;
364 my $rtime = $self->relative_time || 'now';
366 $rtime =~ s/\\/\\\\/go;
367 $rtime =~ s/'/\\'/go;
369 my $val = $self->{params};
370 $val = $$val[0] if (ref($val));
372 $val =~ s/\\/\\\\/go;
375 return "EXTRACT(YEAR FROM '$rtime'::TIMESTAMPTZ + '$val months')" .
376 " || '-' || LPAD(EXTRACT(MONTH FROM '$rtime'::TIMESTAMPTZ + '$val months'),2,'0')";
380 #-------------------------------------------------------------------------------------------------
381 package OpenILS::Reporter::SQLBuilder::Input::Transform::relative_date;
386 my $rtime = $self->relative_time || 'now';
388 $rtime =~ s/\\/\\\\/go;
389 $rtime =~ s/'/\\'/go;
391 my $val = $self->{params};
392 $val = $$val[0] if (ref($val));
394 $val =~ s/\\/\\\\/go;
397 return "DATE('$rtime'::TIMESTAMPTZ + '$val days')";
401 #-------------------------------------------------------------------------------------------------
402 package OpenILS::Reporter::SQLBuilder::Input::Transform::relative_week;
407 my $rtime = $self->relative_time || 'now';
409 $rtime =~ s/\\/\\\\/go;
410 $rtime =~ s/'/\\'/go;
412 my $val = $self->{params};
413 $val = $$val[0] if (ref($val));
415 $val =~ s/\\/\\\\/go;
418 return "EXTRACT(WEEK FROM '$rtime'::TIMESTAMPTZ + '$val weeks')";
422 #-------------------------------------------------------------------------------------------------
423 package OpenILS::Reporter::SQLBuilder::Column;
424 use base qw/OpenILS::Reporter::SQLBuilder/;
428 my $self = $class->SUPER::new;
430 my $col_data = shift;
431 $self->{_relation} = $col_data->{relation};
432 $self->{_column} = $col_data->{column};
434 $self->{_aggregate} = $col_data->{aggregate};
436 if (ref($self->{_column})) {
437 my $trans = $self->{_column}->{transform} || 'Bare';
438 my $pkg = "OpenILS::Reporter::SQLBuilder::Column::Transform::$trans";
439 if (UNIVERSAL::can($pkg => 'toSQL')) {
440 $self->{_transform} = $trans;
442 $self->{_transform} = 'GenericTransform';
444 } elsif( defined($self->{_column}) ) {
445 $self->{_transform} = 'Bare';
447 $self->{_transform} = 'NULL';
456 if (ref($self->{_column})) {
457 return $self->{_column}->{colname};
459 return $self->{_column};
465 my $type = $self->{_transform};
466 return $self->{_sql} if ($self->{_sql});
467 my $toSQL = "OpenILS::Reporter::SQLBuilder::Column::Transform::${type}::toSQL";
468 return $self->{_sql} = $self->$toSQL;
473 my $type = $self->{_transform};
474 my $is_agg = "OpenILS::Reporter::SQLBuilder::Column::Transform::${type}::is_aggregate";
475 return $self->$is_agg;
479 #-------------------------------------------------------------------------------------------------
480 package OpenILS::Reporter::SQLBuilder::Column::OrderBy;
481 use base qw/OpenILS::Reporter::SQLBuilder::Column/;
485 my $self = $class->SUPER::new(@_);
487 my $col_data = shift;
488 $self->{_direction} = $col_data->{direction} || 'ascending';
494 my $dir = ($self->{_direction} =~ /^d/oi) ? 'DESC' : 'ASC';
495 return $self->{_sql} if ($self->{_sql});
496 return $self->{_sql} = $self->SUPER::toSQL . " $dir";
500 #-------------------------------------------------------------------------------------------------
501 package OpenILS::Reporter::SQLBuilder::Column::Select;
502 use base qw/OpenILS::Reporter::SQLBuilder::Column/;
506 my $self = $class->SUPER::new(@_);
508 my $col_data = shift;
509 $self->{_alias} = $col_data->{alias} || $self->name;
515 return $self->{_sql} if ($self->{_sql});
516 return $self->{_sql} = $self->SUPER::toSQL . ' AS "' . $self->resolve_param( $self->{_alias} ) . '"';
520 #-------------------------------------------------------------------------------------------------
521 package OpenILS::Reporter::SQLBuilder::Column::Transform::GenericTransform;
525 my $name = $self->name;
526 my $func = $self->{_column}->{transform};
529 @params = @{ $self->resolve_param( $self->{_column}->{params} ) } if ($self->{_column}->{params});
531 my $sql = $func . '("' . $self->{_relation} . '"."' . $self->name . '"';
532 $sql .= ",'" . join("','", @params) . "'" if (@params);
538 sub is_aggregate { return $self->{_aggregate} }
540 #-------------------------------------------------------------------------------------------------
541 package OpenILS::Reporter::SQLBuilder::Column::Transform::Bare;
545 return '"' . $self->{_relation} . '"."' . $self->name . '"';
548 sub is_aggregate { return 0 }
550 #-------------------------------------------------------------------------------------------------
551 package OpenILS::Reporter::SQLBuilder::Column::Transform::upper;
555 my $params = $self->resolve_param( $self->{_column}->{params} );
556 my $start = $$params[0];
557 my $len = $$params[1];
558 return 'UPPER("' . $self->{_relation} . '"."' . $self->name . '")';
561 sub is_aggregate { return 0 }
564 #-------------------------------------------------------------------------------------------------
565 package OpenILS::Reporter::SQLBuilder::Column::Transform::lower;
569 my $params = $self->resolve_param( $self->{_column}->{params} );
570 my $start = $$params[0];
571 my $len = $$params[1];
572 return 'LOWER("' . $self->{_relation} . '"."' . $self->name . '")';
575 sub is_aggregate { return 0 }
578 #-------------------------------------------------------------------------------------------------
579 package OpenILS::Reporter::SQLBuilder::Column::Transform::substring;
583 my $params = $self->resolve_param( $self->{_column}->{params} );
584 my $start = $$params[0];
585 my $len = $$params[1];
586 return 'SUBSTRING("' . $self->{_relation} . '"."' . $self->name . "\",$start,$len)";
589 sub is_aggregate { return 0 }
592 #-------------------------------------------------------------------------------------------------
593 package OpenILS::Reporter::SQLBuilder::Column::Transform::day_name;
597 return 'TO_CHAR("' . $self->{_relation} . '"."' . $self->name . '", \'Day\')';
600 sub is_aggregate { return 0 }
603 #-------------------------------------------------------------------------------------------------
604 package OpenILS::Reporter::SQLBuilder::Column::Transform::month_name;
608 return 'TO_CHAR("' . $self->{_relation} . '"."' . $self->name . '", \'Month\')';
611 sub is_aggregate { return 0 }
614 #-------------------------------------------------------------------------------------------------
615 package OpenILS::Reporter::SQLBuilder::Column::Transform::doy;
619 return 'EXTRACT(DOY FROM "' . $self->{_relation} . '"."' . $self->name . '")';
622 sub is_aggregate { return 0 }
625 #-------------------------------------------------------------------------------------------------
626 package OpenILS::Reporter::SQLBuilder::Column::Transform::woy;
630 return 'EXTRACT(WEEK FROM "' . $self->{_relation} . '"."' . $self->name . '")';
633 sub is_aggregate { return 0 }
636 #-------------------------------------------------------------------------------------------------
637 package OpenILS::Reporter::SQLBuilder::Column::Transform::moy;
641 return 'EXTRACT(MONTH FROM "' . $self->{_relation} . '"."' . $self->name . '")';
644 sub is_aggregate { return 0 }
647 #-------------------------------------------------------------------------------------------------
648 package OpenILS::Reporter::SQLBuilder::Column::Transform::qoy;
652 return 'EXTRACT(QUARTER FROM "' . $self->{_relation} . '"."' . $self->name . '")';
655 sub is_aggregate { return 0 }
658 #-------------------------------------------------------------------------------------------------
659 package OpenILS::Reporter::SQLBuilder::Column::Transform::dom;
663 return 'EXTRACT(DAY FROM "' . $self->{_relation} . '"."' . $self->name . '")';
666 sub is_aggregate { return 0 }
669 #-------------------------------------------------------------------------------------------------
670 package OpenILS::Reporter::SQLBuilder::Column::Transform::dow;
674 return 'EXTRACT(DOW FROM "' . $self->{_relation} . '"."' . $self->name . '")';
677 sub is_aggregate { return 0 }
680 #-------------------------------------------------------------------------------------------------
681 package OpenILS::Reporter::SQLBuilder::Column::Transform::year_trunc;
685 return 'EXTRACT(YEAR FROM "' . $self->{_relation} . '"."' . $self->name . '")';
688 sub is_aggregate { return 0 }
691 #-------------------------------------------------------------------------------------------------
692 package OpenILS::Reporter::SQLBuilder::Column::Transform::month_trunc;
696 return 'EXTRACT(YEAR FROM "' . $self->{_relation} . '"."' . $self->name . '")' .
697 ' || \'-\' || LPAD(EXTRACT(MONTH FROM "' . $self->{_relation} . '"."' . $self->name . '"),2,\'0\')';
700 sub is_aggregate { return 0 }
703 #-------------------------------------------------------------------------------------------------
704 package OpenILS::Reporter::SQLBuilder::Column::Transform::date_trunc;
708 return 'DATE("' . $self->{_relation} . '"."' . $self->name . '")';
711 sub is_aggregate { return 0 }
714 #-------------------------------------------------------------------------------------------------
715 package OpenILS::Reporter::SQLBuilder::Column::Transform::hour_trunc;
719 return 'DATE_TRUNC("' . $self->{_relation} . '"."' . $self->name . '")';
722 sub is_aggregate { return 0 }
725 #-------------------------------------------------------------------------------------------------
726 package OpenILS::Reporter::SQLBuilder::Column::Transform::quarter;
730 return 'EXTRACT(YEAR FROM "' . $self->{_relation} . '"."' . $self->name . '")' .
731 ' || \'-Q\' || EXTRACT(QUARTER FROM "' . $self->{_relation} . '"."' . $self->name . '")';
734 sub is_aggregate { return 0 }
737 #-------------------------------------------------------------------------------------------------
738 package OpenILS::Reporter::SQLBuilder::Column::Transform::months_ago;
742 return 'EXTRACT(MONTH FROM AGE(NOW(),"' . $self->{_relation} . '"."' . $self->name . '"))';
745 sub is_aggregate { return 0 }
748 #-------------------------------------------------------------------------------------------------
749 package OpenILS::Reporter::SQLBuilder::Column::Transform::hod;
753 return 'EXTRACT(HOUR FROM "' . $self->{_relation} . '"."' . $self->name . '")';
756 sub is_aggregate { return 0 }
759 #-------------------------------------------------------------------------------------------------
760 package OpenILS::Reporter::SQLBuilder::Column::Transform::quarters_ago;
764 return 'EXTRACT(QUARTER FROM AGE(NOW(),"' . $self->{_relation} . '"."' . $self->name . '"))';
767 sub is_aggregate { return 0 }
770 #-------------------------------------------------------------------------------------------------
771 package OpenILS::Reporter::SQLBuilder::Column::Transform::age;
775 return 'AGE(NOW(),"' . $self->{_relation} . '"."' . $self->name . '")';
778 sub is_aggregate { return 0 }
781 #-------------------------------------------------------------------------------------------------
782 package OpenILS::Reporter::SQLBuilder::Column::Transform::first;
786 return 'FIRST("' . $self->{_relation} . '"."' . $self->name . '")';
789 sub is_aggregate { return 1 }
792 #-------------------------------------------------------------------------------------------------
793 package OpenILS::Reporter::SQLBuilder::Column::Transform::last;
797 return 'LAST("' . $self->{_relation} . '"."' . $self->name . '")';
800 sub is_aggregate { return 1 }
803 #-------------------------------------------------------------------------------------------------
804 package OpenILS::Reporter::SQLBuilder::Column::Transform::min;
808 return 'MIN("' . $self->{_relation} . '"."' . $self->name . '")';
811 sub is_aggregate { return 1 }
814 #-------------------------------------------------------------------------------------------------
815 package OpenILS::Reporter::SQLBuilder::Column::Transform::max;
819 return 'MAX("' . $self->{_relation} . '"."' . $self->name . '")';
822 sub is_aggregate { return 1 }
825 #-------------------------------------------------------------------------------------------------
826 package OpenILS::Reporter::SQLBuilder::Column::Transform::count;
830 return 'COUNT("' . $self->{_relation} . '"."' . $self->name . '")';
833 sub is_aggregate { return 1 }
836 #-------------------------------------------------------------------------------------------------
837 package OpenILS::Reporter::SQLBuilder::Column::Transform::count_distinct;
841 return 'COUNT(DISTINCT "' . $self->{_relation} . '"."' . $self->name . '")';
844 sub is_aggregate { return 1 }
847 #-------------------------------------------------------------------------------------------------
848 package OpenILS::Reporter::SQLBuilder::Column::Transform::sum;
852 return 'SUM("' . $self->{_relation} . '"."' . $self->name . '")';
855 sub is_aggregate { return 1 }
858 #-------------------------------------------------------------------------------------------------
859 package OpenILS::Reporter::SQLBuilder::Column::Transform::average;
863 return 'AVG("' . $self->{_relation} . '"."' . $self->name . '")';
866 sub is_aggregate { return 1 }
869 #-------------------------------------------------------------------------------------------------
870 package OpenILS::Reporter::SQLBuilder::Column::Where;
871 use base qw/OpenILS::Reporter::SQLBuilder::Column/;
875 my $self = $class->SUPER::new(@_);
877 my $col_data = shift;
878 $self->{_condition} = $col_data->{condition};
883 sub _flesh_conditions {
885 $cond = [$cond] unless (ref($cond) eq 'ARRAY');
889 push @out, OpenILS::Reporter::SQLBuilder::Input->new( $c );
898 return $self->{_sql} if ($self->{_sql});
899 my $sql = $self->SUPER::toSQL;
901 my ($op) = keys %{ $self->{_condition} };
902 my $val = _flesh_conditions( $self->resolve_param( $self->{_condition}->{$op} ) );
904 if (lc($op) eq 'in') {
905 $sql .= " IN (". join(",", map { $_->toSQL } @$val).")";
906 } elsif (lc($op) eq 'not in') {
907 $sql .= " NOT IN (". join(",", map { $_->toSQL } @$val).")";
908 } elsif (lc($op) eq 'between') {
909 $sql .= " BETWEEN ". join(" AND ", map { $_->toSQL } @$val);
910 } elsif (lc($op) eq 'not between') {
911 $sql .= " NOT BETWEEN ". join(" AND ", map { $_->toSQL } @$val);
912 } elsif (lc($op) eq 'like') {
913 $val = $$val[0] if (ref($val) eq 'ARRAY');
914 $val =~ s/^'(.*)'$/$1/o;
917 $sql .= " LIKE '\%$val\%'";
918 } elsif (lc($op) eq 'ilike') {
919 $val = $$val[0] if (ref($val) eq 'ARRAY');
920 $val =~ s/^'(.*)'$/$1/o;
923 $sql .= " ILIKE '\%$val\%'";
925 $val = $$val[0] if (ref($val) eq 'ARRAY');
926 $sql .= " $op " . $val->toSQL;
929 return $self->{_sql} = $sql;
933 #-------------------------------------------------------------------------------------------------
934 package OpenILS::Reporter::SQLBuilder::Column::Having;
935 use base qw/OpenILS::Reporter::SQLBuilder::Column::Where/;
937 #-------------------------------------------------------------------------------------------------
938 package OpenILS::Reporter::SQLBuilder::Relation;
939 use base qw/OpenILS::Reporter::SQLBuilder/;
943 $self = $self->SUPER::new if (!ref($self));
945 my $rel_data = shift;
947 $self->{_table} = $rel_data->{table};
948 $self->{_alias} = $rel_data->{alias} || $self->name;
950 $self->{_columns} = [];
952 if ($rel_data->{join}) {
954 $_ => OpenILS::Reporter::SQLBuilder::Relation->parse( $rel_data->{join}->{$_} ) => $rel_data->{join}->{$_}->{key}
955 ) for ( keys %{ $rel_data->{join} } );
965 push @{ $self->{_columns} }, $col;
971 return (grep { $_->name eq $col} @{ $self->{_columns} })[0];
980 if (ref($col) eq 'OpenILS::Reporter::SQLBuilder::Join') {
981 push @{ $self->{_join} }, $col;
983 push @{ $self->{_join} }, OpenILS::Reporter::SQLBuilder::Join->build( $self => $col, $frel => $fkey );
992 $self->{_is_join} = $j if ($j);
993 return $self->{_is_join};
998 return $self->{_sql} if ($self->{_sql});
1000 my $sql = $self->{_table} .' AS "'. $self->{_alias} .'"';
1002 if (!$self->is_join) {
1003 for my $j ( @{ $self->{_join} } ) {
1008 return $self->{_sql} = $sql;
1011 #-------------------------------------------------------------------------------------------------
1012 package OpenILS::Reporter::SQLBuilder::Join;
1013 use base qw/OpenILS::Reporter::SQLBuilder/;
1017 $self = $self->SUPER::new if (!ref($self));
1019 $self->{_left_rel} = shift;
1020 ($self->{_left_col}) = split(/-/,shift());
1022 $self->{_right_rel} = shift;
1023 $self->{_right_col} = shift;
1025 $self->{_right_rel}->is_join(1);
1032 return $self->{_sql} if ($self->{_sql});
1034 my $sql = "\n\tJOIN " . $self->{_right_rel}->toSQL .
1035 ' ON ("' . $self->{_left_rel}->{_alias} . '"."' . $self->{_left_col} .
1036 '" = "' . $self->{_right_rel}->{_alias} . '"."' . $self->{_right_col} . '")';
1038 $sql .= $_->toSQL for (@{ $self->{_right_rel}->{_join} });
1040 return $self->{_sql} = $sql;