Support setting default cmp operator in compat
[dbsrgits/SQL-Abstract-2.0-ish.git] / lib / SQL / Abstract / Compat.pm
CommitLineData
0bcf772f 1use MooseX::Declare;
2
3class SQL::Abstract::Compat {
7c300b3a 4
5 use Moose::Util::TypeConstraints;
bad761ba 6 use MooseX::Types::Moose qw/Str ScalarRef ArrayRef HashRef/;
0a18aa4f 7 use SQL::Abstract::Types::Compat ':all';
1c51edc4 8 use SQL::Abstract::Types qw/AST/;
0a18aa4f 9 use SQL::Abstract::AST::v1;
10 use Data::Dump qw/pp/;
1c51edc4 11 use Devel::PartialDump qw/dump/;
12 use Carp qw/croak/;
bad761ba 13
0a18aa4f 14 class_type 'SQL::Abstract';
7c300b3a 15 clean;
16
17 has logic => (
18 is => 'rw',
19 isa => LogicEnum,
aa0f2366 20 default => 'AND',
f94aef7f 21 coerce => 1,
22 required => 1,
7c300b3a 23 );
24
0a18aa4f 25 has visitor => (
26 is => 'rw',
27 isa => 'SQL::Abstract',
28 clearer => 'clear_visitor',
29 lazy => 1,
30 builder => '_build_visitor',
31 );
bad761ba 32
f94aef7f 33 has cmp => (
34 is => 'rw',
35 isa => 'Str',
36 default => '=',
37 required => 1,
38 );
39
40 our %CMP_MAP = (
41 '=' => '==',
42 );
bad761ba 43
7c300b3a 44 method select(Str|ArrayRef|ScalarRef $from, ArrayRef|Str $fields,
0a18aa4f 45 WhereType $where?,
46 WhereType $order?)
47 {
1c51edc4 48 my $ast = {
49 -type => 'select',
50 columns => [
51 map {
52 $self->mk_name($_)
53 } ( is_Str($fields) ? $fields : @$fields )
54 ],
55 tablespec => $self->tablespec($from)
56 };
0a18aa4f 57
1c51edc4 58
59 $ast->{where} = $self->recurse_where($where)
60 if defined $where;
7c300b3a 61
0a18aa4f 62 return ($self->visitor->dispatch($ast), $self->visitor->binds);
63 }
bad761ba 64
0a18aa4f 65 method where(WhereType $where,
66 WhereType $order?)
67 {
68 my $ret = "";
69
70 if ($where) {
1c51edc4 71 my $ast = $self->recurse_where($where);
0a18aa4f 72 $ret .= "WHERE " . $self->visitor->_expr($ast);
73 }
74
75 return $ret;
7c300b3a 76 }
bad761ba 77
0a18aa4f 78 method _build_visitor() {
79 return SQL::Abstract->create(1);
80 }
81
1c51edc4 82 sub mk_name {
83 shift;
84 return { -type => 'name', args => [ @_ ] };
85 }
86
87 method tablespec(Str|ArrayRef|ScalarRef $from) {
88 return $self->mk_name($from)
89 if is_Str($from);
90 }
91
92 method recurse_where(WhereType $ast, LogicEnum $logic?) returns (AST) {
93 return $self->recurse_where_hash($logic || 'AND', $ast) if is_HashRef($ast);
94 return $self->recurse_where_array($logic || 'OR', $ast) if is_ArrayRef($ast);
95 croak "Unknown where clause type " . dump($ast);
96 }
97
98 method recurse_where_hash(LogicEnum $logic, HashRef $ast) returns (AST) {
99 my @args;
100 my $ret = {
101 -type => 'expr',
102 op => lc $logic,
103 args => \@args
104 };
105
106 while (my ($key,$value) = each %$ast) {
107 if ($key =~ /^-(or|and)$/) {
108 my $val = $self->recurse_where($value, uc $1);
109 if ($val->{op} eq $ret->{op}) {
110 push @args, @{$val->{args}};
111 }
112 else {
113 push @args, $val;
114 }
115 next;
116 }
117
118 push @args, $self->field($key, $value);
119 }
120
121 return $args[0] if @args == 1;
122
123 return $ret;
124 }
125
126 method recurse_where_array(LogicEnum $logic, ArrayRef $ast) returns (AST) {
127 my @args;
128 my $ret = {
129 -type => 'expr',
130 op => lc $logic,
131 args => \@args
132 };
133 my @nodes = @$ast;
134
135 while (my $key = shift @nodes) {
136 if ($key =~ /^-(or|and)$/) {
137 my $value = shift @nodes
138 or confess "missing value after $key at " . dump($ast);
139
140 my $val = $self->recurse_where($value, uc $1);
141 if ($val->{op} eq $ret->{op}) {
142 push @args, @{$val->{args}};
143 }
144 else {
145 push @args, $val;
146 }
147 next;
148 }
149
150 push @args, $self->recurse_where($key);
151 }
152
153 return $args[0] if @args == 1;
154
155 return $ret;
156 }
157
158 method field(Str $key, $value) returns (AST) {
f94aef7f 159 my $op = $CMP_MAP{$self->cmp} || $self->cmp;
1c51edc4 160 my $ret = {
161 -type => 'expr',
f94aef7f 162 op => $op,
1c51edc4 163 args => [
164 { -type => 'name', args => [$key] }
165 ],
166 };
167
168 if (is_HashRef($value)) {
169 my ($op, @rest) = keys %$value;
170 confess "Don't know how to handle " . dump($value) . " (too many keys)"
171 if @rest;
172
173 # TODO: Validate the op?
174 if ($op =~ /^-([a-z_]+)$/i) {
175 $ret->{op} = lc $1;
176
177 if (is_ArrayRef($value->{$op})) {
178 push @{$ret->{args}}, $self->value($_)
179 for @{$value->{$op}};
180 return $ret;
181 }
182 }
183 else {
184 $ret->{op} = $op;
185 }
186
187 push @{$ret->{args}}, $self->value($value->{$op});
188
189 }
190 elsif (is_ArrayRef($value)) {
191 # Return an or clause, sort of.
192 return {
193 -type => 'expr',
194 op => 'or',
195 args => [ map {
196 {
197 -type => 'expr',
f94aef7f 198 op => $op,
1c51edc4 199 args => [
200 { -type => 'name', args => [$key] },
201 $self->value($_)
202 ],
203 }
204 } @$value ]
205 };
206 }
207 else {
208 push @{$ret->{args}}, $self->value($value);
209 }
210
211 return $ret;
212 }
213
214 method value($value) returns (AST) {
215 return { -type => 'value', value => $value }
216 if is_Str($value);
217
218 confess "Don't know how to handle terminal value " . dump($value);
219 }
220
221
0bcf772f 222}
223
224=head1 NAME
225
226SQL::Abstract::Compant - compatibility layer for SQL::Abstrct v 1.xx
227
228=head1 DESCRIPTION
229
230This class attempts to maintain the original behaviour of version 1 of
231SQL::Abstract. It does this by internally converting to an AST and then using
232the standard AST visitor.
233
234If so desired, you can get hold of this transformed AST somehow. This is aimed
235at libraries such as L<DBIx::Class> that use SQL::Abstract-style arrays or
236hashes as part of their public interface.
237
238=head1 AUTHOR
239
240Ash Berlin C<< <ash@cpan.org> >>
241
242=cut
243
2441;