Support NOCYCLE parameter.
Robert Bohne [Sat, 27 Mar 2010 11:03:23 +0000 (11:03 +0000)]
lib/DBIx/Class/SQLAHacks/Oracle.pm
lib/DBIx/Class/Storage/DBI/Oracle/Generic.pm
t/73oracle.t

index 6046372..ca6700e 100644 (file)
@@ -8,8 +8,7 @@ use Carp::Clan qw/^DBIx::Class|^SQL::Abstract/;
 #  TODO:
 #   - Check the parameter syntax of connect_by
 #   - Review by experienced DBIC/SQL:A developers :-)
-#   - Check NOCYCLE parameter
-#       http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/pseudocolumns001.htm#i1009434
+#   - Problem with count and connect_by look the TODO in t/73oracle.t
 # 
 
 sub select {
@@ -45,10 +44,11 @@ sub _connect_by {
             $sql .= $self->_sqlcase(' start with ') . $ws;
             push @bind, @wb;
         }
-        if ( my $connect_by = $attrs->{'connect_by'}) {
+        if ( my $connect_by = $attrs->{'connect_by'} ) {
             my ($connect_by_sql, @connect_by_sql_bind) = $self->_recurse_where( $attrs->{'connect_by'} );
             $sql .= sprintf(" %s %s",
-                $self->_sqlcase('connect by'),
+                ( $attrs->{'nocycle'} ) ? $self->_sqlcase('connect by nocycle')
+                    : $self->_sqlcase('connect by'),
                 $connect_by_sql,
             );
             push @bind, @connect_by_sql_bind;
index 95e428d..37ddf7a 100644 (file)
@@ -420,9 +420,10 @@ sub _select_args {
     my ($self, $ident, $select, $where, $attrs) = @_;
 
     my $connect_by_args = {};
-    if ( $attrs->{connect_by} || $attrs->{start_with} || $attrs->{order_siblings_by} ) {
+    if ( $attrs->{connect_by} || $attrs->{start_with} || $attrs->{order_siblings_by} || $attrs->{nocycle} ) {
         $connect_by_args = {
             connect_by => $attrs->{connect_by},
+            nocycle => $attrs->{nocycle},
             start_with => $attrs->{start_with},
             order_siblings_by => $attrs->{order_siblings_by},
         }
@@ -458,6 +459,28 @@ and child rows of the hierarchy.
   # CONNECT BY
   #     parentid = prior persionid
 
+=head2 nocycle
+
+=over 4
+
+=item Value: [1|0]
+
+=back
+
+If you want to use NOCYCLE set to 1.
+
+    connect_by => { parentid => 'prior personid' },
+    nocycle    => 1
+
+    # adds a connect by statement to the query:
+    # SELECT
+    #     me.persionid me.firstname, me.lastname, me.parentid
+    # FROM
+    #     person me
+    # CONNECT BY NOCYCLE
+    #     parentid = prior persionid
+
+
 =head2 start_with
 
 =over 4
index a8ca401..f1c1ca0 100644 (file)
@@ -586,27 +586,66 @@ if ( $schema->storage->isa('DBIx::Class::Storage::DBI::Oracle::Generic') ) {
       # after count_subq, 
       # I will fix this later...
       # 
+      # is_same_sql_bind (
+      #   $rs->count_rs->as_query,
+      #   '( 
+      #       SELECT COUNT( * ) FROM (
+      #           SELECT * FROM (
+      #               SELECT A.*, ROWNUM r FROM (
+      #                   SELECT 
+      #                       me.artistid AS col1 
+      #                   FROM artist me 
+      #                   START WITH name = ? 
+      #                   CONNECT BY artistid = PRIOR( parentid ) 
+      #               ) A
+      #               WHERE ROWNUM < 3
+      #           ) B
+      #           WHERE r >= 1
+      #       ) count_subq 
+      #   )',
+      #   [ [ name => 'greatgrandchild' ] ],
+      # );
+      # 
+      # is( $rs->count, 2, 'Connect By; LIMIT count ok' );
+    }
+
+    # select the whole tree with nocylce
+    {
+      my $rs = $schema->resultset('Artist')->search({}, {
+        nocycle    => 1,
+        start_with => { name => 'root' },
+        connect_by => { parentid => { -prior => \ 'artistid' } },
+      });
+
+      is_same_sql_bind (
+        $rs->as_query,
+        '(
+          SELECT me.artistid, me.name, me.rank, me.charfield, me.parentid
+            FROM artist me
+          START WITH name = ?
+          CONNECT BY NOCYCLE parentid = PRIOR( artistid )
+        )',
+        [ [ name => 'root'] ],
+      );
+      is_deeply (
+        [ $rs->get_column ('name')->all ],
+        [ qw/root child1 grandchild greatgrandchild child2/ ],
+        'got artist tree with nocycle',
+      );
+
+
       is_same_sql_bind (
         $rs->count_rs->as_query,
-        '( 
-            SELECT COUNT( * ) FROM (
-                SELECT * FROM (
-                    SELECT A.*, ROWNUM r FROM (
-                        SELECT 
-                            me.artistid AS col1 
-                        FROM artist me 
-                        START WITH name = ? 
-                        CONNECT BY artistid = PRIOR( parentid ) 
-                    ) A
-                    WHERE ROWNUM < 3
-                ) B
-                WHERE r >= 1
-            ) count_subq 
+        '(
+          SELECT COUNT( * )
+            FROM artist me
+          START WITH name = ?
+          CONNECT BY NOCYCLE parentid = PRIOR( artistid )
         )',
-        [ [ name => 'greatgrandchild' ] ],
+        [ [ name => 'root'] ],
       );
 
-      is( $rs->count, 2, 'Connect By; LIMIT count ok' );
+      is( $rs->count, 5, 'Connect By Nocycle count ok' );
     }
 }