Support arrayrefref for table names table-arrayrefref
Dagfinn Ilmari Mannsåker [Mon, 7 Aug 2017 15:51:28 +0000 (16:51 +0100)]
This is particularly handy for set-returning functions.

lib/SQL/Abstract.pm
t/01generate.t

index d3b17c3..aa7341f 100644 (file)
@@ -210,21 +210,22 @@ sub _assert_pass_injection_guard {
 
 sub insert {
   my $self    = shift;
-  my $table   = $self->_table(shift);
+  my ($table_sql, @all_bind) = $self->_table(shift);
   my $data    = shift || return;
   my $options = shift;
 
   my $method       = $self->_METHOD_FOR_refkind("_insert", $data);
-  my ($sql, @bind) = $self->$method($data);
-  $sql = join " ", $self->_sqlcase('insert into'), $table, $sql;
+  my ($values_sql, @values_bind) = $self->$method($data);
+  my $sql = join " ", $self->_sqlcase('insert into'), $table_sql, $values_sql;
+  push @all_bind, @values_bind;
 
   if ($options->{returning}) {
-    my ($s, @b) = $self->_insert_returning($options);
-    $sql .= $s;
-    push @bind, @b;
+    my ($returning_sql, @returning_bind) = $self->_insert_returning($options);
+    $sql .= $returning_sql;
+    push @all_bind, @returning_bind;
   }
 
-  return wantarray ? ($sql, @bind) : $sql;
+  return wantarray ? ($sql, @all_bind) : $sql;
 }
 
 # So that subclasses can override INSERT ... RETURNING separately from
@@ -362,7 +363,7 @@ sub _insert_value {
 
 sub update {
   my $self    = shift;
-  my $table   = $self->_table(shift);
+  my ($table_sql, @all_bind) = $self->_table(shift);
   my $data    = shift || return;
   my $where   = shift;
   my $options = shift;
@@ -371,9 +372,10 @@ sub update {
   puke "Unsupported data type specified to \$sql->update"
     unless ref $data eq 'HASH';
 
-  my ($sql, @all_bind) = $self->_update_set_values($data);
-  $sql = $self->_sqlcase('update ') . $table . $self->_sqlcase(' set ')
-          . $sql;
+  my ($values_sql, @values_bind) = $self->_update_set_values($data);
+  my $sql = $self->_sqlcase('update ') . $table_sql . $self->_sqlcase(' set ')
+    . $values_sql;
+  push @all_bind, @values_bind;
 
   if ($where) {
     my($where_sql, @where_bind) = $self->where($where);
@@ -459,20 +461,20 @@ sub _update_returning { shift->_returning(@_) }
 
 sub select {
   my $self   = shift;
-  my $table  = $self->_table(shift);
+  my ($table_sql, @table_bind)  = $self->_table(shift);
   my $fields = shift || '*';
   my $where  = shift;
   my $order  = shift;
 
-  my($where_sql, @bind) = $self->where($where, $order);
+  my($where_sql, @where_bind) = $self->where($where, $order);
 
   my $f = (ref $fields eq 'ARRAY') ? join ', ', map { $self->_quote($_) } @$fields
                                    : $fields;
   my $sql = join(' ', $self->_sqlcase('select'), $f,
-                      $self->_sqlcase('from'),   $table)
+                      $self->_sqlcase('from'),   $table_sql)
           . $where_sql;
 
-  return wantarray ? ($sql, @bind) : $sql;
+  return wantarray ? ($sql, @table_bind, @where_bind) : $sql;
 }
 
 #======================================================================
@@ -482,20 +484,21 @@ sub select {
 
 sub delete {
   my $self    = shift;
-  my $table   = $self->_table(shift);
+  my ($table_sql, @all_bind) = $self->_table(shift);
   my $where   = shift;
   my $options = shift;
 
-  my($where_sql, @bind) = $self->where($where);
-  my $sql = $self->_sqlcase('delete from ') . $table . $where_sql;
+  my($where_sql, @where_bind) = $self->where($where);
+  my $sql = $self->_sqlcase('delete from ') . $table_sql . $where_sql;
+  push @all_bind, @where_bind;
 
   if ($options->{returning}) {
     my ($returning_sql, @returning_bind) = $self->_delete_returning($options);
     $sql .= $returning_sql;
-    push @bind, @returning_bind;
+    push @all_bind, @returning_bind;
   }
 
-  return wantarray ? ($sql, @bind) : $sql;
+  return wantarray ? ($sql, @all_bind) : $sql;
 }
 
 # So that subclasses can override DELETE ... RETURNING separately from
@@ -1405,6 +1408,11 @@ sub _table  {
   my $from = shift;
   $self->_SWITCH_refkind($from, {
     ARRAYREF     => sub {join ', ', map { $self->_quote($_) } @$from;},
+    ARRAYREFREF  => sub {
+      my ($sql, @bind) = @$$from;
+      $self->_assert_bindval_matches_bindtype(@bind);
+      return ($sql, @bind);
+    },
     SCALAR       => sub {$self->_quote($from)},
     SCALARREF    => sub {$$from},
   });
@@ -2143,7 +2151,8 @@ Specification of the 'FROM' part of the statement.
 The argument can be either a plain scalar (interpreted as a table
 name, will be quoted), or an arrayref (interpreted as a list
 of table names, joined by commas, quoted), or a scalarref
-(literal SQL, not quoted).
+(literal SQL, not quoted), or a reference to an arrayref
+(literal SQL and bind values).
 
 =item $fields
 
index ebe3aad..cae9f9a 100644 (file)
@@ -616,6 +616,27 @@ my @tests = (
               stmt_q => 'DELETE FROM `test` WHERE ( `requestor` IS NULL ) RETURNING `id`, `created_at`',
               bind   => []
       },
+      {
+              func   => 'select',
+              args   => [\['my_srf(?)', 42], [qw(foo bar)], {foo => 37}],
+              stmt   => 'select foo, bar from my_srf(?) where foo = ?',
+              stmt_q => 'select `foo`, `bar` from my_srf(?) where `foo` = ?',
+              bind   => [42, 37],
+      },
+      {
+              func   => 'delete',
+              args   => [\['my_srf(?)', 42], {foo => 37}],
+              stmt   => 'delete from my_srf(?) where foo = ?',
+              stmt_q => 'delete from my_srf(?) where `foo` = ?',
+              bind   => [42, 37],
+      },
+      {
+              func   => 'update',
+              args   => [\['my_srf(?)', 42], {foo => 37}, {bar => 53}],
+              stmt   => 'update my_srf(?) set foo = ? where bar = ?',
+              stmt_q => 'update my_srf(?) set `foo` = ? where `bar` = ?',
+              bind   => [42, 37, 53],
+      },
 );
 
 # check is( not) => undef