File Coverage

blib/lib/Class/ReluctantORM/SQL/Column.pm
Criterion Covered Total %
statement 21 87 24.1
branch 0 36 0.0
condition 0 23 0.0
subroutine 7 16 43.7
pod 8 8 100.0
total 36 170 21.1


line stmt bran cond sub pod time code
1             package Class::ReluctantORM::SQL::Column;
2              
3             =head1 NAME
4              
5             Class::ReluctantORM::SQL::Column - Represent a Column in a SQL statement
6              
7             =head1 SYNOPSIS
8              
9             use Class::ReluctantORM::SQL::Aliases;
10              
11             # Make a column
12             my $col1 = Column->new(); # undetermined name
13             my $col2 = Column->new(column => 'my_col'); # undetermined table
14             my $col3 = Column->new(column => 'my_col', table => Table->new()); # fully specified
15              
16             # Use a column in a Where clause criterion ('foo' = ?)
17             my $crit = Criterion->new('=', Column->new(column => 'foo'), Param->new());
18             my $where = Where->new($crit);
19             $sql->where($where);
20             my @cols = $where->columns();
21              
22             # Use a column in an OrderBy clause
23             my $ob = OrderBy->new();
24             $ob->add($col, 'DESC');
25             my @cols = $ob->columns;
26              
27             # Use the column as an output column
28             my $sql = SQL->new(...);
29             $sql->add_output($col);
30              
31              
32             =head1 DESCRIPTION
33              
34             Represents a database column in a SQL statement. Used wehere you need to refer to a column, except for SELECT output columns (Which wraps Column in an OutputColumn to allow for an expression).
35              
36             =cut
37              
38 1     1   5 use strict;
  1         2  
  1         26  
39 1     1   5 use warnings;
  1         1  
  1         20  
40              
41 1     1   5 use Data::Dumper;
  1         2  
  1         47  
42 1     1   5 use Scalar::Util qw(blessed);
  1         1  
  1         41  
43 1     1   5 use Class::ReluctantORM::Exception;
  1         2  
  1         19  
44 1     1   5 use Class::ReluctantORM::Utilities qw(install_method);
  1         2  
  1         45  
45              
46 1     1   4 use base 'Class::ReluctantORM::SQL::Expression';
  1         1  
  1         571  
47             our $DEBUG = 0;
48              
49              
50             =head1 CONSTRUCTORS
51              
52             =cut
53              
54             =head2 $col = Column->new();
55              
56             =head2 $col = Column->new(column => $column_name, [alias => 'col_alias']);
57              
58             =head2 $col = Column->new(column => $column_name, table => $table, [alias => 'col_alias']);
59              
60             Makes a new Column object.
61              
62             In the first form, the column's identity is undetermined. You must call $col->column() before
63             trying to render the SQL.
64              
65             In the second and third forms, the columns name is provided. Optionally provide an alias
66             for the column, which will be used in output and order_by roles. If the second form is
67             used, attempts will be made to disambiguate the column by looking for matching
68             tables as the SQL statement is built.
69              
70             In the third form, a reference to a Table object is provided, fully determining the column's identity.
71              
72             =cut
73              
74             sub new {
75 0     0 1   my $class = shift;
76 0 0         if (@_ % 2) { Class::ReluctantORM::Exception::Param::ExpectedHash->croak(); }
  0            
77 0           my %args = @_;
78 0           my %expected = map { $_ => 1 } qw(column table alias);
  0            
79 0           my @extra = grep { !exists $expected{$_} } keys %args;
  0            
80 0 0         if (@extra) { Class::ReluctantORM::Exception::Param::Spurious->croak(params => \@extra); }
  0            
81 0 0 0       if ($args{table} && !$args{column}) { Class::ReluctantORM::Exception::Param::Missing->croak("If table is provided, you must also provide column"); }
  0            
82              
83 0           my $self = bless {}, $class;
84 0           $self->table($args{table});
85 0           $self->column($args{column});
86 0           $self->alias($args{alias});
87              
88 0           return $self;
89             }
90              
91              
92             =head1 ACCESSORS AND MUTATORS
93              
94             =cut
95              
96             =head2 $col_alias_name = $col->alias();
97              
98             =head2 $col->alias($col_alias_name);
99              
100             Reads or sets the column alias.
101              
102             =cut
103              
104             __PACKAGE__->mk_accessors(qw(alias));
105              
106             =head2 @empty = $col->child_expressions();
107              
108             Always returns an empty list. Required by the Expression interface.
109              
110             =cut
111              
112 0     0 1   sub child_expressions { return (); }
113              
114             =head2 $col_name = $col->column();
115              
116             =head2 $col->column($col_name);
117              
118             Reads or sets the case-insensitve column name.
119              
120             =cut
121              
122             __PACKAGE__->mk_accessors(qw(column));
123              
124              
125             =head2 $bool = $arg->is_column();
126              
127             All objects of this class return true. The class adds this method to its parent class, making all other subclasses of return false.
128              
129             =cut
130              
131 0     0     install_method('Class::ReluctantORM::SQL::Expression', 'is_column', sub { return 0; });
132 0     0 1   sub is_column { return 1; }
133              
134              
135             =head2 $bool = $col->is_leaf_expression();
136              
137             Always returns true for this class. Required by the Expression interface.
138              
139             =cut
140              
141 0     0 1   sub is_leaf_expression { return 1; }
142              
143             =head2 $value = $col->output_value();
144              
145             =head2 $col->output_value($value);
146              
147             Reads or sets the output value of the column. This only makes sense if the
148             column was used as an output column on a SQL query. An undef should interpreted as NULL.
149              
150             =cut
151              
152             __PACKAGE__->mk_accessors(qw(output_value));
153              
154             =head2 $str = $col->pretty_print();
155              
156             Renders a human-readable representation of the Column.
157              
158             =cut
159              
160             sub pretty_print {
161 0     0 1   my $self = shift;
162 0           my %args = @_;
163 0 0         if ($args{one_line}) {
164 0 0         if ($self->alias) { return $self->alias; }
  0            
165 0 0         if ($self->table) {
166 0           my $t = $self->table;
167 0 0         if ($t->alias) { return $t->alias . '.' . $self->column; }
  0            
168 0 0         if ($t->schema) { return $t->schema . '.' . $t->table . '.' . $self->column; }
  0            
169 0           return $t->table . '.' . $self->column;
170             }
171 0           return $self->column;
172             } else {
173 0   0       return ($args{prefix} || '' ) . 'COLUMN ' . $self->pretty_print(one_line => 1) . "\n";
174             }
175             }
176              
177              
178             =head2 $table = $col->table();
179              
180             =head2 $col->table($table);
181              
182             Reads or sets the Table object that the Column belongs to. On set, the table is checked to
183             confirm that the column named is indeed a column of that table.
184              
185             =cut
186              
187             sub table {
188 0     0 1   my $self = shift;
189 0 0         if (@_) {
190 0           my $table = shift;
191 0 0         if ($table) {
192 0 0 0       unless (blessed($table) && $table->isa('Class::ReluctantORM::SQL::Table')) {
193 0           Class::ReluctantORM::Exception::Param::WrongType->croak(
194             expected => 'Class::ReluctantORM::SQL::Table',
195             value => $table,
196             param => 'table',
197             );
198             }
199              
200             # Confirm that the table has this as a column
201 0 0 0       if ($table->knows_all_columns && $self->column && !$table->has_column($self->column)) {
      0        
202 0           Class::ReluctantORM::Exception::Param::BadValue->croak(
203             error => $self->column . " is not a column of table " . $table->table,
204             );
205             }
206 0           $self->set('table', $table);
207             }
208             }
209 0           return $self->get('table');
210             }
211              
212             =head2 $clone = $col->clone()
213              
214             Copies the column, by deeply cloning the table, and then directly copying the alias and column name.
215              
216             =cut
217              
218              
219             sub clone {
220 0     0 1   my $self = shift;
221 0           my $class = ref $self;
222              
223 0           my $other = $class->new();
224              
225 0           foreach my $simple (qw(column alias)) {
226 0 0         if ($self->$simple) {
227 0           $other->$simple($self->$simple);
228             }
229             }
230              
231 0           foreach my $complex (qw(table)) {
232 0 0         if ($self->$complex) {
233 0           $other->$complex($self->$complex()->clone());
234             }
235             }
236              
237 0           return $other;
238             }
239              
240             =head2 $bool = $param->is_equivalent($expr);
241              
242             Returns true if $expr is a Column, with matching column name. Alias is IGNORED.
243              
244             If both columns have Tables, then the table name (and schema, if present) are compared. The table aliases are IGNORED. If only one column has a Table, that difference is IGNORED.
245              
246             =cut
247              
248             sub is_equivalent {
249 0     0 1   my $left = shift;
250 0           my $right = shift;
251 0 0         unless ($right->is_column()) { return 0; }
  0            
252              
253             # If one column name is missing, both must be.
254 0 0 0       if (!$left->column || !$right->column) {
255 0   0       return (!$left->column && !$right->column);
256             }
257             # Otherwise column names must match, case insensitively.
258 0 0         unless (uc($left->column()) eq uc($right->column())) { return 0; }
  0            
259              
260             # Don't check aliases.
261              
262             # Table checks.
263             # If either table is missing, assume a match.
264 0 0 0       if (!$left->table || !$right->table) { return 1; }
  0            
265              
266             # Both have tables.
267 0           return $left->table->is_the_same_table($right->table());
268              
269             }
270              
271              
272             =head1 AUTHOR
273              
274             Clinton Wolfe
275              
276             =cut
277              
278             1;