Skip to content

Commit 99361bc

Browse files
authored
Merge pull request #57 from sixapart/feature-for-sub-query-by-prepared-statement3
search method and SQL clauses now accept SQL objects recursively
2 parents 4943ba1 + fbd0c73 commit 99361bc

File tree

9 files changed

+925
-381
lines changed

9 files changed

+925
-381
lines changed

cpanfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ on develop => sub {
2222

2323
on test => sub {
2424
requires 'version';
25+
requires 'Tie::IxHash';
2526
};
2627

2728
feature 'test_sqlite', 'Test SQLite' => sub {

lib/Data/ObjectDriver/Driver/DBI.pm

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use Data::ObjectDriver::Errors;
1212
use Data::ObjectDriver::SQL;
1313
use Data::ObjectDriver::Driver::DBD;
1414
use Data::ObjectDriver::Iterator;
15+
use Scalar::Util 'blessed';
1516

1617
my $ForkSafe = _is_fork_safe();
1718
my %Handles;
@@ -172,27 +173,33 @@ sub prepare_fetch {
172173

173174
sub fetch {
174175
my $driver = shift;
175-
my($rec, $class, $orig_terms, $orig_args) = @_;
176+
my ($rec, $class, $terms_or_stmt, $orig_args) = @_;
177+
my ($sql, $stmt);
176178

177179
if ($Data::ObjectDriver::RESTRICT_IO) {
178180
use Data::Dumper;
179-
die "Attempted DBI I/O while in restricted mode: fetch() " . Dumper($orig_terms, $orig_args);
181+
die "Attempted DBI I/O while in restricted mode: fetch() " . Dumper($terms_or_stmt, $orig_args);
180182
}
181183

182-
my ($sql, $bind, $stmt) = $driver->prepare_fetch($class, $orig_terms, $orig_args);
184+
if (blessed($terms_or_stmt) && $terms_or_stmt->isa('Data::ObjectDriver::SQL')) {
185+
$sql = $terms_or_stmt->as_sql;
186+
$stmt = $terms_or_stmt;
187+
} else {
188+
($sql, undef, $stmt) = $driver->prepare_fetch($class, $terms_or_stmt, $orig_args);
189+
}
183190

184-
my @bind;
191+
my @columns;
185192
my $map = $stmt->select_map;
186193
for my $col (@{ $stmt->select }) {
187-
push @bind, \$rec->{ $map->{$col} };
194+
push @columns, \$rec->{ $map->{$col} };
188195
}
189196

190197
my $dbh = $driver->r_handle($class->properties->{db});
191198
$driver->start_query($sql, $stmt->{bind});
192199

193200
my $sth = $orig_args->{no_cached_prepare} ? $dbh->prepare($sql) : $driver->_prepare_cached($dbh, $sql);
194201
$sth->execute(@{ $stmt->{bind} });
195-
$sth->bind_columns(undef, @bind);
202+
$sth->bind_columns(undef, @columns);
196203

197204
# need to slurp 'offset' rows for DBs that cannot do it themselves
198205
if (!$driver->dbd->offset_implemented && $orig_args->{offset}) {
@@ -218,11 +225,11 @@ sub load_object_from_rec {
218225
}
219226

220227
sub search {
221-
my($driver) = shift;
222-
my($class, $terms, $args) = @_;
228+
my ($driver) = shift;
229+
my ($class, $terms_or_stmt, $args) = @_;
223230

224231
my $rec = {};
225-
my $sth = $driver->fetch($rec, $class, $terms, $args);
232+
my $sth = $driver->fetch($rec, $class, $terms_or_stmt, $args);
226233

227234
my $iter = sub {
228235
## This is kind of a hack--we need $driver to stay in scope,

lib/Data/ObjectDriver/SQL.pm

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
package Data::ObjectDriver::SQL;
44
use strict;
55
use warnings;
6+
use Scalar::Util 'blessed';
67

78
use base qw( Class::Accessor::Fast );
89

910
__PACKAGE__->mk_accessors(qw(
1011
select distinct select_map select_map_reverse
1112
from joins where bind limit offset group order
1213
having where_values column_mutator index_hint
13-
comment
14+
comment as
1415
));
1516

1617
sub new {
@@ -33,10 +34,16 @@ sub new {
3334
sub add_select {
3435
my $stmt = shift;
3536
my($term, $col) = @_;
36-
$col ||= $term;
3737
push @{ $stmt->select }, $term;
38-
$stmt->select_map->{$term} = $col;
39-
$stmt->select_map_reverse->{$col} = $term;
38+
if (blessed($term) && $term->isa('Data::ObjectDriver::SQL')) {
39+
my $alias = $col || $term->as || $term->as_sql;
40+
$stmt->select_map->{$term} = $alias;
41+
$stmt->select_map_reverse->{$alias} = $term;
42+
} else {
43+
$col ||= $term;
44+
$stmt->select_map->{$term} = $col;
45+
$stmt->select_map_reverse->{$col} = $term;
46+
}
4047
}
4148

4249
sub add_join {
@@ -60,12 +67,26 @@ sub add_index_hint {
6067
sub as_sql {
6168
my $stmt = shift;
6269
my $sql = '';
70+
my @bind_for_select;
71+
6372
if (@{ $stmt->select }) {
6473
$sql .= 'SELECT ';
6574
$sql .= 'DISTINCT ' if $stmt->distinct;
75+
my $select_map = $stmt->select_map;
6676
$sql .= join(', ', map {
67-
my $alias = $stmt->select_map->{$_};
68-
$alias && /(?:^|\.)\Q$alias\E$/ ? $_ : "$_ $alias";
77+
my $col = $_;
78+
my $alias = $select_map->{$col};
79+
if (blessed($col) && $col->isa('Data::ObjectDriver::SQL')) {
80+
push @bind_for_select, @{ $col->{bind} };
81+
@{ $col->{bind} } = ();
82+
$col->as_subquery($alias);
83+
} else {
84+
if ($alias) {
85+
/(?:^|\.)\Q$alias\E$/ ? $col : "$col $alias";
86+
} else {
87+
$col;
88+
}
89+
}
6990
} @{ $stmt->select }) . "\n";
7091
}
7192
$sql .= 'FROM ';
@@ -91,8 +112,19 @@ sub as_sql {
91112
$sql .= ', ' if @from;
92113
}
93114

115+
my @bind_for_from;
116+
94117
if (@from) {
95-
$sql .= join ', ', map { $stmt->_add_index_hint($_) } @from;
118+
$sql .= join ', ', map {
119+
my $from = $_;
120+
if (blessed($from) && $from->isa('Data::ObjectDriver::SQL')) {
121+
push @bind_for_from, @{$from->{bind}};
122+
@{$from->{bind}} = ();
123+
$from->as_subquery;
124+
} else {
125+
$stmt->_add_index_hint($from);
126+
}
127+
} @from;
96128
}
97129

98130
$sql .= "\n";
@@ -107,9 +139,22 @@ sub as_sql {
107139
if ($comment && $comment =~ /([ 0-9a-zA-Z.:;()_#&,]+)/) {
108140
$sql .= "-- $1" if $1;
109141
}
142+
143+
@{ $stmt->{bind} } = (@bind_for_select, @bind_for_from, @{ $stmt->{bind} });
144+
110145
return $sql;
111146
}
112147

148+
sub as_subquery {
149+
my ($stmt, $alias) = @_;
150+
my $subquery = '(' . $stmt->as_sql . ')';
151+
$alias ||= $stmt->as;
152+
if ($alias) {
153+
$subquery .= ' AS ' . $alias;
154+
}
155+
$subquery;
156+
}
157+
113158
sub as_limit {
114159
my $stmt = shift;
115160
my $n = $stmt->limit or
@@ -231,7 +276,11 @@ sub add_having {
231276
# Carp::croak("Invalid/unsafe column name $col") unless $col =~ /^[\w\.]+$/;
232277

233278
if (my $orig = $stmt->select_map_reverse->{$col}) {
234-
$col = $orig;
279+
if (blessed($orig) && $orig->isa('Data::ObjectDriver::SQL')) {
280+
# do nothins
281+
} else {
282+
$col = $orig;
283+
}
235284
}
236285

237286
my($term, $bind) = $stmt->_mk_term($col, $val);
@@ -281,12 +330,17 @@ sub _mk_term {
281330
$term = "$c $op ? AND ?";
282331
push @bind, @{$val->{value}};
283332
} else {
284-
if (ref $val->{value} eq 'SCALAR') {
285-
$term = "$c $val->{op} " . ${$val->{value}};
333+
my $value = $val->{value};
334+
if (ref $value eq 'SCALAR') {
335+
$term = "$c $val->{op} " . $$value;
336+
} elsif (blessed($value) && $value->isa('Data::ObjectDriver::SQL')) {
337+
local $value->{as} = undef;
338+
$term = "$c $val->{op} ". $value->as_subquery;
339+
push @bind, @{$value->{bind}};
286340
} else {
287341
$term = "$c $val->{op} ?";
288342
$term .= $stmt->as_escape($val->{escape}) if $val->{escape} && $op =~ /^(?:NOT\s+)?I?LIKE$/;
289-
push @bind, $val->{value};
343+
push @bind, $value;
290344
}
291345
}
292346
} elsif (ref($val) eq 'SCALAR') {

0 commit comments

Comments
 (0)