File Coverage

blib/lib/Class/ReluctantORM/SQL.pm
Criterion Covered Total %
statement 72 866 8.3
branch 0 398 0.0
condition 0 100 0.0
subroutine 24 100 24.0
pod 35 39 89.7
total 131 1503 8.7


line stmt bran cond sub pod time code
1             package Class::ReluctantORM::SQL;
2              
3             =head1 NAME
4              
5             Class::ReluctantORM::SQL - Represent SQL Statements
6              
7             =head1 SYNOPSIS
8              
9             use Class::ReluctantORM::SQL::Aliases;
10              
11             # Insert
12             my $insert = Class::ReluctantORM::SQL->new('insert');
13             $insert->table(Table->new(table => 'table_name'));
14              
15             # TODO DOCS
16              
17             $sql->table(Table->new($tb_class);
18             $sql->add_input($sql_column);
19             $sql->add_output($sql_column);
20              
21             =head1 DESCRIPTION
22              
23             Represent SQL DML statements (INSERT, SELECT, UPDATE, and DELETE) in an abstract, driver-independent way. Class::ReluctantORM uses this suite of classes to construct each statement that it executes, then passes it to the Driver for rendering and execution. Results are then stored in the SQL object, and may be retrieved directly or inflated into Class::ReluctantORM objects.
24              
25             =head2 Clauses, Relations, and Expressions
26              
27             The SQL objects are loosely grouped into 4 categories:
28              
29             =over
30              
31             =item Statement - Class::ReluctantORM::SQL
32              
33             Represents a DML SQL statement, its parameters and bindings, and output columns and fetched values.
34              
35             Provides a location for the clauses, whether as strings or as objects.
36              
37             =item Clauses - Where, From, OrderBy, Limit
38              
39             Represents major portions of the statement. These clauses are independent objects which are built separately, then attached to the SQL statment object.
40              
41             =item Relations - Table, Join, SubQuery
42              
43             Represents a table-like entity. Relations share a common superclass (Class::ReluctantORM::SQL::Relation), know about their columns, and are used in From clauses.
44              
45             =item Expressions - Literal, FunctionCall, Column, Param
46              
47             Represents an expression, which may be used in numerous locations.
48              
49             =back
50              
51              
52             =head2 Retrieving and Inflating Results
53              
54             Some SQL statement objects can have OutputColumn objects associated with them (this includes all SELECT statments, and INSERT and UPDATE statements with RETURNING clauses). As results are retrieved, the values are stored in these OutputColumns.
55              
56             If the statement is expected to only have one row of results, you can simply do this:
57              
58             $driver->run_sql($sql);
59             foreach my $oc ($sql->output_columns) {
60             # do something with $oc->output_value();
61             }
62              
63             If the statement is expected to return multiple rows, you should register a callback:
64              
65             my $handle_fetchrow = sub {
66             my $sql = shift;
67             foreach my $oc ($sql->output_columns) {
68             # do something with $oc->output_value();
69             }
70             };
71             $sql->add_fetchrow_listener($handle_fetchrow);
72             $driver->run_sql($sql)
73              
74             If you are seeking Class::ReluctantORM model objects (like Ships and Pirates), you need to use the inflation facility:
75              
76             if ($sql->is_inflatable()) {
77             @ships = $sql->inflate();
78             } else {
79             # Too complex
80             }
81              
82             =head2 Parsing Support
83              
84             Parsing support is provided by the Driver area. See Class::ReluctantORM::Driver.
85              
86             =head2 Non-Parsed SQL Support
87              
88             If you perform a query with 'parse_sql' false (or set that as a global default, see Class::ReluctantORM - Non-Parsed SQL Support), the SQL object still acts as the data object and provides execution and fetching services. Instead of populating the where attribute (which is expected to be a Where object), populate the raw_where attribute (which is expected to be a string, the SQL WHERE clause).
89              
90             You may build your SQL object out of a mix of objects and raw SQL, though this is less likely to work.
91              
92             Support is eventually planned for there to be a rw_from, raw_ordeR_by, raw_group_by, and raw_statement. For now, only raw_where is supported.
93              
94              
95              
96             =begin devdocs
97              
98             also provide raw_order_by raw_from raw_group_by and raw_statement
99              
100             =cut
101              
102             =head2 Annotate and Reconcile
103              
104             After constructing a SQL object, it will usually need some additional metadata associated with it before being executed. This metadata can generally be discovered automatically.
105              
106             The annotate() method is called internally (usually before an inflate()) to associate table references with classes in your model.
107              
108             The reconcile() method is called internally before the rendering process to ensure that all column and table references are resolvable and unambiguous.
109              
110             =head2 Auto-Aliasing of SQL Classes
111              
112             Because the class names tend to get rather long, this module by default
113             exports subroutines whose return value is the name of a SQL class. For example:
114              
115             Table() # returns 'Class::ReluctantORM::SQL::Table';
116              
117             This allows you to do this:
118              
119             my $table = Table->new(...);
120              
121             This functionality is very similar to that provided by the 'aliased' CPAN module,
122             but here is provided automatically.
123              
124             =head2 Limitations
125              
126             This is not a general purpose SQL abstraction library, but it is close.
127             Operations that are not supported by Class::ReluctantORM will generally not be well supported by this module.
128              
129             In particular:
130              
131             =over
132              
133             =item DML only
134              
135             No support for data definition language (CREATE TABLE, etc) is planned.
136              
137             =item Single-table INSERTs, UPDATEs, and DELETEs
138              
139             There is no support for UPDATE ... FROM, for example.
140              
141             =item Aggregate Support is in its infancy
142              
143             Aggregates are not supported in combination with JOINs.
144              
145             =back
146              
147             =cut
148              
149 1     1   5 use strict;
  1         2  
  1         29  
150 1     1   5 use warnings;
  1         2  
  1         35  
151             our $DEBUG ||=2;
152              
153 1     1   4 use Data::Dumper;
  1         3  
  1         53  
154 1     1   6 use Scalar::Util qw(blessed);
  1         7  
  1         46  
155 1     1   5 use Class::ReluctantORM::Utilities qw(check_args);
  1         2  
  1         48  
156 1     1   5 use Class::ReluctantORM::FetchDeep::Results qw(fd_inflate);
  1         9  
  1         45  
157              
158 1     1   5 use base 'Class::ReluctantORM::OriginSupport';
  1         2  
  1         70  
159 1     1   6 use base 'Class::Accessor::Fast';
  1         2  
  1         59  
160              
161 1     1   5 use Class::ReluctantORM::SQL::Aliases;
  1         1  
  1         100  
162              
163 1     1   598 use Class::ReluctantORM::SQL::Column;
  1         3  
  1         8  
164 1     1   564 use Class::ReluctantORM::SQL::Expression::Criterion;
  1         4  
  1         17  
165 1     1   36 use Class::ReluctantORM::SQL::Expression;
  1         3  
  1         6  
166 1     1   854 use Class::ReluctantORM::SQL::From;
  1         4  
  1         29  
167 1     1   6 use Class::ReluctantORM::SQL::Function;
  1         2  
  1         8  
168 1     1   23 use Class::ReluctantORM::SQL::Expression::FunctionCall;
  1         2  
  1         16  
169 1     1   19 use Class::ReluctantORM::SQL::From::Join;
  1         2  
  1         4  
170 1     1   20 use Class::ReluctantORM::SQL::Expression::Literal;
  1         1  
  1         11  
171 1     1   580 use Class::ReluctantORM::SQL::OrderBy;
  1         2  
  1         22  
172 1     1   506 use Class::ReluctantORM::SQL::OutputColumn;
  1         4  
  1         6  
173 1     1   96 use Class::ReluctantORM::SQL::Param;
  1         2  
  1         7  
174 1     1   21 use Class::ReluctantORM::SQL::From::Relation;
  1         2  
  1         7  
175 1     1   570 use Class::ReluctantORM::SQL::SubQuery;
  1         3  
  1         10  
176 1     1   23 use Class::ReluctantORM::SQL::Table;
  1         2  
  1         9  
177 1     1   550 use Class::ReluctantORM::SQL::Where;
  1         4  
  1         8671  
178              
179              
180             =head1 CONSTRUCTORS
181              
182             =cut
183              
184             =head2 $sql = SQL->new('operation');
185              
186             Creates a new abstract SQL object. Operation must be one of
187             INSERT, UPDATE, DELETE, or SELECT. Case is ignored.
188              
189             =cut
190              
191             our %OPERATIONS = map {uc($_) => 1} qw(select update delete insert);
192              
193             sub new {
194 0     0 1   my $class = shift;
195 0           my $op = shift;
196 0 0         unless ($op) { Class::ReluctantORM::Exception::Param::Missing->croak(param => 'operation'); }
  0            
197 0 0         unless (exists $OPERATIONS{uc($op)}) {
198 0           Class::ReluctantORM::Exception::Param::BadValue->croak(param => 'operation', value => uc($op));
199             }
200              
201 0           my $self = bless {}, $class;
202 0           $self->set('operation', uc($op));
203 0           $self->{outputs} = [];
204 0           $self->{inputs} = [];
205 0           $self->{fetchrow_listeners} = [];
206 0           $self->{reconcile_options} =
207             {
208             add_output_columns => 1,
209             realias_raw_sql => 1,
210             };
211              
212 0           $self->__set_unique_alias_prefix();
213 0           $self->table_alias_counter(0);
214 0           $self->column_alias_counter(0);
215 0           $self->capture_origin();
216              
217 0           return $self;
218             }
219              
220             # Internal
221             __PACKAGE__->mk_accessors(qw(unique_alias_prefix));
222             sub __set_unique_alias_prefix {
223 0     0     my $self = shift;
224             # Derive a unique prefix from the memory address of $self
225             # using the last 4 digits of the address
226 0           my ($address) = "$self" =~ /0x.+([a-f0-9]{4})\)$/;
227 0           $self->unique_alias_prefix('_' . $address . '_');
228             }
229              
230             =head1 ACCESSORS AND MUTATORS
231              
232             =cut
233              
234             =head2 $sql->add_input($col, $param);
235              
236             Adds an input column to the statement. Valid only for
237             insert and update operations.
238              
239             Arguments are the SQL::Column that should get the value stored to it,
240             and the SQL::Param that will carry the value.
241              
242             =cut
243              
244             sub add_input {
245 0     0 1   my $self = shift;
246 0           my $col = shift;
247 0           my $param = shift;
248              
249 0           my %permitted = map {uc($_) => 1} qw(update insert);
  0            
250              
251 0 0         unless (exists $permitted{$self->operation}) {
252 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak('add_input is only permitted for UPDATE and INSERT operations');
253             }
254 0 0 0       unless (blessed($col) && $col->isa('Class::ReluctantORM::SQL::Column')) {
255 0           Class::ReluctantORM::Exception::Param::WrongType->croak(param => 'column', expected => 'Class::ReluctantORM::SQL::Column');
256             }
257              
258 0 0         unless ($self->input_subquery) {
259 0 0 0       unless (blessed($param) && $param->isa('Class::ReluctantORM::SQL::Param')) {
260 0           Class::ReluctantORM::Exception::Param::WrongType->croak(param => 'param', expected => 'Class::ReluctantORM::SQL::Param');
261             }
262             }
263              
264 0           push @{$self->{inputs}}, {column => $col, param => $param};
  0            
265              
266 0           return 1;
267              
268             }
269              
270             =head2 $oc = $sql->add_output($output_column);
271              
272             =head2 $oc = $sql->add_output($column);
273              
274             =head2 $oc = $sql->add_output($expression);
275              
276             Adds an output column to the statement. Valid only for
277             insert, select and update operations.
278              
279             In the first form, an OutputColumn you have constructed is added to the list directly.
280              
281             In the second and third forms, the argument is first wrapped in a new OutputColumn object, then added. Note that a Column is a subclass of Expression, so this is really the same usage.
282              
283             The (possibly new) OutputColumn is returned.
284              
285             =cut
286              
287             sub add_output {
288 0     0 1   my $self = shift;
289 0           my $oc = shift;
290              
291 0 0 0       if (blessed($oc) && $oc->isa(Expression)) {
    0 0        
292 0           $oc = OutputColumn->new($oc);
293             } elsif (!(blessed($oc) && $oc->isa(OutputColumn))) {
294 0           Class::ReluctantORM::Exception::Param::WrongType->croak(param => 'expression', expected => Expression, error => "need an Expression or a OutputColumn object");
295             }
296              
297 0           my %permitted = map {uc($_) => 1} qw(update insert select);
  0            
298              
299 0 0         unless (exists $permitted{$self->operation}) {
300 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak('add_output is only permitted for SELECT, UPDATE and INSERT operations');
301             }
302 0           push @{$self->{outputs}}, $oc;
  0            
303              
304 0           return $oc;
305             }
306              
307             =head2 $sql->remove_all_outputs();
308              
309             Removes all output columns from the SQL statement.
310              
311             =cut
312              
313             sub remove_all_outputs {
314 0     0 1   my $self = shift;
315 0           $self->{outputs} = [];
316             }
317              
318             # Internal
319             __PACKAGE__->mk_accessors(qw(table_alias_counter));
320              
321             # Internal SQL-to-Driver linkage
322             __PACKAGE__->mk_accessors(qw(_sth _sql_string _execution_driver));
323              
324             =head2 $str = $sql->new_table_alias();
325              
326             Get a table alias that is certainly unique within this SQL statement, and probaby unique accross substatements (and superstatments, if you will).
327              
328             =cut
329              
330             sub new_table_alias {
331 0     0 1   my $self = shift;
332 0           my $counter = $self->table_alias_counter($self->table_alias_counter() + 1);
333 0           my $pfx = $self->unique_alias_prefix();
334 0           return 'tx' . $pfx . sprintf('%04d', $counter);
335             }
336              
337             # Internal
338             __PACKAGE__->mk_accessors(qw(column_alias_counter));
339              
340             =head2 $str = $sql->column_table_alias();
341              
342             Get a column alias that is certainly unique within this SQL statement, and probaby unique accross substatements (and superstatments, if you will).
343              
344             =cut
345              
346             sub new_column_alias {
347 0     0 0   my $self = shift;
348 0           my $counter = $self->column_alias_counter($self->column_alias_counter() + 1);
349 0           my $pfx = $self->unique_alias_prefix();
350 0           return 'cx' . $pfx . sprintf('%04d', $counter);
351             }
352              
353              
354             =head2 @bindings = $sql->get_bind_values();
355              
356             Returns an array of values bound to the
357             parameters of the query, in query placeholder order.
358              
359             This will include input bindings first, followed by where clause bindings.
360              
361             =cut
362              
363             sub get_bind_values {
364 0     0 1   my $self = shift;
365 0           my @binds = (
366 0           (map { $_->bind_value } $self->input_params),
367 0           ($self->raw_where ? map { $_->bind_value } $self->_raw_where_params : ()),
368 0 0         ($self->where ? map { $_->bind_value } $self->where->params : ()),
    0          
369             );
370 0           return @binds;
371             }
372              
373             sub params {
374 0     0 0   my $self = shift;
375 0 0         my @params = (
    0          
    0          
376             $self->input_params,
377             ($self->input_subquery ? $self->input_subquery->params : ()),
378             ($self->raw_where ? $self->_raw_where_params : ()),
379             ($self->where ? $self->where->params : ()),
380             );
381 0           return @params;
382             }
383              
384             =head2 $q = $sql->input_subquery();
385              
386             =head2 $sql->input_subquery($subquery);
387              
388              
389             Applicable only to INSERT statements. Sets a SubQuery to use as the source for INSERT ... SELECT statements.
390              
391             =cut
392              
393             sub input_subquery {
394 0     0 1   my $self = shift;
395 0 0         if (@_) {
396 0           my $sq = shift;
397 0 0 0       unless (blessed($sq) && $sq->isa(SubQuery)) {
398 0           Class::ReluctantORM::Exception::Param::WrongType->croak(param => 'subquery', expected => SubQuery, value => $sq);
399             }
400 0 0         unless ($self->operation() eq 'INSERT') {
401 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak('You may only set an input_subquery on an INSERT statment. This is a ' . $self->operation . " statement.");
402             }
403 0           $self->set('input_subquery', $sq);
404             }
405 0           return $self->get('input_subquery');
406              
407             }
408              
409             =head2 $sql->set_bind_values($val1, $val2,...);
410              
411             Binds the given values to the parameters in the where clause.
412              
413             =cut
414              
415             sub set_bind_values {
416 0     0 1   my $self = shift;
417 0           my @vals = @_;
418 0           my @params = $self->params();
419 0 0         if (@vals < @params) {
    0          
420 0           Class::ReluctantORM::Exception::Param::Missing->croak('The number of values must match the number of parameters in the where clause.');
421             } elsif (@vals > @params) {
422 0           Class::ReluctantORM::Exception::Param::Spurious->croak('The number of values must match the number of parameters in the where clause.');
423             }
424 0           for my $i (0..(@params - 1)) {
425 0           $params[$i]->bind_value($vals[$i]);
426             }
427             }
428              
429              
430             =head2 $from = $sql->from();
431              
432             =head2 $sql->from($sql_FROM_object);
433              
434             Gets or sets the FROM clause of the query. The argument is a
435             Class::ReluctantORM::SQL::From .
436              
437             =cut
438              
439             sub from {
440 0     0 1   my $self = shift;
441 0 0         if (@_) {
442 0           my $thing = shift;
443 0 0         if (!ref($thing)) {
    0          
444             # Setting raw_from via from() - kinda sloppy
445 0           $self->raw_from($thing);
446             } elsif ($thing->isa(From)) {
447             # Clear raw_from
448 0           $self->raw_from(undef);
449 0           $self->set('from', $thing);
450             } else {
451 0           Class::ReluctantORM::Exception::Param::WrongType->croak
452             (
453             param => 'from',
454             expected => From . ' or raw SQL string',
455             value => $thing,
456             );
457             }
458             }
459 0           return $self->get('from');
460             }
461              
462             =begin vaporware
463              
464             =head2 $str = $sql->raw_from();
465              
466             =head2 $sql->raw_from();
467              
468             If you choose not to (or are unable to) use the From object to represent your FROM clause, you can use this facility to pass in a raw SQL string that will be used as the from clause.
469              
470             It will not pass through unmolested - see Class::ReluctantORM::Driver - Raw SQL Mangling .
471              
472             =cut
473              
474             sub raw_from {
475 0     0 1   my $self = shift;
476 0 0         if (@_) {
477 0           my $thing = shift();
478 0 0         if (!defined($thing)) {
    0          
479             # OK, clearing
480 0           $self->set('raw_from', undef);
481             } elsif (!ref($thing)) {
482 0           $self->set('from', undef);
483 0           $self->set('raw_from', $thing);
484             } else {
485 0           Class::ReluctantORM::Exception::Param::WrongType->croak
486             (
487             param => 'raw_from',
488             expected => 'raw SQL string',
489             value => $thing,
490             );
491             }
492             }
493 0           return $self->get('raw_from');
494             }
495              
496              
497              
498             =head2 @pairs = $sql->inputs();
499              
500             Returns the list of inputs as an array of hashrefs. Each hashref has keys 'column' and 'param'.
501              
502             Only valid for INSERT and UPDATE statements.
503              
504             =cut
505              
506             sub inputs {
507 0     0 1   my $self = shift;
508 0 0 0       unless ($self->operation eq 'INSERT' || $self->operation eq 'UPDATE' ) { Class::ReluctantORM::Exception::Call::NotPermitted->croak('May only call inputs() on an INSERT or UPDATE statement. Use input_params instead.'); }
  0            
509 0 0         return @{$self->{inputs} || []};
  0            
510             }
511              
512             =head2 @params = $sql->input_params();
513              
514             Returns the list of input params as an array.
515              
516             To get where clause params, call $sql->where->params();
517              
518             =cut
519              
520             sub input_params {
521 0     0 1   my $self = shift;
522 0 0 0       if ($self->operation eq 'INSERT' || $self->operation eq 'UPDATE') {
523 0 0         if ($self->input_subquery) {
524 0           return $self->input_subquery->statement->params();
525             } else {
526 0           return map { $_->{param} } $self->inputs;
  0            
527             }
528             } else {
529 0           return ();
530             }
531             }
532              
533              
534             =head2 $int = $sql->limit();
535              
536             =head2 $sql->limit($int);
537              
538             =head2 $sql->limit(undef);
539              
540             Reads, sets, or clears the LIMIT clause of the statement.
541              
542             =cut
543              
544             __PACKAGE__->mk_accessors(qw(limit));
545              
546             =head2 $int = $sql->offset();
547              
548             =head2 $sql->offset($int);
549              
550             =head2 $sql->offset(undef);
551              
552             Reads, sets, or clears the OFFSET clause of the statement.
553              
554             =cut
555              
556             __PACKAGE__->mk_accessors(qw(offset));
557              
558              
559              
560              
561             =head2 $op = $sql->operation();
562              
563             Reads the operation (command) of the SQL statement. Result
564             will be one of INSERT, DELETE, SELECT, or UPDATE.
565              
566             =cut
567              
568             sub operation {
569 0     0 1   my $self = shift;
570 0 0         if (@_) { Class::ReluctantORM::Exception::Call::NotMutator->croak(); }
  0            
571 0           return $self->get('operation');
572             }
573              
574             =head2 $where = $sql->order_by();
575              
576             =head2 $sql->order_by($order);
577              
578             Sets the optional ORDER BY clause of the query. The argument is a
579             Class::ReluctantORM::SQL::OrderBy .
580              
581             =cut
582              
583             sub order_by {
584 0     0 1   my $self = shift;
585 0 0         if (@_) {
586 0           $self->set('order_by', shift);
587             }
588 0           my $ob = $self->get('order_by');
589 0 0         unless ($ob) {
590 0           $ob = OrderBy->new();
591 0           $self->set('order_by', $ob);
592             }
593              
594 0           return $ob;
595             }
596              
597              
598              
599             =head2 @cols = $sql->output_columns();
600              
601             Returns the list of output columns as OutputColumns.
602              
603             =cut
604              
605             sub output_columns {
606 0     0 1   my $self = shift;
607 0           return @{$self->{outputs}};
  0            
608             }
609              
610             =head2 $table = $sql->table();
611              
612             =head2 $sql->table($table);
613              
614             Reads or sets the target table for use with INSERT, UPDATE, and DELETE queries.
615             It is invalid to call this on a SELECT query (use from() to set a From clause, instead).
616              
617             =cut
618              
619             sub table {
620 0     0 1   my $self = shift;
621 0 0         if ($self->operation eq 'SELECT') { Class::ReluctantORM::Exception::Call::NotPermitted->croak('Do not call table() on a SELECT query. Use tables() to read tables and from() to set a from clause.'); }
  0            
622 0 0         if (@_) {
623 0           my $t = shift;
624 0 0 0       unless (blessed($t) && $t->isa(Table)) { Class::ReluctantORM::Exception::Param::WrongType->croak(expected => Table, value => $t); }
  0            
625 0           $self->set('table', $t);
626             }
627 0           return $self->get('table');
628             }
629              
630             =head2 $table = $sql->base_table();
631              
632             =cut
633              
634             sub base_table {
635 0     0 1   my $sql = shift;
636 0 0         if (@_) {
637 0           Class::ReluctantORM::Exception::Call::NotMutator->croak();
638             }
639 0 0         if ($sql->operation() eq 'SELECT') {
640 0           return $sql->from()->root_relation()->leftmost_table();
641             } else {
642 0           return $sql->table();
643             }
644             }
645              
646             =head2 @tables = $sql->tables(%opts);
647              
648             Returns an array of all tables involved in the query, both from the from clause and the where clause.
649              
650             Supported options:
651              
652             =over
653              
654             =item exclude_subqueries
655              
656             Optional boolean, default false. If true, tables mentioned only in subqueries will not be included.
657              
658             =back
659              
660             =cut
661              
662             sub tables {
663 0     0 1   my $self = shift;
664 0           my %opts = check_args(args => \@_, optional => [qw(exclude_subqueries)]);
665              
666 0           my @from_tables;
667 0 0         if ($self->operation eq 'SELECT') {
668 0 0         unless ($self->from) {
669 0           Class::ReluctantORM::Exception::Call::NotPermitted->croak('For SELECT statements, you must set the FROM clause using from() before calling tables().');
670             }
671 0 0         @from_tables = $self->from ? $self->from->tables(%opts) : ();
672             } else {
673 0 0         @from_tables = $self->table ? ($self->table()) : ();
674             }
675              
676 0 0         my @where_tables = $self->where ? $self->where->tables(%opts) : ();
677              
678             # Unique-ify this list using their memory addresses
679 0           my %tables = map {('' . $_ . '') => $_ } (@from_tables, @where_tables);
  0            
680 0           return values %tables;
681             }
682              
683              
684             =head2 $where = $sql->where();
685              
686             =head2 $sql->where($sql_where);
687              
688             Reads or sets the WHERE clause of the query. The argument is a
689             Class::ReluctantORM::SQL::Where .
690              
691             =cut
692              
693             sub where {
694 0     0 1   my $self = shift;
695 0 0         if (@_) {
696 0           my $thing = shift;
697 0 0         if (!ref($thing)) {
    0          
698             # Setting raw_where via where() - kinda sloppy
699 0           $self->raw_where($thing);
700             } elsif ($thing->isa(Where)) {
701             # Clear raw_where
702 0           $self->raw_where(undef);
703 0           $self->set('where', $thing);
704             } else {
705 0           Class::ReluctantORM::Exception::Param::WrongType->croak
706             (
707             param => 'where',
708             expected => Where . ' or raw SQL string',
709             value => $thing,
710             );
711             }
712             }
713 0           return $self->get('where');
714             }
715              
716             =head2 $str = $sql->raw_where();
717              
718             =head2 $sql->raw_where();
719              
720             If you choose not to (or are unable to) use the Where object to represent your WHERE clause, you can use this facility to pass in a raw SQL string that will be used as the where clause.
721              
722             It will not pass through unmolested - see Class::ReluctantORM::Driver - Raw SQL Mangling .
723              
724             =cut
725              
726             sub raw_where {
727 0     0 1   my $self = shift;
728 0 0         if (@_) {
729 0           my $thing = shift();
730 0 0         if (!defined($thing)) {
    0          
731             # OK, clearing
732 0           $self->set('raw_where', undef);
733             } elsif (!ref($thing)) {
734 0           $self->set('where', undef);
735 0           $self->__find_raw_where_params($thing);
736 0           $self->set('raw_where', $thing);
737             } else {
738 0           Class::ReluctantORM::Exception::Param::WrongType->croak
739             (
740             param => 'raw_where',
741             expected => 'raw SQL string',
742             value => $thing,
743             );
744             }
745             }
746 0           return $self->get('raw_where');
747             }
748              
749             sub _raw_where_execargs {
750 0     0     my $self = shift;
751 0 0         if (@_) {
752 0           $self->set('raw_where_execargs', shift);
753             }
754 0           return $self->get('raw_where_execargs');
755             }
756              
757             sub _raw_where_pristine {
758 0     0     my $self = shift;
759 0 0         if (@_) {
760 0           $self->set_reconcile_option('realias_raw_sql', !shift);
761             }
762             }
763              
764             sub _cooked_where {
765 0     0     my $self = shift;
766 0 0         if (@_) {
767 0           $self->set('cooked_where', shift);
768             }
769 0           return $self->get('cooked_where');
770             }
771              
772             sub __find_raw_where_params {
773 0     0     my $self = shift;
774 0           my $raw = shift;
775             # TODO - check for ?'s in quoted strings more effectively
776 0           while ($raw =~ s{'.*?'}{}g) { } # Crudely delete all quoted strings from the SQL
777 0           my @params = map { Param->new() } $raw =~ m{(\?)}g;
  0            
778 0           $self->_raw_where_params(\@params);
779             }
780              
781             sub _raw_where_params {
782 0     0     my $self = shift;
783 0 0         if (@_) {
784 0           $self->set('raw_where_params', shift);
785             }
786 0 0         return @{$self->get('raw_where_params') || []};
  0            
787             }
788              
789              
790              
791             #========================================================#
792             # Inflation Support
793             #========================================================#
794              
795             =head1 INFLATION SUPPORT
796              
797             These methods implement the ability to create CRO model objects from a SQL query object.
798              
799             =cut
800              
801             =head2 $bool = $sql->is_inflatable(%make_inflatable_opts);
802              
803             =head2 ($bool, $exception) = $sql->is_inflatable(%make_inflatable_opts);
804              
805             Analyzes the SQL statement and tries to determine if it
806             can be successfully used to inflate CRO model objects after
807             execution. Calls make_inflatable before performing the analysis, passing on any options.
808              
809             This captures any exception from the analysis, and optionally returns it in the second form.
810              
811             A false return from is_inflatable indicates that inflate() will certainly fail before executing.
812              
813             A true return indicates that inflate() will survive at least until execution
814             (a runtime database error may still occur).
815              
816             =cut
817              
818             sub is_inflatable {
819 0     0 1   my $sql = shift;
820 0           my %args = check_args(args => \@_, optional => [qw(auto_annotate auto_reconcile add_output_columns)]);
821 0 0         unless (defined($args{auto_annotate})) { $args{auto_annotate} = 1; }
  0            
822 0 0         unless (defined($args{auto_reconcile})) { $args{auto_reconcile} = 1; }
  0            
823 0 0         unless (defined($args{add_output_columns})) { $args{add_output_columns} = 1; }
  0            
824              
825 0           eval {
826 0           $sql->make_inflatable(%args);
827             };
828 0 0         if ($@) {
829 0 0         return wantarray ? (0, $@) : 0;
830             }
831              
832             # Inflatability checks
833 0           my @checks =
834             (
835             '__is_inflatable_find_base_class',
836             '__is_inflatable_has_output_columns',
837             '__is_inflatable_all_non_join_tables_are_in_relationships',
838             '__is_inflatable_all_joins_have_relationships',
839             '__is_inflatable_essential_output_columns_present_and_reconciled',
840             );
841              
842 0           my $inflatable = 1;
843 0           my $exception = undef;
844 0           foreach my $check (@checks) {
845 0 0         if ($inflatable) {
846 0           my $check_result = 1;
847 0           ($check_result, $exception) = $sql->$check;
848 0   0       $inflatable &&= $check_result;
849             }
850             }
851              
852 0 0         return wantarray ? ($inflatable, $exception) : $inflatable;
853             }
854              
855              
856             sub __is_inflatable_find_base_class {
857 0     0     my $sql = shift;
858 0           my $base_table = $sql->base_table();
859 0 0         return $base_table->class() ? (1, undef) : (0, Class::ReluctantORM::Exception::SQL::NotInflatable->new(error => 'Base table does not have a class associated with it', sql => $sql));
860             }
861              
862             sub __is_inflatable_has_output_columns {
863 0     0     my $sql = shift;
864 0 0         return (scalar $sql->output_columns) ? (1, undef) : (0, Class::ReluctantORM::Exception::SQL::NotInflatable->new(error => 'SQL object has no output columns', sql => $sql));
865             }
866              
867             sub __is_inflatable_essential_output_columns_present_and_reconciled {
868 0     0     my $sql = shift;
869              
870 0           my $ok = 1;
871 0           my $check = 1;
872 0           my $exception = undef;
873              
874 0           my %cache =
875 0           map { __is_inflatable_EOCPAR_column_name($_) => $_}
876             $sql->output_columns();
877              
878             # Check the base table
879 0           my $base = $sql->base_table();
880 0           ($check, $exception) = __is_inflatable_EOCPAR_columns_present_for_table($base, \%cache);
881 0   0       $ok &&= $check;
882              
883             # Check all relationships
884 0 0         if ($sql->from()) {
885 0           foreach my $rel ($sql->from->relationships()) {
886 0 0         last unless $ok;
887 0           my @tables = ($rel->local_sql_table(), $rel->remote_sql_table());
888              
889 0           foreach my $table (@tables) {
890 0 0         next unless $ok;
891 0 0         next unless $table;
892             # May seem odd, but it's actually OK for a relationship to be
893             # present while missing the local or remote table IFF the relationship has a join depth > 1
894             # (relied on by HasManyMany->fetch_all())
895 0 0 0       next if ($rel->join_depth > 1 && !grep { $_->is_the_same_table($table) } $sql->tables(exclude_subqueries => 1));
  0            
896              
897 0           ($check, $exception) = __is_inflatable_EOCPAR_columns_present_for_table($table, \%cache);
898 0   0       $ok &&= $check;
899             }
900             }
901             }
902              
903 0 0         return (($ok ? 1 : 0), $exception);
904             }
905              
906             sub __is_inflatable_EOCPAR_column_name {
907 0     0     my $oc = shift;
908 0 0         if ($oc->expression->is_column()) {
909 0           my $col = $oc->expression();
910 0 0         if ($col->table) {
911 0 0         if ($col->table->schema) {
912 0           return $col->table->schema . '.' . $col->table->table . '.' . $col->column;
913             } else {
914 0           return '(unknown schema).' . $col->table->table . '.' . $col->column;
915             }
916              
917             } else {
918 0           return '(unknown table).' . $col->column();
919             }
920             } else {
921 0           return '(expression)';
922             }
923             }
924              
925              
926             sub __is_inflatable_EOCPAR_columns_present_for_table {
927 0     0     my $table = shift;
928 0           my $column_lookup = shift;
929 0           my $sql = shift;
930              
931 0           my $ok = 1;
932 0           my $check = 1;
933 0           my $exception = undef;
934              
935 0           my $class = $table->class();
936 0           foreach my $ec ($class->essential_sql_columns($table)) {
937 0 0         last unless $ok;
938 0           my $eoc = OutputColumn->new(expression => $ec);
939 0           $check = exists $column_lookup->{__is_inflatable_EOCPAR_column_name($eoc)};
940 0 0         unless ($check) {
941 0           $exception = Class::ReluctantORM::Exception::SQL::NotInflatable::MissingColumn->new
942             (
943             table => $table->schema . '.' . $table->table(),
944             column => $ec->column,
945             sql => $sql,
946             );
947             }
948 0   0       $ok &&= $check;
949             }
950 0           return ($ok, $exception);
951             }
952              
953             sub __is_inflatable_all_non_join_tables_are_in_relationships {
954 0     0     my $sql = shift;
955              
956 0           my @non_join_tables =
957 0           grep { ! Class::ReluctantORM->_is_join_table(table_obj => $_) }
958             $sql->tables(exclude_subqueries => 1);
959              
960             # We're OK if it's just the base table left
961 0 0 0       if (@non_join_tables == 1 && $non_join_tables[0]->is_the_same_table($sql->base_table)) {
962 0           return (1, undef);
963             }
964              
965 0 0         unless ($sql->from) {
966             # WTF - has multiple tables, but no FROM clause?
967 0           return (0, Class::ReluctantORM::Exception::SQL::NotInflatable->new(sql => $sql, error => "Multiple tables, but no from clause... confused am I!"));
968             }
969              
970 0           my @rels = $sql->from->relationships();
971             TABLE:
972 0           foreach my $table (@non_join_tables) {
973 0           foreach my $rel (@rels) {
974 0           foreach my $end (qw(local_sql_table remote_sql_table)) {
975 0           my $rel_table = $rel->$end();
976 0 0 0       if ($rel_table && $table->is_the_same_table($rel_table)) {
977 0           next TABLE;
978             }
979             }
980             }
981             # Been through all the relations and didn't a rel for this table
982 0           return (0, Class::ReluctantORM::Exception::SQL::NotInflatable::ExtraTable->new(sql => $sql, error => "A table is neither an intermediate join table, nor does it appear at either end of any relationships", table => $table));
983             }
984            
985             # All tables check out....
986 0           return (1, undef);
987             }
988              
989             sub __is_inflatable_all_joins_have_relationships {
990 0     0     my $sql = shift;
991 0 0         unless ($sql->from) { return (1, undef); }
  0            
992 0           my @joins = $sql->from->joins();
993 0           foreach my $j (@joins) {
994 0 0         unless ($j->relationship()) {
995 0           return (0, Class::ReluctantORM::Exception::SQL::NotInflatable::VagueJoin->new(sql => $sql, error => "A join does not have a Relationship associated with it", join => $j));
996             }
997             }
998              
999             # A-OK
1000 0           return (1, undef);
1001             }
1002              
1003             =head2 $sql->make_inflatable(%opts);
1004              
1005             Performs various actions to increase the inflatability of the SQL object. Calls annotate and reconcile. If any exceptions are thrown, they are passed on.
1006              
1007             Compare to is_inflatable, which optionally calls make_inflatable but captures any exception.
1008              
1009             Currently supported options:
1010              
1011             =over
1012              
1013             =item auto_annotate
1014              
1015             Optional boolean, default true. If true, call annotate() before performing the analysis. If false, you are saying that you have already attached any model metadata.
1016              
1017             =item auto_reconcile
1018              
1019             Optional boolean, default true. If true, call reconcile() before performing the analysis.
1020              
1021             =item add_output_columns
1022              
1023             Optional boolean, default true. If auto_reconcile is true, output columns will be added to the query to ensure that all essential (non-lazy) columns are present in the query. If auto_reconcile is false, has no effect.
1024              
1025             =back
1026              
1027             =cut
1028              
1029             sub make_inflatable {
1030 0     0 1   my $sql = shift;
1031 0           my %args = check_args(args => \@_, optional => [qw(auto_annotate auto_reconcile add_output_columns)]);
1032 0 0         unless (defined($args{auto_annotate})) { $args{auto_annotate} = 1; }
  0            
1033 0 0         unless (defined($args{auto_reconcile})) { $args{auto_reconcile} = 1; }
  0            
1034 0 0         unless (defined($args{add_output_columns})) { $args{add_output_columns} = 1; }
  0            
1035              
1036 0 0         if ($args{auto_annotate}) {
1037 0           $sql->annotate();
1038             }
1039 0 0         if ($args{auto_reconcile}) {
1040 0           $sql->reconcile(add_output_columns => $args{add_output_columns});
1041             }
1042             }
1043              
1044             =head2 $sql->annotate();
1045              
1046             Scans the SQL tree and attaches Tables and Relationships where they can be determined.
1047              
1048             =cut
1049              
1050             sub annotate {
1051 0     0 1   my $sql = shift;
1052              
1053             TABLE:
1054 0           foreach my $table ($sql->tables()) {
1055 0 0         if (Class::ReluctantORM->_is_join_table(table_obj => $table)) {
    0          
1056 0           my $jst = Class::ReluctantORM->_find_sql_table_for_join_table(table_obj => $table);
1057 0           $table->_copy_manual_columns($jst);
1058 0           $table->schema($jst->schema());
1059             } elsif (!$table->class()) {
1060 0           my $class = Class::ReluctantORM->_find_class_by_table(table_obj => $table);
1061             # might not be found (alias macro, for example)
1062             # alias macros will get resolved during reconciliation anyway
1063 0 0         if ($class) {
1064 0           $table->class($class);
1065             }
1066             }
1067             }
1068              
1069             # Hunt for relationships in the joins
1070 0 0         if ($sql->from) {
1071 0           $sql->__annotate_find_relationships();
1072             }
1073              
1074              
1075             # Anything else?
1076              
1077             }
1078              
1079             sub __annotate_find_relationships {
1080 0     0     my $sql = shift;
1081 0           __annotate_FR_recursor($sql, $sql->from->root_relation());
1082             }
1083              
1084             sub __annotate_FR_recursor {
1085 0     0     my $sql = shift;
1086 0           my $rel = shift;
1087 0 0         unless ($rel->is_join) { return; }
  0            
1088 0           my $join = $rel;
1089              
1090 0           my ($right_rel, $left_rel) = ($join->right_relation(), $join->left_relation());
1091              
1092             # Maybe it's already set?
1093 0 0         if ($join->relationship()) {
1094             # Just recurse and return
1095 0           __annotate_FR_recursor($sql, $left_rel);
1096 0           __annotate_FR_recursor($sql, $right_rel);
1097 0           return;
1098             }
1099              
1100             # Find the leftmost table on the each side
1101 0           my $left_table = $left_rel->leftmost_table();
1102 0           my $right_table = $right_rel->leftmost_table();
1103 0           my @candidates;
1104              
1105             # Look for a relationship in which the local table of the relationship is the left table
1106             # and the right table is either the remote table or the join table
1107 0 0         if (@candidates == 0) {
1108 0           @candidates = Class::ReluctantORM->_find_relationships_by_local_table(table_obj => $left_table);
1109 0 0 0       @candidates =
      0        
1110             grep {
1111 0           ($_->remote_sql_table && $right_table->is_the_same_table($_->remote_sql_table, 0)) ||
1112             ($_->join_sql_table && $right_table->is_the_same_table($_->join_sql_table, 0))
1113             }
1114             @candidates;
1115             }
1116              
1117             # Look for a relationship in which the local table of the relationship is the right table
1118             # and the left table is either the remote table or the join table
1119 0 0         if (@candidates == 0) {
1120 0           @candidates = Class::ReluctantORM->_find_relationships_by_local_table(table_obj => $right_table);
1121 0 0 0       @candidates =
      0        
1122             grep {
1123 0           ($_->remote_sql_table && $left_table->is_the_same_table($_->remote_sql_table, 0)) ||
1124             ($_->join_sql_table && $left_table->is_the_same_table($_->join_sql_table, 0))
1125             }
1126             @candidates;
1127             }
1128              
1129             # Try desperate measures?
1130 0           if (1) {
1131              
1132             # Look for a relationship in which the remote table of the relationship is the left table
1133             # and the right table is either the local table or the join table
1134 0 0         if (@candidates == 0) {
1135 0           @candidates = Class::ReluctantORM->_find_relationships_by_remote_table(table_obj => $left_table);
1136 0 0 0       @candidates =
      0        
1137             grep {
1138 0           ($_->remote_sql_table && $right_table->is_the_same_table($_->local_sql_table, 0)) ||
1139             ($_->join_sql_table && $right_table->is_the_same_table($_->join_sql_table, 0))
1140             }
1141             @candidates;
1142             }
1143              
1144             # Look for a relationship in which the remote table of the relationship is the right table
1145             # and the left table is either the local table or the join table
1146 0 0         if (@candidates == 0) {
1147 0           @candidates = Class::ReluctantORM->_find_relationships_by_remote_table(table_obj => $right_table);
1148 0 0 0       @candidates =
      0        
1149             grep {
1150 0           ($_->remote_sql_table && $left_table->is_the_same_table($_->local_sql_table, 0)) ||
1151             ($_->join_sql_table && $left_table->is_the_same_table($_->join_sql_table, 0))
1152             }
1153             @candidates;
1154             }
1155             }
1156              
1157              
1158             # The candidate relationships must have a criterion that is equivalent to the one on the join
1159 0           @candidates = grep { $_->matches_join_criterion($join->criterion()) } @candidates;
  0            
1160 0           my %unique_candidates = map { $_->method_name => $_ } @candidates;
  0            
1161 0           @candidates = values %unique_candidates;
1162              
1163 0 0         if (@candidates == 0) {
    0          
1164 0           Class::ReluctantORM::Exception::SQL::NotInflatable::VagueJoin->croak(join => $join, error => "Could not find any relationships that matched the tables on the ends of this Join", sql => $sql);
1165             } elsif (@candidates > 1) {
1166 0           Class::ReluctantORM::Exception::SQL::NotInflatable::VagueJoin->croak(join => $join, error => "Could not find a unique relationship that matched the tables on the ends of this Join", sql => $sql);
1167             } else {
1168             # Yay, exactly one relationship matched
1169 0           $join->relationship($candidates[0]);
1170             }
1171              
1172             # Recurse
1173 0           __annotate_FR_recursor($sql, $left_rel);
1174 0           __annotate_FR_recursor($sql, $right_rel);
1175              
1176             }
1177              
1178             =head2 @objects = $sql->inflate();
1179              
1180             Executes the given query, and builds Class::ReluctantORM model objects directly from the results.
1181              
1182             This does not call is_inflatable() or make_inflatable() for you. See those methods to increase your chances of success.
1183              
1184             =cut
1185              
1186             sub inflate {
1187 0     0 1   my $sql = shift;
1188 0           my @results = fd_inflate($sql); # yipes
1189 0           return @results;
1190             }
1191              
1192              
1193              
1194              
1195             #========================================================#
1196             # Column Disambiguation
1197             #========================================================#
1198              
1199             =begin devdocs
1200              
1201             =head2 $sql->set_reconcile_option(option => $value);
1202              
1203             This might go public one day, but for now it's best left to those who read the source.
1204              
1205             You can use this to set reconciliation options. Read reconcile() to see what they do.
1206              
1207             =cut
1208              
1209             sub set_reconcile_option {
1210 0     0 1   my $sql = shift;
1211 0           my %opts = @_;
1212 0           foreach my $opt (keys %opts) {
1213 0           $sql->{reconcile_options}{$opt} = $opts{$opt};
1214             }
1215             }
1216              
1217              
1218             =head2 $sql->reconcile();
1219              
1220             Prepares the SQL object for rendering. This includes:
1221              
1222             =over
1223              
1224             =item ensure output columns are generated
1225              
1226             =item disambiguate column references in the WHERE and ORDER BY clauses
1227              
1228             =back
1229              
1230             There is no harm in calling this method multiple times. This method will
1231             throw exceptions if it encounters irreconcilable ambiguities.
1232              
1233             =cut
1234              
1235             sub reconcile {
1236 0     0 1   my $sql = shift;
1237 0           my %args = check_args(args => \@_, optional => [qw(add_output_columns realias_raw_sql)]);
1238              
1239 0           my %instance_options = %{$sql->{reconcile_options}};
  0            
1240 0           my %options = (%instance_options, %args);
1241              
1242 0           $sql->__reconcile_in_subqueries();
1243              
1244 0           $sql->__build_reconciliation_cache();
1245 0           $sql->__disambiguate_columns_in_from();
1246 0           $sql->__set_default_table_aliases();
1247 0           $sql->__build_reconciliation_cache(); # Rebuild needed after setting defualt table aliases
1248              
1249 0           $sql->__resolve_alias_macros();
1250 0           $sql->__disambiguate_columns_in_where();
1251              
1252 0 0         if ($options{add_output_columns}) {
1253 0           $sql->__add_output_columns();
1254             }
1255 0           $sql->__disambiguate_columns_in_output();
1256 0           $sql->__disambiguate_columns_in_order_by();
1257              
1258 0           delete $sql->{_rc};
1259 0           return 1;
1260              
1261             }
1262              
1263              
1264             sub __add_output_columns {
1265 0     0     my $sql = shift;
1266              
1267 0 0         if ($sql->operation eq 'DELETE') { return; }
  0            
1268              
1269             # Add base columns for tables
1270 0           foreach my $table ($sql->tables) {
1271 0 0         if ($table->class) {
1272 0           foreach my $col ($table->class->essential_sql_columns($table)) {
1273 0           $sql->add_output($col);
1274             }
1275             }
1276             }
1277              
1278             # Add extra columns for relations
1279 0 0         if ($sql->from) {
1280 0           foreach my $relship ($sql->from->relationships) {
1281 0           foreach my $col ($relship->additional_output_sql_columns) {
1282 0           $sql->add_output($col);
1283             }
1284             }
1285             }
1286             }
1287              
1288             sub __build_reconciliation_cache {
1289 0     0     my $self = shift;
1290 0           my @from_tables;
1291 0 0         if ($self->operation eq 'SELECT') {
1292 0 0         @from_tables = $self->from ? $self->from->tables() : ();
1293             } else {
1294 0 0         @from_tables = $self->table ? ($self->table()) : ();
1295             }
1296 0           my %tables_by_alias = map { $_->alias => $_ } grep { defined($_->alias) } @from_tables;
  0            
  0            
1297 0           my %tables_by_mem = map { ('' . $_ . '') => $_ } @from_tables;
  0            
1298 0           my %tables_by_schema = map { ($_->schema . '.' . $_->table) => $_ } grep { defined($_->schema) } @from_tables;
  0            
  0            
1299 0           my %tables_by_name;
1300 0           foreach my $table (@from_tables) {
1301 0   0       $tables_by_name{$table->table} ||= [];
1302 0           push @{$tables_by_name{$table->table}}, $table;
  0            
1303             }
1304 0           my %tables_by_column;
1305 0           foreach my $table (grep { $_->knows_any_columns } @from_tables) {
  0            
1306 0           my @col_names = map { lc($_->column) } $table->columns;
  0            
1307 0           foreach my $col_name (@col_names) {
1308 0   0       $tables_by_column{$col_name} ||= [];
1309 0           push @{$tables_by_column{$col_name}}, $table;
  0            
1310             }
1311             }
1312 0           my %tables_by_relation;
1313 0 0         if ($self->operation eq 'SELECT') { # assumes only selects have FROMs
1314 0           foreach my $join ($self->from->joins()) {
1315 0 0         if ($join->relationship()) {
1316 0           my $rel = $join->relationship();
1317 0           my $relname = $rel->method_name();
1318 0           $tables_by_relation{$relname} = {};
1319 0           $tables_by_relation{$relname}{parent} = $join->_find_earliest_table($rel->local_sql_table());
1320 0           $tables_by_relation{$relname}{child} = $join->_find_latest_table($rel->remote_sql_table());
1321 0 0         if ($rel->join_depth > 1) {
1322 0           $tables_by_relation{$relname}{join} = $join->_find_latest_table($rel->join_sql_table());
1323             }
1324             }
1325             }
1326             }
1327              
1328              
1329 0           $self->{_rc} = {
1330             by_alias => \%tables_by_alias,
1331             by_mem => \%tables_by_mem,
1332             by_schema => \%tables_by_schema,
1333             by_name => \%tables_by_name,
1334             by_column => \%tables_by_column,
1335             by_relationship => \%tables_by_relation
1336             };
1337             }
1338              
1339             sub __reconcile_in_subqueries {
1340 0     0     my $sql = shift;
1341              
1342             # Never add output columns to a subquery
1343 0           my %opts = (%{$sql->{reconcile_options}}, add_output_columns => 0);
  0            
1344              
1345             my $reconciler = sub {
1346 0     0     my $thing = shift;
1347 0 0         if ($thing->is_subquery()) {
1348 0           my $st = $thing->statement();
1349 0           $st->reconcile(%opts);
1350             }
1351 0           };
1352              
1353             # Look for subqueries in output columns
1354 0           foreach my $oc ($sql->output_columns) {
1355 0           $oc->expression->walk_leaf_expressions($reconciler);
1356             }
1357              
1358             # Look for subqueries in from
1359 0 0         if ($sql->from) {
    0          
1360 0           $sql->from->root_relation->walk_leaf_relations($reconciler);
1361             } elsif ($sql->table) {
1362 0           $sql->table->walk_leaf_relations($reconciler);
1363             }
1364              
1365             # Look for subqueries in where
1366 0 0         if ($sql->where) {
1367 0           $sql->where->root_criterion->walk_leaf_expressions($reconciler);
1368             }
1369              
1370             # Might have a input subquery (INSERTs only)
1371 0 0         if ($sql->input_subquery) {
1372 0           $sql->input_subquery->statement->reconcile(%opts);
1373             }
1374              
1375             }
1376              
1377             # See 'Alias Macros' in Class/ReluctantORM/Manual/SQL.pod
1378             sub __resolve_alias_macros {
1379 0     0     my $sql = shift;
1380              
1381 0 0         my @cols = (
    0          
1382             ($sql->where ? $sql->where->columns : ()),
1383             ($sql->order_by ? $sql->order_by->columns : ()),
1384             );
1385              
1386 0           foreach my $column (@cols) {
1387 0           my $info = $sql->__looks_like_alias_macro($column);
1388 0 0         next unless $info;
1389 0           my $table;
1390              
1391 0 0         if ($info->{type} eq 'base') {
1392 0           $table = $sql->from->root_relation->leftmost_table();
1393             } else {
1394              
1395             # Find the referred-to relationship
1396 0           my @matching_relations = grep { $_->method_name eq $info->{relname} } $sql->from->relationships();
  0            
1397 0 0         if (@matching_relations != 1) {
1398 0           Class::ReluctantORM::Exception::SQL::AmbiguousReference->croak
1399             ("Must have exactly one reference to the relationship '$info->{relname}' to use a alias macro");
1400             }
1401 0           my $relationship = $matching_relations[0];
1402              
1403             # OK, find the JOIN that uses that relationship...
1404 0 0         my ($join) =
1405 0           grep { $_->relationship && $_->relationship->method_name eq $relationship->method_name }
1406             $sql->from->joins();
1407              
1408 0 0         if ($info->{type} eq 'parent') {
    0          
    0          
1409             # Hunt down the left-branch of the JOIN, looking for the LINKING table
1410 0           my $seek = Table->new($relationship->linking_class());
1411 0           $table = $join->_find_earliest_table($seek);
1412             } elsif ($info->{type} eq 'child') {
1413             # Hunt down the right-branch of the JOIN, looking for the LINKED table
1414 0           my $seek = Table->new($relationship->linked_class());
1415 0           $table = $join->_find_latest_table($seek);
1416             } elsif ($info->{type} eq 'join') {
1417 0           my $seek = $relationship->join_sql_table();
1418 0           $table = $join->_find_latest_table($seek);
1419             } else {
1420 0           Class::ReluctantORM::Exception::NotImplemented->croak
1421             ("Don't know how to handle an alias macro of type '$info->{type}'");
1422             }
1423             }
1424              
1425 0 0         unless ($table) {
1426 0           Class::ReluctantORM::Exception::SQL::TooComplex->croak
1427             ("Unable to resolve alias macro '" . $column->table->table . "' -- try simplifying?");
1428             }
1429              
1430             # Finally
1431 0           $column->table($table);
1432              
1433             }
1434             }
1435              
1436             my @ALIAS_MACRO_PATTERNS = (
1437             # Make these case-insensitive, since SQL::Statement will uppercase them
1438             qr(MACRO__(base)__)i,
1439             qr(MACRO__(parent)__(.+)__)i,
1440             qr(MACRO__(child)__(.+)__)i,
1441             qr(MACRO__(join)__(.+)__)i,
1442             );
1443              
1444             sub __looks_like_alias_macro {
1445 0     0     my $sql = shift;
1446 0           my $column = shift;
1447 0           my $table = $column->table();
1448 0 0         unless ($table) { return undef; }
  0            
1449 0           my $name = $table->table(); # Don't use alias here - it may have been set by __set_default_table_aliases();
1450 0 0         unless ($name) { return undef; }
  0            
1451 0           foreach my $pat (@ALIAS_MACRO_PATTERNS) {
1452 0           my ($type, $relname) = $name =~ $pat;
1453 0 0         if ($type) {
1454 0           $type = lc($type);
1455             # Find the relationship
1456 0           my $lcrelname = '';
1457 0 0         unless ($type eq 'base') {
1458 0 0         if ($sql->from) {
1459 0           ($lcrelname) = grep { lc($relname) eq lc($_) } map { $_->method_name } $sql->from->relationships();
  0            
  0            
1460 0 0         unless ($lcrelname) {
1461 0           Class::ReluctantORM::Exception::SQL::AmbiguousReference->croak
1462             ("Could not resolve alias macro '$name' - no relationship with name '$relname' (looked case insensitively)");
1463             }
1464 0           $relname = $lcrelname;
1465             }
1466             }
1467 0           return { type => $type, relname => $relname };
1468             }
1469             }
1470 0           return undef;
1471             }
1472              
1473              
1474             sub __disambiguate_columns_in_where {
1475 0     0     my $self = shift;
1476 0 0         if ($self->raw_where) {
    0          
1477 0           $self->__raw_where_bind_params();
1478 0 0         if ($self->{reconcile_options}{realias_raw_sql}) {
1479 0           $self->__raw_where_realias(); # sets cooked_where
1480             } else {
1481 0           $self->_cooked_where($self->raw_where());
1482             }
1483             } elsif ($self->where) {
1484 0           foreach my $col ($self->where->columns) {
1485 0           $self->__disambiguate_column($col);
1486             }
1487             }
1488             }
1489              
1490             sub __raw_where_bind_params {
1491 0     0     my $sql = shift;
1492              
1493             # This is kinda dumb - at this point, we're reconciling,
1494             # and further changes to the SQL are not permitted. So if
1495             # anyone called set_bind_params ALREADY, respect that. But
1496             # if they didn't, notice that and make a last minute bind.
1497 0           my $already_bound = 1;
1498 0   0       for ($sql->_raw_where_params) { $already_bound &&= $_->has_bind_value(); }
  0            
1499 0 0         return if $already_bound;
1500              
1501 0 0         return unless defined($sql->_raw_where_execargs()); # Uhh, should this be an exception?
1502              
1503 0 0         my @ea = @{$sql->_raw_where_execargs() || []};
  0            
1504 0           foreach my $p ($sql->_raw_where_params) {
1505 0           $p->bind_value(shift @ea);
1506             }
1507             }
1508              
1509              
1510             # This one is doing string replacements on a SQL string, not working with objects
1511              
1512             sub __raw_where_realias {
1513 0     0     my $sql = shift;
1514 0           my $raw = $sql->raw_where();
1515 0           my $working = $raw;
1516              
1517             # TODO - this whole method should probably be moved into Driver,
1518             # or else provide a way to set the driver being used
1519 0           my $driver_class = Class::ReluctantORM->default_driver_class();
1520              
1521 0 0         if ($sql->operation eq 'SELECT') {
1522             # Have to work hard - may have multiple source tables, perhaps even same table multiple times
1523             # At this point, from() should be defined, annotated, and reconciled
1524              
1525 0           my %rels_by_name = map { $_->method_name => $_ } $sql->from->relationships();
  0            
1526              
1527             # process alias macros
1528 0           foreach my $amre (@ALIAS_MACRO_PATTERNS) {
1529 0 0         if (my ($type, $relname) = $working =~ $amre) {
1530 0           my $alias;
1531 0 0         if ($type eq 'base') {
    0          
    0          
    0          
1532 0           $alias = $sql->base_table->alias();
1533             } elsif ($type eq 'parent') {
1534 0           $alias = $sql->{_rc}{by_relationship}{$relname}{parent}->alias;
1535             } elsif ($type eq 'child') {
1536 0           $alias = $sql->{_rc}{by_relationship}{$relname}{child}->alias;
1537             } elsif ($type eq 'join') {
1538 0           $alias = $sql->{_rc}{by_relationship}{$relname}{join}->alias;
1539             }
1540 0           $working =~ s{$amre}{$alias}ge;
  0            
1541             }
1542             }
1543              
1544             # Now loop over the known tables in the query
1545             # and look for anything that might be refering to that table
1546              
1547 0           my ($oq, $cq, $ns) = ($driver_class->open_quote, $driver_class->close_quote, $driver_class->name_separator);
1548 0           foreach my $t ($sql->from->tables()) {
1549 0           my $alias = $t->alias() . $ns;
1550              
1551             # "schema_name"."table".
1552 0 0         if ($t->schema) {
1553 0           my $re1 = '(' . $oq . $t->schema . $cq . '\\' . $ns . $oq . $t->table . $cq . '\\' . $ns . ')';
1554 0           $working =~ s/$re1/$alias/g;
1555 0 0         if ($DEBUG > 2) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - alias sub pass one:\nre:\t$re1\nadjusted where:\t$working\n"; }
  0            
1556             }
1557              
1558             # schema.table.
1559 0 0         if ($t->schema) {
1560 0           my $re2 = '(' . $t->schema . '\\' . $ns . $t->table . '\\' . $ns . ')';
1561 0           $working =~ s/$re2/$alias/g;
1562 0 0         if ($DEBUG > 2) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - alias sub pass two:\nre:\t$re2\nadjusted where:\t$working\n"; }
  0            
1563             }
1564              
1565             # "table".
1566 0           my $re3 = '(' . $oq . $t->table . $cq . '\\' . $ns . ')';
1567 0           $working =~ s/$re3/$alias/g;
1568 0 0         if ($DEBUG > 2) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - alias sub pass 3:\nre:\t$re3\nadjusted where:\t$working\n"; }
  0            
1569              
1570             # table.
1571 0           my $re4 = '(' . $t->table . '\\' . $ns . ')';
1572 0           $working =~ s/$re4/$alias/g;
1573 0 0         if ($DEBUG > 2) { print STDERR __PACKAGE__ . ':' . __LINE__ . " - alias sub pass 4:\nre:\t$re4\nadjusted where:\t$working\n"; }
  0            
1574              
1575             }
1576             # OK, at this point, $working is as good as we can practically make it. It may still have
1577             # ambiguous table or column references, but if so, the user should use the alias macro facility.
1578              
1579             } else {
1580             # Assume we don't support FROM (or USING) with UPDATE, INSERT, or DELETE
1581             # so we only have one source table. Nothing to do.
1582             }
1583              
1584              
1585 0           $sql->_cooked_where($working);
1586              
1587             }
1588              
1589              
1590              
1591             sub __disambiguate_columns_in_output {
1592 0     0     my $sql = shift;
1593              
1594             # Collect all Columns, even those buried in Expressions
1595 0           my @columns;
1596             my $walker = sub {
1597 0     0     my $expr = shift;
1598 0 0         if ($expr->is_column) {
1599 0           push @columns, $expr;
1600             }
1601 0           };
1602 0           foreach my $expr (map { $_->expression } $sql->output_columns()) {
  0            
1603 0           $expr->walk_leaf_expressions($walker);
1604             }
1605              
1606             # Disambiguate the columns
1607 0           foreach my $col (@columns) {
1608 0           $sql->__disambiguate_column($col);
1609             }
1610              
1611             # At this point, each column knows which table it goes with, but it
1612             # may not have an alias, and it may be a duplicate.
1613              
1614 0           my @all_output_columns = $sql->output_columns;
1615 0           my @simple_outputs = grep { $_->expression->is_column } @all_output_columns;
  0            
1616 0           my @expression_outputs = grep { !$_->expression->is_column } @all_output_columns;
  0            
1617              
1618             # Even after uniqueification, we need to preserve order.
1619 0           my %oc_order;
1620 0           for (0..$#all_output_columns) { $oc_order{$all_output_columns[$_]} = $_; }
  0            
1621              
1622             # Filter out duplicates among the simple
1623             # column outputs, using tablealias.columnname as the key
1624 0           my %unique_simple_cols =
1625 0           map { ($_->expression->table->alias . '.' . $_->expression->column) => $_ }
1626             @simple_outputs;
1627              
1628             # We don't try to uniqueify any expression columns
1629 0           my @unique_all_cols = (values(%unique_simple_cols), @expression_outputs);
1630              
1631             # OK, put them back in original order
1632 0           my @ordered_unique_cols =
1633 0           sort { $oc_order{$a} <=> $oc_order{$b} }
1634             @unique_all_cols;
1635              
1636 0           $sql->{outputs} = \@ordered_unique_cols;
1637              
1638             # Now set column aliases
1639 0           $sql->__set_default_column_aliases();
1640              
1641             }
1642              
1643             sub __disambiguate_columns_in_order_by {
1644 0     0     my $self = shift;
1645 0 0         return unless $self->order_by;
1646 0           foreach my $col ($self->order_by->columns) {
1647 0           $self->__disambiguate_column($col);
1648             }
1649             }
1650              
1651             sub __disambiguate_columns_in_from {
1652 0     0     my $sql = shift;
1653 0 0         return unless $sql->from;
1654              
1655             # Look for criteria and resolve their columns
1656             my $walker = sub {
1657 0     0     my $expr = shift;
1658 0 0         if ($expr->is_column()) {
1659 0           $sql->__disambiguate_column($expr);
1660             }
1661 0           };
1662 0           foreach my $join ($sql->from->joins()) {
1663 0           $join->criterion->walk_leaf_expressions($walker);
1664             }
1665              
1666              
1667             # This will disambiguate any columns in tables referenced in the from clause
1668 0           foreach my $col ($sql->from->columns) {
1669 0           $sql->__disambiguate_column($col);
1670             }
1671             }
1672              
1673             sub __disambiguate_columns_in_input {
1674 0     0     my $self = shift;
1675 0           foreach my $pair ($self->inputs) {
1676 0           $self->__disambiguate_column($pair->{column});
1677             }
1678             }
1679              
1680              
1681             sub __disambiguate_column {
1682 0     0     my $self = shift;
1683 0           my $col = shift;
1684 0           my $table = $col->table();
1685 0           my %cache = %{$self->{_rc}};
  0            
1686              
1687 0 0         if ($table) {
1688              
1689 0   0       my $alias = $table->alias() || 'no alias';
1690 0 0         if ($DEBUG > 2) { print STDERR __PACKAGE__ . ':' . __LINE__ . "Have table " . $table->table . "($alias) for column " . $col->column . "\n"; }
  0            
1691              
1692             # If we have a table, look it up by memory address first; do nothing if found (already unambiguous)
1693 0 0         if (exists $cache{by_mem}{'' . $table . ''}) { return; }
  0            
1694              
1695             # look up by alias and replace if found
1696 0 0 0       if ($table->alias && exists($cache{by_alias}{$table->alias})) {
1697 0           $col->table($cache{by_alias}{$table->alias});
1698 0           return;
1699             }
1700              
1701             # look up by schema.table and replace if found
1702 0 0 0       if ($table->schema && exists($cache{by_schema}{$table->schema . '.' . $table->table})) {
1703 0           $col->table($cache{by_schema}{$table->schema . '.' . $table->table});
1704 0           return;
1705             }
1706              
1707             # look up by table and replace if found; panic if more than one table with that name
1708 0 0         my @tables_with_that_name = @{$cache{by_name}{$table->table} || []};
  0            
1709 0 0         if (@tables_with_that_name == 1) {
    0          
1710 0           $col->table($tables_with_that_name[0]);
1711             } elsif (@tables_with_that_name == 0) {
1712 0           Class::ReluctantORM::Exception::SQL::AmbiguousReference->croak("The column " . $col->column . " apparently belongs to a table that is not referenced in the query (" . $table->table . ")");
1713             } else {
1714 0 0         Class::ReluctantORM::Exception::SQL::AmbiguousReference->croak("The column " . $col->column . " could not be unambiguously assigned to a table - candidates: " . (join ',', map { ($_->schema ? ($_->schema . '.') : '') . $_->table } @tables_with_that_name));
  0            
1715             }
1716              
1717             } else {
1718             # else no table, so look by column
1719 0 0         my @tables_with_that_column = @{$cache{by_column}{lc($col->column)} || []};
  0            
1720              
1721 0 0         if (@tables_with_that_column == 1) {
    0          
1722 0           $col->table($tables_with_that_column[0]);
1723             } elsif (@tables_with_that_column == 0) {
1724 0           Class::ReluctantORM::Exception::SQL::AmbiguousReference->croak("The column " . $col->column . " has no table specified, and no table in the query could be found that has that column.");
1725             } else {
1726 0 0         Class::ReluctantORM::Exception::SQL::AmbiguousReference->croak("The column " . $col->column . " could not be unambiguously assigned to a table by column name - candidates: " . (join ',', map { ($_->schema ? ($_->schema . '.') : '') . $_->table . ($_->alias ? ' (' . $_->alias . ')' : '') } @tables_with_that_column));
  0 0          
1727             }
1728             }
1729             }
1730              
1731             #=======================================================#
1732             # DIRECT EXECUTION
1733             #=======================================================#
1734              
1735             =head1 DIRECT EXECUTION
1736              
1737             These low-level methods allow you to use DBI-style prepare/execute/fetch cycles on SQL objects.
1738              
1739             Use $driver->prepare($sql) to start this process.
1740              
1741             =cut
1742              
1743             =head2 $bool = $sql->is_prepared();
1744              
1745             Returns true if the SQL object has been prepared using $driver->prepare().
1746              
1747             =cut
1748              
1749             sub is_prepared {
1750 0     0 1   my $sql = shift;
1751 0           return defined ($sql->_sth());
1752             }
1753              
1754             =head2 $sql->execute();
1755              
1756             =head2 $sql->execute(@bind_values);
1757              
1758             In the first form, executes the statement using the existing values bound to the Params (if any).
1759              
1760             In the second form, binds the given values to the parameters in the SQL object, and executes the statement handle.
1761              
1762             is_prepared() must return true for this to work. If anything goes wrong (including database errors) an exception will be thrown.
1763              
1764             =cut
1765              
1766             __PACKAGE__->mk_accessors(qw(execute_hints));
1767              
1768             sub execute {
1769 0     0 1   my $sql = shift;
1770 0 0         unless ($sql->is_prepared()) {
1771 0           Class::ReluctantORM::Exception::SQL::ExecuteWithoutPrepare->croak();
1772             }
1773              
1774             # If binds were provided, set them
1775 0 0         if (@_) {
1776 0           $sql->set_bind_values(@_);
1777             }
1778              
1779 0           my %monitor_args = $sql->__monitor_args();
1780 0           my $driver = $sql->_execution_driver();
1781              
1782 0           $driver->_monitor_execute_begin(%monitor_args);
1783 0           $driver->_pre_execute_hook($sql);
1784 0           $sql->_sth->execute($sql->get_bind_values());
1785 0           $driver->_post_execute_hook($sql);
1786 0           $driver->_monitor_execute_finish(%monitor_args);
1787              
1788 0           return;
1789             }
1790              
1791             sub __monitor_args {
1792 0     0     my $sql = shift;
1793             return (
1794 0           sql_obj => $sql,
1795             sql_str => $sql->_sql_string,
1796             binds => [ $sql->get_bind_values() ],
1797             sth => $sql->_sth(),
1798             );
1799             }
1800              
1801             =head2 $sql->fetchrow();
1802              
1803             Fetches one row from the statment handle. The fetched values are bound to the Output Columns of the SQL object - access them using $sql->output_columns.
1804              
1805             =cut
1806              
1807             sub fetchrow {
1808 0     0 1   my $sql = shift;
1809              
1810 0 0         unless ($sql->is_prepared()) {
1811 0           Class::ReluctantORM::Exception::SQL::ExecuteWithoutPrepare->croak();
1812             }
1813              
1814 0           my $row = $sql->_sth->fetchrow_hashref();
1815 0           $sql->_execution_driver->_monitor_fetch_row($sql->__monitor_args(), row => $row);
1816 0           $sql->set_single_row_results($row);
1817              
1818 0           return $row;
1819             }
1820              
1821             =head2 $sql->fetch_all();
1822              
1823             Fetches all rows from the statement handle, and calls your callback after fetching each row (see $sql->add_fetchrow_listener()).
1824              
1825             =cut
1826              
1827             sub fetch_all {
1828 0     0 1   my $sql = shift;
1829              
1830 0 0         unless ($sql->is_prepared()) {
1831 0           Class::ReluctantORM::Exception::SQL::ExecuteWithoutPrepare->croak();
1832             }
1833              
1834 0           my %monitor_args = $sql->__monitor_args();
1835 0           while (my $row = $sql->_sth->fetchrow_hashref()) {
1836 0           $sql->_execution_driver->_monitor_fetch_row(%monitor_args, row => $row);
1837 0           $sql->set_single_row_results($row);
1838             }
1839              
1840 0           return;
1841             }
1842              
1843              
1844             =head2 $sql->finish();
1845              
1846             Releases the statement handle. is_prepared() must be true for this to work.
1847              
1848             =cut
1849              
1850             sub finish {
1851 0     0 1   my $sql = shift;
1852 0 0         unless ($sql->is_prepared()) {
1853 0           Class::ReluctantORM::Exception::SQL::FinishWithoutPrepare->croak();
1854             }
1855              
1856 0           $sql->_sth->finish();
1857 0           $sql->_execution_driver->_monitor_finish($sql->__monitor_args());
1858              
1859 0           $sql->_sth(undef);
1860 0           $sql->_sql_string(undef);
1861 0           $sql->_execution_driver(undef);
1862              
1863 0           return;
1864             }
1865              
1866              
1867             #=======================================================#
1868             # Results Fetching
1869             #=======================================================#
1870              
1871             =head1 FETCHING RESULTS
1872              
1873             =cut
1874              
1875             =head2 $bool = $sql->has_results();
1876              
1877             Returns true if the SQL object has been executed and has at least one row of results.
1878              
1879             =cut
1880              
1881             __PACKAGE__->mk_accessors(qw(has_results));
1882              
1883             =head2 $sql->add_fetchrow_listener($coderef);
1884              
1885             Adds a coderef that will be called with the SQL object as the only argument immediately after a row is fetched. You may then obtain results from the $sql->output_columns, calling output_value on each.
1886              
1887             =cut
1888              
1889             sub add_fetchrow_listener {
1890 0     0 1   my $self = shift;
1891 0           my $coderef = shift;
1892 0 0         unless (ref($coderef) eq 'CODE') {
1893 0           Class::ReluctantORM::Exception::Param::WrongType->croak(expected => 'CODEREF', param => 'code');
1894             }
1895 0           push @{$self->{fetchrow_listeners}}, $coderef;
  0            
1896             }
1897              
1898              
1899             =head2 $sql->clear_fetchrow_listeners();
1900              
1901             Clears the list of listeners.
1902              
1903             =cut
1904              
1905             sub clear_fetchrow_listeners {
1906 0     0 1   my $self = shift;
1907 0           $self->{fetchrow_listeners} = [];
1908             }
1909              
1910             sub _notify_fetchrow_listeners {
1911 0     0     my $self = shift;
1912 0           foreach my $coderef (@{$self->{fetchrow_listeners}}) {
  0            
1913 0           $coderef->($self);
1914             }
1915             }
1916              
1917             sub set_single_row_results {
1918 0     0 0   my $sql = shift;
1919 0           my $row = shift;
1920 0 0         if ($row) {
1921 0           foreach my $col ($sql->output_columns) {
1922 0           $col->output_value($row->{$col->alias});
1923             }
1924 0           $sql->has_results(1);
1925 0           $sql->_notify_fetchrow_listeners();
1926             } else {
1927 0           $sql->has_results(0);
1928             }
1929             }
1930              
1931              
1932             #=================================================================#
1933             # MISC METHODS
1934             #=================================================================#
1935              
1936              
1937             =head1 MISC METHODS
1938              
1939             =cut
1940              
1941             =head2 $str = $sql->pretty_print();
1942              
1943             Returns a human-readable string representation of the query. Not appropriate for use for feeding to a prepare() statement.
1944              
1945             =cut
1946              
1947             sub pretty_print {
1948 0     0 1   my $self = shift;
1949 0           my %args = @_;
1950 0           my $op = $self->operation;
1951 0   0       my $prefix = $args{prefix} || '';
1952 0           my $str = $prefix . "$op\n";
1953 0           $prefix .= ' ';
1954 0 0         if ($op ne 'DELETE') {
1955 0           $str .= $prefix . "OUTPUT Columns:\n";
1956 0           foreach my $oc ($self->output_columns) {
1957 0           $str .= $prefix . ' ' . $oc->pretty_print(one_line => 1) . "\n";
1958             }
1959             }
1960 0 0         if ($op ne 'SELECT') {
1961 0           $str .= $prefix . 'TABLE: ' . $self->table->pretty_print(one_line => 1) . "\n";
1962             } else {
1963 0           $str .= $self->from->pretty_print(prefix => $prefix);
1964             }
1965              
1966 0 0 0       if (($op eq 'INSERT') || ($op eq 'UPDATE')) {
1967 0           $str .= $self->__pretty_print_inputs(prefix => $prefix);
1968             }
1969 0 0 0       if ($op eq 'INSERT' && $self->input_subquery()) {
1970 0           $str .= $prefix . "INPUT SUBQUERY:\n";
1971 0           $str .= $self->input_subquery->statement->pretty_print(prefix => $prefix . ' ');
1972             }
1973 0 0         if ($op ne 'INSERT') {
1974 0 0         if ($self->_cooked_where) {
    0          
    0          
1975 0           $str .= 'WHERE[cooked] ' . $self->_cooked_where() . "\n";
1976             } elsif ($self->raw_where) {
1977 0           $str .= 'WHERE[raw] ' . $self->raw_where() . "\n";
1978             } elsif ($self->where) {
1979 0           $str .= $self->where->pretty_print(prefix => $prefix);
1980             }
1981             }
1982 0 0         if ($self->order_by) {
1983 0           $str .= $self->order_by->pretty_print(prefix => $prefix);
1984             }
1985 0 0         if (defined $self->limit) {
1986 0           $str .= $prefix . 'LIMIT ' . $self->limit;
1987 0 0         if (defined $self->offset) {
1988 0           $str .= 'OFFSET ' . $self->offset;
1989             }
1990             }
1991              
1992 0           return $str;
1993             }
1994             sub __pretty_print_inputs {
1995 0     0     my $self = shift;
1996 0           my %args = @_;
1997 0   0       my $prefix = $args{prefix} || '';
1998 0           my $str = $prefix . "INPUTS:\n";
1999 0           foreach my $i ($self->inputs) {
2000 0           $str .= $prefix . ' ';
2001 0           $str .= $i->{column}->pretty_print(one_line => 1);
2002 0 0         if ($i->{param}) {
2003 0           $str .= ' = ';
2004 0           $str .= $i->{param}->pretty_print(one_line =>1);
2005 0           $str .= "\n";
2006             }
2007             }
2008 0           return $str;
2009             }
2010              
2011              
2012             =head2 $sql->set_default_output_aliases();
2013              
2014             Ensures that each table and output column has
2015             an alias. If a table or column already has
2016             an alias, it is left alone.
2017              
2018             =cut
2019              
2020             sub set_default_output_aliases {
2021 0     0 1   my $self = shift;
2022              
2023 0           $self->__set_default_table_aliases();
2024 0           $self->__set_default_column_aliases();
2025             }
2026              
2027             sub __set_default_column_aliases {
2028 0     0     my $self = shift;
2029              
2030             # Make sure each output column has an alias
2031 0           foreach my $oc (grep { !defined($_->alias)} $self->output_columns) {
  0            
2032 0           my $exp = $oc->expression();
2033 0 0         if ($exp->is_column()) {
2034 0           my $col = $oc->expression();
2035 0           $oc->alias($col->table->alias() . '_' . $col->column);
2036             } else {
2037             # Make something up
2038 0           $oc->alias($self->new_column_alias());
2039             }
2040             }
2041             }
2042              
2043             sub __set_default_table_aliases {
2044 0     0     my $self = shift;
2045 0           my $counter = 0;
2046              
2047 0           my %tables_by_alias = map { $_->alias => $_ } grep { defined($_->alias) } $self->tables;
  0            
  0            
2048              
2049             # Make sure each table has an alias
2050             # Be sure to exclude those whose names look like a alias macro!
2051 0           foreach my $table (grep {!defined($_->alias)} $self->tables) {
  0            
2052 0           my $alias = 'ts' . $counter;
2053 0           while (exists $tables_by_alias{$alias}) {
2054 0           $counter++;
2055 0           $alias = 'ts' . $counter;
2056             }
2057 0           $table->alias($alias);
2058 0           $tables_by_alias{$alias} = $table;
2059             }
2060              
2061             }
2062              
2063              
2064             sub clone {
2065 0     0 0   my $self = shift;
2066 0           my $class = ref $self;
2067              
2068 0           my $other = $class->new($self->operation());
2069              
2070             # Scalars
2071 0 0         if (defined $self->limit) { $other->limit($self->limit()); }
  0            
2072 0 0         if (defined $self->offset) { $other->offset($self->offset()); }
  0            
2073 0 0         if (defined $self->raw_where) {
2074 0           $other->raw_where($self->raw_where());
2075 0 0         if ($self->_cooked_where) { $other->_cooked_where($self->_cooked_where); }
  0            
2076 0 0         if ($self->_raw_where_execargs) { $other->_raw_where_execargs($self->_raw_where_execargs); }
  0            
2077 0 0         if ($self->_raw_where_params) { $other->_raw_where_params([ map { $_->clone() } $self->_raw_where_params ]); }
  0            
  0            
2078             }
2079              
2080             # Single Objects
2081 0 0         if ($self->where) { $other->where( $self->where->clone() ); }
  0            
2082 0 0         if ($self->get('table')) { $other->table( $self->table->clone() ); }
  0            
2083 0 0         if ($self->from) { $other->from( $self->from->clone() ); }
  0            
2084 0 0         if ($self->order_by) { $other->order_by( $self->order_by->clone() ); }
  0            
2085 0 0         if ($self->input_subquery) { $other->input_subquery($self->input_subquery->clone()); }
  0            
2086              
2087             # Lists of things
2088 0           foreach my $input (@{$self->{inputs}}) {
  0            
2089 0           push @{$other->{inputs}},
  0            
2090             {
2091             column => $input->{column}->clone(),
2092             param => $input->{param}->clone(),
2093             };
2094             }
2095 0           foreach my $output ($self->output_columns) {
2096 0           $other->add_output($output->clone());
2097             }
2098              
2099 0           return $other;
2100              
2101             }
2102              
2103              
2104             sub DESTROY {
2105 0     0     my $sql = shift;
2106             # Break links between all objects
2107              
2108 0 0 0       if ($sql->from && $sql->from->root_relation) { $sql->from->root_relation->__break_links(); }
  0            
2109 0 0 0       if ($sql->where && $sql->where->root_criterion) { $sql->where->root_criterion->__break_links(); }
  0            
2110             }
2111              
2112             1;