Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to get ordered bind names #25

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions lib/SQL/Abstract.pm
Original file line number Diff line number Diff line change
Expand Up @@ -727,12 +727,17 @@ sub _update_returning { shift->_returning(@_) }

sub select {
my ($self, @args) = @_;

local $self->{bind_names};

my $stmt = do {
if (ref(my $sel = $args[0]) eq 'HASH') {
$sel
} else {
my %clauses;
@clauses{qw(from select where order_by)} = @args;
@clauses{qw(from select where order_by bind_names)} = @args;

$self->{bind_names} = $clauses{bind_names} if $clauses{bind_names};

# This oddity is to literalify since historically SQLA doesn't quote
# a single identifier argument, so we convert it into a literal
Expand Down Expand Up @@ -856,12 +861,12 @@ sub _render_delete_clause_target {
#======================================================================



# Finally, a separate routine just to handle WHERE clauses
sub where {
my ($self, $where, $order) = @_;
my ($self, $where, $order, $bind_names) = @_;

local $self->{convert_where} = $self->{convert};
local $self->{bind_names} = $bind_names;

# where ?
my ($sql, @bind) = defined($where)
Expand Down Expand Up @@ -1539,6 +1544,7 @@ sub _render_func {

sub _render_bind {
my ($self, undef, $bind) = @_;
push @{$self->{bind_names}} => $bind->[0] if $self->{bind_names};
return [ '?', $self->_bindtype(@$bind) ];
}

Expand Down Expand Up @@ -2148,7 +2154,7 @@ SQL::Abstract - Generate SQL from Perl data structures

my $sql = SQL::Abstract->new;

my($stmt, @bind) = $sql->select($source, \@fields, \%where, $order);
my($stmt, @bind) = $sql->select($source, \@fields, \%where, $order, \@bind_names);

my($stmt, @bind) = $sql->insert($table, \%fieldvals || \@values);

Expand All @@ -2161,7 +2167,7 @@ SQL::Abstract - Generate SQL from Perl data structures
$sth->execute(@bind);

# Just generate the WHERE clause
my($stmt, @bind) = $sql->where(\%where, $order);
my($stmt, @bind) = $sql->where(\%where, $order, \@bind_names);

# Return values in the same order, for hashed queries
# See PERFORMANCE section for more details
Expand Down Expand Up @@ -2578,7 +2584,7 @@ L<insert|/insert($table, \@values || \%fieldvals, \%options)>.

=back

=head2 select($source, $fields, $where, $order)
=head2 select($source, $fields, $where, $order, \@bind_names)

This returns a SQL SELECT statement and associated list of bind values, as
specified by the arguments:
Expand Down Expand Up @@ -2617,6 +2623,11 @@ The argument can be a scalar, a hashref or an arrayref
-- see section L<ORDER BY clause|/"ORDER BY CLAUSES">
for details.

=item \@bind_names

If an arrayref is provided, it will be populated with the column names
associated with every element in the @bind array in the same order.

=back


Expand All @@ -2638,14 +2649,16 @@ L<insert|/insert($table, \@values || \%fieldvals, \%options)>.

=back

=head2 where(\%where, $order)
=head2 where(\%where, $order, \@bind_names)

This is used to generate just the WHERE clause. For example,
if you have an arbitrary data structure and know what the
rest of your SQL is going to look like, but want an easy way
to produce a WHERE clause, use this. It returns an SQL WHERE
clause and list of bind values.

If a \@bind_names arrayref is provided, it will be populated with the column
names associated with every element in the @bind array in the same order.

=head2 values(\%data)

Expand Down
61 changes: 61 additions & 0 deletions t/25bind_names.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use strict;
use warnings;
use Test::More;

use SQL::Abstract;

my $sql = SQL::Abstract->new;

my $A = bless(["a"], 'A');
my $B = bless(["b"], 'B');
my $X1 = bless(["x1"], 'X');
my $X2 = bless(["x2"], 'X');

my $where = [{a => $A, b => $B}, {x => { '-in' => [$X1, $X2]}}];

subtest select => sub {
my @bind_names;
my ($stmt, @bind) = $sql->select(
'foo',
['a', 'b'],
$where,
undef, # order_by
\@bind_names,
);

is($stmt, 'SELECT a, b FROM foo WHERE ( ( a = ? AND b = ? ) OR x IN ( ?, ? ) )', "Got correct statement");
is_deeply(
\@bind,
[$A, $B, $X1, $X2],
"Got expected binds in correct order"
);
is_deeply(
\@bind_names,
[qw/a b x x/],
"Got the column names of all the binds in order"
);
};

subtest where => sub {
my @bind_names;
my ($stmt, @bind) = $sql->where(
$where,
undef, # order by
\@bind_names,
);

# Not sure why, but these WHERE has one extra set of parens compared to the select() version
is($stmt, ' WHERE ( ( ( a = ? AND b = ? ) OR x IN ( ?, ? ) ) )', "Got correct statement");
is_deeply(
\@bind,
[$A, $B, $X1, $X2],
"Got expected binds in correct order"
);
is_deeply(
\@bind_names,
[qw/a b x x/],
"Got the column names of all the binds in order"
);
};

done_testing;