File Coverage

blib/lib/Class/ReluctantORM/SQL/From/Relation.pm
Criterion Covered Total %
statement 18 81 22.2
branch 0 20 0.0
condition 0 6 0.0
subroutine 6 22 27.2
pod 12 12 100.0
total 36 141 25.5


line stmt bran cond sub pod time code
1             package Class::ReluctantORM::SQL::From::Relation;
2              
3             =head1 NAME
4              
5             Class::ReluctantORM::SQL::From::Relation - Base class for SQL relations
6              
7             =head1 DESCRIPTION
8              
9             Abstract base class to represent a SQL relation.
10              
11             Known subclasses:
12              
13             =over
14              
15             =item Class::ReluctantORM::SQL::Table
16              
17             =item Class::ReluctantORM::SQL::From::Join
18              
19             =item Class::ReluctantORM::SQL::SubQuery
20              
21             =back
22              
23             =cut
24              
25 1     1   6 use strict;
  1         2  
  1         32  
26 1     1   7 use warnings;
  1         2  
  1         25  
27              
28 1     1   5 use Data::Dumper;
  1         2  
  1         43  
29 1     1   5 use Class::ReluctantORM::Exception;
  1         2  
  1         50  
30              
31             our $DEBUG ||= 0;
32              
33 1     1   6 use Scalar::Util qw(weaken);
  1         3  
  1         50  
34              
35 1     1   6 use base 'Class::Accessor::Fast';
  1         2  
  1         913  
36              
37              
38             =head1 VIRTUAL METHODS
39              
40             All of these methods are intended to be overridden in subclasses. Some methods
41             provide a default implementation.
42              
43             =cut
44              
45             =head2 $rel->alias('my_alias');
46              
47             =head2 $alias = $rel->alias();
48              
49             Reads or sets the alias used for this relation in SQL.
50              
51             =cut
52              
53             __PACKAGE__->mk_accessors('alias');
54              
55             =head2 @args = $arg->child_relations();
56              
57             Returns any children of the object. Results only defined if is_leaf is false.
58              
59             =cut
60              
61 0     0 1   sub child_relations { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
62              
63             =head2 @cols = $rel->columns()
64              
65             Returns a boolean indicating whether a column is present in this relation. Only valid if knows_columns() is true.
66              
67             No default implementation provided.
68              
69             =cut
70              
71 0     0 1   sub columns { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
72              
73             =head2 $bool = $rel->has_column('col_name')
74              
75             Returns a boolean indicating whether a column is present in this relation. Only valid if knows_columns() is true.
76              
77             No default implementation provided.
78              
79             =cut
80              
81 0     0 1   sub has_column { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
82              
83             =head2 $bool = $arg->is_leaf_relation();
84              
85             Indicates if the object is a terminal point on the From tree. Default implementation returns true.
86              
87             =cut
88              
89 0     0 1   sub is_leaf_relation { return 1; }
90              
91             =head2 $bool = $rel->knows_all_columns()
92              
93             Returns a boolean indicating whether all output columns are known in advance from this relation.
94              
95             No default implementation provided.
96              
97             =cut
98              
99 0     0 1   sub knows_all_columns { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
100              
101             =head2 $bool = $rel->knows_any_columns()
102              
103             Returns a boolean indicating whether any output columns are known in advance from this relation.
104              
105             No default implementation provided.
106              
107             =cut
108              
109 0     0 1   sub knows_any_columns { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
110              
111              
112             =head2 $rel = $rel->parent_relation();
113              
114             Returns the parent node of the object. If undefined, this is the root node.
115              
116             =cut
117              
118             sub parent_relation {
119 0     0 1   my $self = shift;
120 0 0         if (@_) {
121 0           my $real = shift;
122 0           my $weak_ref = \$real;
123 0           weaken($weak_ref);
124 0           $self->set('parent_ref', $weak_ref);
125             }
126 0           my $ref = $self->get('parent_ref');
127 0 0         if ($ref) {
128 0           return ${$ref};
  0            
129             } else {
130 0           return;
131             }
132             }
133              
134             =head2 $str = $rel->pretty_print();
135              
136             Renders a human-readable version of the relation to a string.
137              
138             =cut
139              
140 0     0 1   sub pretty_print { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
141              
142             =head2 @tables = $rel->tables(%opts);
143              
144             Returns a list of all tables referenced in the relation and its children.
145              
146             Supported options:
147              
148             =over
149              
150             =item exclude_subqueries
151              
152             Optional boolean, default false. If true, tables mentioned only in subqueries will not be included.
153              
154             =back
155              
156             =cut
157              
158 0     0 1   sub tables { Class::ReluctantORM::Exception::Call::PureVirtual->croak(); }
159              
160             =head2 $table = $rel->leftmost_table();
161              
162             Finds the "base" table, the one added earliest. This will return either a Table or a SubQuery, but never a Join.
163              
164             =cut
165              
166             sub leftmost_table {
167 0     0 1   my $rel = shift;
168 0           until (!$rel->is_join()) {
169 0           $rel = $rel->left_relation();
170             }
171 0           return $rel;
172             }
173              
174             =begin devnotes
175              
176             =head2 $table = $rel->_find_latest_table($seek_table);
177              
178             Performs a right-branch-first search of the relation tree, looking for a table that matches the schema name and table name of the given argument. Alias is ignored.
179              
180             This finds the last table of that name to be added.
181              
182             =cut
183              
184             sub _find_latest_table {
185 0     0     my $rel = shift;
186 0           my $seek = shift;
187 0 0         if ($rel->__matches_table($seek)) { return $rel; }
  0            
188 0           foreach my $kid (reverse $rel->child_relations) {
189 0           my $result = $kid->_find_latest_table($seek);
190 0 0         if ($result) { return $result; }
  0            
191             }
192 0           return;
193             }
194              
195             =begin devnotes
196              
197             =head2 $table = $rel->_find_earliest_table($seek_table);
198              
199             Performs a left-branch-first search of the relation tree, looking for a table that matches the schema name and table name of the given argument. Alias is ignored.
200              
201             This finds the first table of that name to be added.
202              
203             =cut
204              
205              
206             sub _find_earliest_table {
207 0     0     my $rel = shift;
208 0           my $seek = shift;
209 0 0         if ($rel->__matches_table($seek)) { return $rel; }
  0            
210 0           foreach my $kid ($rel->child_relations) {
211 0           my $result = $kid->_find_earliest_table($seek);
212 0 0         if ($result) { return $result; }
  0            
213             }
214 0           return;
215             }
216              
217              
218              
219             sub __matches_table {
220 0     0     my $table = shift;
221 0 0         unless ($table->is_table) { return 0; }
  0            
222 0           my $seek = shift;
223 0 0 0       if (1 # for formatting
      0        
224             && $seek->schema
225             && $table->schema
226             && $table->schema eq $seek->schema
227             ) {
228 0           return $table->table eq $seek->table;
229             }
230 0           return $table->table eq $seek->table;
231             }
232              
233             =head2 $rel->walk_leaf_relations($coderef);
234              
235             Recurses throughout the relation tree, and executes the coderef on each leaf of the relation.
236              
237             The coderef will be passed the leaf relation as the only parameter.
238              
239             =cut
240              
241             sub walk_leaf_relations {
242 0     0 1   my $rel = shift;
243 0           my $coderef = shift;
244 0 0         if ($rel->is_leaf_relation()) {
245 0           $coderef->($rel);
246             } else {
247 0           foreach my $child ($rel->child_relations()) {
248 0           $child->walk_leaf_relations($coderef);
249             }
250             }
251             }
252              
253              
254             =head2 @joins = $rel->joins()
255              
256             Returns a list of any Joins present in the children of this Relation.
257              
258             =cut
259              
260             sub joins {
261 0     0 1   my ($rel) = @_;
262 0 0         unless ($rel->is_join()) { return (); }
  0            
263 0           return ($rel, map { $_->joins() } $rel->child_relations());
  0            
264             }
265              
266             sub __break_links {
267 0     0     my $rel = shift;
268              
269             # We maintain links both ways - parent to child and child to parent. Break them.
270 0           foreach my $crel ($rel->child_relations) {
271 0           $crel->__break_links();
272             }
273 0           $rel->set('parent_ref', undef);
274             }
275              
276              
277             =head1 AUTHOR
278              
279             Clinton Wolfe January 2009
280              
281             =cut
282              
283             1;