abstract from code imported from DBIC::SQL::Abstract plus tests
Ash Berlin [Wed, 7 Feb 2007 15:10:07 +0000 (15:10 +0000)]
r2298@metis (orig r20):  matthewt | 2007-02-07 13:46:06 +0000
 r33610@cain (orig r9):  matthewt | 2006-11-13 09:14:03 +0000

lib/SQL/Abstract.pm
t/04from.t [new file with mode: 0644]

index a0c6b4a..cb37b1a 100644 (file)
@@ -184,21 +184,100 @@ sub puke (@) {
 # Utility functions
 sub _table  {
     my $self = shift;
-    my $tab  = shift;
-    if (ref $tab eq 'ARRAY') {
-        return join ', ', map { $self->_quote($_) } @$tab;
+    my $from = shift;
+    if (ref $from eq 'ARRAY') {
+        return $self->_recurse_from(@$from);
+    } elsif (ref $from eq 'HASH') {
+        return $self->_make_as($from);
     } else {
-        return $self->_quote($tab);
+        return $self->_quote($from);
     }
 }
 
+sub _recurse_from {
+    my ($self, $from, @join) = @_;
+    my @sqlf;
+    push(@sqlf, $self->_make_as($from));
+    foreach my $j (@join) {
+        push @sqlf, ', ' . $self->_quote($j) and next unless ref $j;
+        push @sqlf, ', ' . $$j and next if ref $j eq 'SCALAR';
+        my ($to, $on) = @$j;
+
+        # check whether a join type exists
+        my $join_clause = '';
+        my $to_jt = ref($to) eq 'ARRAY' ? $to->[0] : $to;
+        if (ref($to_jt) eq 'HASH' and exists($to_jt->{-join_type})) {
+            $join_clause = $self->_sqlcase(' '.($to_jt->{-join_type}).' JOIN ');
+        } else {
+            $join_clause = $self->_sqlcase(' JOIN ');
+        }
+        push(@sqlf, $join_clause);
+
+        if (ref $to eq 'ARRAY') {
+            push(@sqlf, '(', $self->_recurse_from(@$to), ')');
+        } else {
+            push(@sqlf, $self->_make_as($to));
+        }
+        push(@sqlf, $self->_sqlcase(' ON '), $self->_join_condition($on));
+    }
+    return join('', @sqlf);
+}
+
+sub _make_as {
+    my ($self, $from) = @_;
+    return $self->_quote($from) unless ref $from;
+    return $$from if ref $from eq 'SCALAR';
+    return join(' ', map { (ref $_ eq 'SCALAR' ? $$_ : $self->_quote($_)) }
+                         reverse each %{$self->_skip_options($from)});
+}
+
+sub _skip_options {
+    my ($self, $hash) = @_;
+    my $clean_hash = {};
+    $clean_hash->{$_} = $hash->{$_}
+        for grep {!/^-/} keys %$hash;
+    return $clean_hash;
+}
+
+sub _join_condition {
+    my ($self, $cond) = @_;
+    if (ref $cond eq 'HASH') {
+        my %j;
+        for (keys %$cond) {
+            my $x = '= '.$self->_quote($cond->{$_}); $j{$_} = \$x;
+        };
+        return $self->_recurse_where(\%j);
+    } elsif (ref $cond eq 'ARRAY') {
+        return join(' OR ', map { $self->_join_condition($_) } @$cond);
+    } else {
+        die "Can't handle this yet!";
+    }
+}
+
+
 sub _quote {
     my $self  = shift;
     my $label = shift;
 
+    return '' unless defined $label;
+
     return $label
       if $label eq '*';
 
+    return $label unless $self->{quote_char};
+
+    if (ref $self->{quote_char} eq "ARRAY") {
+
+        return $self->{quote_char}->[0] . $label . $self->{quote_char}->[1]
+            if !defined $self->{name_sep};
+
+        my $sep = $self->{name_sep};
+        return join($self->{name_sep},
+            map { $self->{quote_char}->[0] . $_ . $self->{quote_char}->[1] }
+              split( /\Q$sep\E/, $label ) );
+    }
+
+
     return $self->{quote_char} . $label . $self->{quote_char}
       if !defined $self->{name_sep};
 
diff --git a/t/04from.t b/t/04from.t
new file mode 100644 (file)
index 0000000..293589a
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -I. -w
+
+use strict;
+use vars qw($TESTING);
+$TESTING = 1;
+use Test;
+
+# use a BEGIN block so we print our plan before SQL::Abstract is loaded
+BEGIN { plan tests => 4 }
+
+use SQL::Abstract;
+
+sub is {
+    my ($got, $expect, $msg) = @_;
+    ok($got eq $expect) or
+        warn "got [${got}]\ninstead of [${expect}]\nfor test ${msg}\n\n";
+}
+
+my $sa = new SQL::Abstract;
+
+my @j = (
+    { child => 'person' },
+    [ { father => 'person' }, { 'father.person_id' => 'child.father_id' }, ],
+    [ { mother => 'person' }, { 'mother.person_id' => 'child.mother_id' } ],
+);
+my $match = 'person child JOIN person father ON ( father.person_id = '
+          . 'child.father_id ) JOIN person mother ON ( mother.person_id '
+          . '= child.mother_id )'
+          ;
+is( $sa->_recurse_from(@j), $match, 'join 1 ok' );
+
+my @j2 = (
+    { mother => 'person' },
+    [   [   { child => 'person' },
+            [   { father             => 'person' },
+                { 'father.person_id' => 'child.father_id' }
+            ]
+        ],
+        { 'mother.person_id' => 'child.mother_id' }
+    ],
+);
+$match = 'person mother JOIN (person child JOIN person father ON ('
+       . ' father.person_id = child.father_id )) ON ( mother.person_id = '
+       . 'child.mother_id )'
+       ;
+is( $sa->_recurse_from(@j2), $match, 'join 2 ok' );
+
+my @j3 = (
+    { child => 'person' },
+    [ { father => 'person', -join_type => 'inner' }, { 'father.person_id' => 'child.father_id' }, ],
+    [ { mother => 'person', -join_type => 'inner'  }, { 'mother.person_id' => 'child.mother_id' } ],
+);
+$match = 'person child INNER JOIN person father ON ( father.person_id = '
+          . 'child.father_id ) INNER JOIN person mother ON ( mother.person_id '
+          . '= child.mother_id )'
+          ;
+
+is( $sa->_recurse_from(@j3), $match, 'join 3 (inner join) ok');
+
+my @j4 = (
+    { mother => 'person' },
+    [   [   { child => 'person', -join_type => 'left' },
+            [   { father             => 'person', -join_type => 'right' },
+                { 'father.person_id' => 'child.father_id' }
+            ]
+        ],
+        { 'mother.person_id' => 'child.mother_id' }
+    ],
+);
+$match = 'person mother LEFT JOIN (person child RIGHT JOIN person father ON ('
+       . ' father.person_id = child.father_id )) ON ( mother.person_id = '
+       . 'child.mother_id )'
+       ;
+is( $sa->_recurse_from(@j4), $match, 'join 4 (nested joins + join types) ok');