fixed rels ending with me breaking subquery realiasing
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / SQLAHacks.pm
index faa3a48..3cf750c 100644 (file)
@@ -8,8 +8,10 @@ package # Hide from PAUSE
 use base qw/SQL::Abstract::Limit/;
 use strict;
 use warnings;
-use Carp::Clan qw/^DBIx::Class|^SQL::Abstract/;
-use Sub::Name();
+use List::Util 'first';
+use Sub::Name 'subname';
+use namespace::clean;
+use Carp::Clan qw/^DBIx::Class|^SQL::Abstract|^Try::Tiny/;
 
 BEGIN {
   # reinstall the carp()/croak() functions imported into SQL::Abstract
@@ -19,7 +21,7 @@ BEGIN {
   for my $f (qw/carp croak/) {
 
     my $orig = \&{"SQL::Abstract::$f"};
-    *{"SQL::Abstract::$f"} = Sub::Name::subname "SQL::Abstract::$f" =>
+    *{"SQL::Abstract::$f"} = subname "SQL::Abstract::$f" =>
       sub {
         if (Carp::longmess() =~ /DBIx::Class::SQLAHacks::[\w]+ .+? called \s at/x) {
           __PACKAGE__->can($f)->(@_);
@@ -31,6 +33,11 @@ BEGIN {
   }
 }
 
+# the "oh noes offset/top without limit" constant
+# limited to 32 bits for sanity (and since it is fed
+# to sprintf %u)
+sub __max_int { 0xFFFFFFFF };
+
 
 # Tries to determine limit dialect.
 #
@@ -115,8 +122,8 @@ sub _subqueried_limit_attrs {
   # for possible further chaining)
   my (@in_sel, @out_sel, %renamed);
   for my $node (@sel) {
-    if (List::Util::first { $_ =~ / (?<! $re_alias ) $re_sep /x } ($node->{as}, $node->{unquoted_sql}) )  {
-      $node->{as} =~ s/ $re_sep /__/xg;
+    if (first { $_ =~ / (?<! ^ $re_alias ) $re_sep /x } ($node->{as}, $node->{unquoted_sql}) )  {
+      $node->{as} = $self->_unqualify_colname($node->{as});
       my $quoted_as = $self->_quote($node->{as});
       push @in_sel, sprintf '%s AS %s', $node->{sql}, $quoted_as;
       push @out_sel, $quoted_as;
@@ -152,6 +159,13 @@ sub _subqueried_limit_attrs {
   );
 }
 
+sub _unqualify_colname {
+  my ($self, $fqcn) = @_;
+  my $re_sep = quotemeta($self->name_sep || '.');
+  $fqcn =~ s/ $re_sep /__/xg;
+  return $fqcn;
+}
+
 # ANSI standard Limit/Offset implementation. DB2 and MSSQL >= 2005 use this
 sub _RowNumberOver {
   my ($self, $sql, $rs_attrs, $rows, $offset ) = @_;
@@ -390,6 +404,21 @@ sub _Top {
   return $sql;
 }
 
+# This for Sybase ASE, to use SET ROWCOUNT when there is no offset, and
+# GenericSubQ otherwise.
+sub _RowCountOrGenericSubQ {
+  my $self = shift;
+  my ($sql, $rs_attrs, $rows, $offset) = @_;
+
+  return $self->_GenericSubQ(@_) if $offset;
+
+  return sprintf <<"EOF", $rows, $sql;
+SET ROWCOUNT %d
+%s
+SET ROWCOUNT 0
+EOF
+}
+
 # This is the most evil limit "dialect" (more of a hack) for *really*
 # stupid databases. It works by ordering the set by some unique column,
 # and calculating amount of rows that have a less-er value (thus
@@ -466,7 +495,8 @@ sub _GenericSubQ {
   my $cmp_op = $direction eq 'desc' ? '>' : '<';
   my $count_tbl_alias = 'rownum__emulation';
 
-  my $order_group_having = $self->_parse_rs_attrs($rs_attrs);
+  my $order_sql = $self->_order_by (delete $rs_attrs->{order_by});
+  my $group_having_sql = $self->_parse_rs_attrs($rs_attrs);
 
   # add the order supplement (if any) as this is what will be used for the outer WHERE
   $in_sel .= ", $_" for keys %{$extra_order_sel||{}};
@@ -474,9 +504,10 @@ sub _GenericSubQ {
   $sql = sprintf (<<EOS,
 SELECT $out_sel
   FROM (
-    SELECT $in_sel ${sql}${order_group_having}
+    SELECT $in_sel ${sql}${group_having_sql}
   ) %s
 WHERE ( SELECT COUNT(*) FROM %s %s WHERE %s $cmp_op %s ) %s
+$order_sql
 EOS
     ( map { $self->_quote ($_) } (
       $rs_attrs->{alias},
@@ -508,8 +539,6 @@ sub _find_syntax {
 sub select {
   my ($self, $table, $fields, $where, $rs_attrs, @rest) = @_;
 
-  $self->{"${_}_bind"} = [] for (qw/having from order/);
-
   if (not ref($table) or ref($table) eq 'SCALAR') {
     $table = $self->_quote($table);
   }
@@ -518,10 +547,20 @@ sub select {
   croak "LIMIT 0 Does Not Compute" if $rest[0] == 0;
     # and anyway, SQL::Abstract::Limit will cause a barf if we don't first
 
-  my ($sql, @where_bind) = $self->SUPER::select(
+  my ($sql, @bind) = $self->SUPER::select(
     $table, $self->_recurse_fields($fields), $where, $rs_attrs, @rest
   );
-  return wantarray ? ($sql, @{$self->{from_bind}}, @where_bind, @{$self->{having_bind}}, @{$self->{order_bind}} ) : $sql;
+  push @{$self->{where_bind}}, @bind;
+
+# this *must* be called, otherwise extra binds will remain in the sql-maker
+  my @all_bind = $self->_assemble_binds;
+
+  return wantarray ? ($sql, @all_bind) : $sql;
+}
+
+sub _assemble_binds {
+  my $self = shift;
+  return map { @{ (delete $self->{"${_}_bind"}) || [] } } (qw/from where having order/);
 }
 
 # Quotes table names, and handles default inserts
@@ -783,7 +822,7 @@ sub _join_condition {
   } elsif (ref $cond eq 'ARRAY') {
     return join(' OR ', map { $self->_join_condition($_) } @$cond);
   } else {
-    die "Can't handle this yet!";
+    croak "Can't handle this yet!";
   }
 }