File Coverage

blib/lib/Class/ReluctantORM/SQL/SubQuery.pm
Criterion Covered Total %
statement 27 76 35.5
branch 0 8 0.0
condition 0 10 0.0
subroutine 9 25 36.0
pod 14 14 100.0
total 50 133 37.5


line stmt bran cond sub pod time code
1             package Class::ReluctantORM::SQL::SubQuery;
2              
3             =head1 NAME
4              
5             Class::ReluctantORM::SQL::SubQuery - Represent a sub-SELECT in a FROM or WHERE clause
6              
7             =head1 SYNOPSIS
8              
9             use Class::ReluctantORM::SQL::Aliases;
10              
11             # Make a SELECT as usual
12             my $select = SQL->new('select');
13             $select->from(Table->new(table => 'mytable');
14             $select->where(Criterion->new('=', 1,1));
15              
16             # Make a subquery
17             my $subquery = SubQuery->new($select);
18              
19             # Use it as an expression
20             my $in_crit = Criterion->new(
21             'IN',
22             'needle',
23             $subquery, # haystack
24             );
25              
26              
27             # Or use it as a JOIN relation
28             # TODO DOCS
29              
30              
31             =head1 DESCRIPTION
32              
33             Wrapper around a SELECT statement, that implements both the Expression
34             interface as well as the Relation interface, allowing it to be used in
35             a WHERE clause or FROM clause.
36              
37             =cut
38              
39 1     1   5 use strict;
  1         2  
  1         25  
40 1     1   5 use warnings;
  1         2  
  1         19  
41              
42 1     1   5 use Data::Dumper;
  1         1  
  1         45  
43 1     1   4 use Scalar::Util qw(blessed);
  1         6  
  1         34  
44 1     1   5 use Class::ReluctantORM::Exception;
  1         1  
  1         26  
45 1     1   5 use Class::ReluctantORM::Utilities qw(install_method check_args);
  1         2  
  1         61  
46              
47             our $DEBUG ||= 0;
48              
49 1     1   5 use Scalar::Util qw(weaken);
  1         2  
  1         43  
50              
51 1     1   4 use base 'Class::ReluctantORM::SQL::Expression';
  1         2  
  1         73  
52 1     1   4 use base 'Class::ReluctantORM::SQL::From::Relation';
  1         1  
  1         652  
53              
54              
55             =head1 CONSTRUCTOR
56              
57             =cut
58              
59             =head2 $sq = SubQuery->new($select);
60              
61             Creates a new SubQuery containing the SQL object given by $select. The SQL
62             object's operation must be 'SELECT'.
63              
64             =cut
65              
66             sub new {
67 0     0 1   my $class = shift;
68 0           my $sql = shift;
69 0           my $self = bless {}, $class;
70 0           $self->statement($sql);
71 0           return $self;
72             }
73              
74             =head1 ACCESSORS
75              
76             =cut
77              
78             =head2 $sql = $sq->statement();
79              
80             =head2 $sq->statement($sql);
81              
82             Sets or reads the underlying SQL object.
83              
84             =cut
85              
86             sub statement {
87 0     0 1   my $self = shift;
88 0 0         unless (@_) {
89 0           return $self->get('statement');
90             }
91              
92 0           my $sql = shift;
93 0 0 0       unless (blessed($sql) && $sql->isa('Class::ReluctantORM::SQL')) {
94 0           Class::ReluctantORM::Exception::Param::WrongType->croak
95             (
96             param => 'sql',
97             value => $sql,
98             expected => 'Class::ReluctantORM::SQL',
99             );
100             }
101 0 0         unless ($sql->operation eq 'SELECT') {
102 0           Class::ReluctantORM::Exception::Param::BadValue->croak
103             (
104             param => 'sql',
105             value => $sql,
106             error => "SQL statement's operation() must be SELECT",
107             expected => 'SELECT-type SQL statement',
108             );
109             }
110 0           $self->set('statement', $sql);
111             }
112              
113             =head2 $bool = $sq->is_subquery();
114              
115             All objects of this class return true. The class adds this method to both Expression and Relation, making all other subclasses of them return false.
116              
117             =cut
118              
119 0     0     install_method('Class::ReluctantORM::SQL::From::Relation', 'is_subquery', sub { return 0; });
120 0     0     install_method('Class::ReluctantORM::SQL::Expression', 'is_subquery', sub { return 0; });
121 0     0 1   sub is_subquery { return 1; }
122              
123             #=======================================================#
124             # From Relation
125             #=======================================================#
126              
127              
128             =head2 $sq->alias('my_alias');
129              
130             =head2 $alias = $sq->alias();
131              
132             From Relation interface.
133              
134             Reads or sets the alias used for this relation in FROM clauses.
135              
136             =cut
137              
138             __PACKAGE__->mk_accessors('alias');
139              
140              
141             =head2 $str = $sq->pretty_print();
142              
143             From both Expression and Relation interfaces.
144              
145             Renders a human-readable version of the relation to a string.
146              
147             =cut
148              
149             sub pretty_print {
150 0     0 1   my $self = shift;
151 0           my %args = @_;
152 0   0       my $prefix = $args{prefix} || '';
153 0   0       my $str = $prefix . 'SUBQUERY alias:' . ($self->alias() || '(none)') . "\n";
154 0           $prefix .= ' ';
155 0           $str .= $self->statement->pretty_print(%args, prefix => $prefix);
156 0           return $str;
157             }
158              
159              
160             =head2 @cols = $sq->columns()
161              
162             Returns a list of re-aliased columns returned by the subquery. This presents the externally visible set of columns.
163              
164             From Relation interface.
165              
166             =cut
167              
168             sub columns {
169 0     0 1   my $self = shift;
170 0           my $sq_alias = $self->alias();
171 0           my @external_cols;
172 0           foreach my $internal_output_col ($self->statement->output_columns) {
173 0   0       my $ext_col = Column->new(
174             table => $sq_alias,
175             column => $internal_output_col->alias() || $internal_output_col->name(),
176             );
177 0           push @external_cols, $ext_col;
178             }
179 0           return @external_cols;
180             }
181              
182             =head2 $bool = $sq->has_column('col_name')
183              
184             Returns a boolean indicating whether a column is present in the external columns returned. The name will be the re-aliased name.
185              
186             From Relation interface.
187              
188             =cut
189              
190             sub has_column {
191 0     0 1   my $self = shift;
192 0           my $needle = shift;
193 0           return grep { $_->name eq $needle } $self->columns;
  0            
194             }
195              
196              
197             =head2 $bool = $sq->knows_all_columns()
198              
199             Returns a boolean indicating whether all output columns are known in advance from this relation. Always returns true for SubQueries.
200              
201             From Relation interface.
202              
203             =cut
204              
205 0     0 1   sub knows_all_columns { return 1; }
206              
207             =head2 $bool = $sq->knows_any_columns()
208              
209             Returns a boolean indicating whether any output columns are known in advance from this relation. Always returns true.
210              
211             From Relation interface.
212              
213             =cut
214              
215 0     0 1   sub knows_any_columns { return 1; }
216              
217             =head2 @tables = $sq->tables(%opts);
218              
219             Returns a list of all tables referenced in the FROM clause of the subquery.
220              
221             From the Relation interface.
222              
223             If the exclude_subqueries option is enabled, this returns an empty list.
224              
225             =cut
226              
227             sub tables {
228 0     0 1   my $self = shift;
229 0           my %opts = check_args(args => \@_, optional => [qw(exclude_subqueries)]);
230 0 0         if ($opts{exclude_subqueries}) {
231 0           return ();
232             } else {
233 0           return $self->statement()->from()->tables(%opts);
234             }
235              
236             }
237              
238              
239             #=======================================================#
240             # Conflicts
241             #=======================================================#
242              
243              
244             =head2 @rels = $sq->child_relations();
245              
246             Always returns an empty list. If you want to access the relations in the subquery, use $sq->statement->from->child_relations().
247              
248             From the Relation interface.
249              
250             =cut
251              
252 0     0 1   sub child_relations { return (); }
253              
254              
255             =head2 $bool = $sq->is_leaf_relation();
256              
257             Indicates if the object is a terminal point on the From tree. Always returns true.
258              
259             From the Relation interface.
260              
261             =cut
262              
263 0     0 1   sub is_leaf_relation { return 1; }
264              
265             =head2 $rel = $sq->parent_relation();
266              
267             Returns the parent node of the object. If undefined, this is the root node.
268              
269             From the Relation interface.
270              
271             =cut
272              
273             # Inherited
274              
275              
276             =head2 $bool = $sq->is_leaf_expression();
277              
278             Indicates if the object is a terminal point on the Expression tree. Always returns true.
279              
280             =cut
281              
282 0     0 1   sub is_leaf_expression { return 1; }
283              
284             =head2 @exps = $sq->child_expressions();
285              
286             Always returns an empty list.
287              
288             =cut
289              
290 0     0 1   sub child_expressions { return (); }
291              
292             =head2 $exp = $sq->parent_expression();
293              
294             Returns the parent node of the expression. If undefined, this is the root node.
295              
296             =cut
297              
298             # Inherited from Expression
299              
300             =head2 $clone = $sq->clone()
301              
302             Creates a new SubQuery whose statement is a clone of the original's statement.
303              
304             =cut
305              
306             sub clone {
307 0     0 1   my $self = shift;
308 0           my $class = ref $self;
309 0           return $class->new($self->statement->clone());
310             }
311              
312              
313             =head1 AUTHOR
314              
315             Clinton Wolfe January 2010
316              
317             =cut
318              
319             1;
320              
321