3 class SQL::Abstract::Compat {
5 use Moose::Util::TypeConstraints;
6 use MooseX::Types::Moose qw/Str ScalarRef ArrayRef HashRef/;
7 use SQL::Abstract::Types::Compat ':all';
8 use SQL::Abstract::Types qw/AST NameSeparator QuoteChars/;
9 use SQL::Abstract::AST::v1;
10 use Data::Dump qw/pp/;
11 use Devel::PartialDump qw/dump/;
14 class_type 'SQL::Abstract';
26 isa => 'SQL::Abstract',
27 clearer => 'clear_visitor',
29 builder => '_build_visitor',
46 predicate => 'has_field_convertor'
49 # TODO: a metaclass trait to automatically use this on vistior construction
54 predicate => "has_quote_chars"
60 predicate => "has_name_sep"
63 method _build_visitor() {
67 $args{quote_chars} = $self->quote_char
68 if $self->has_quote_chars;
69 $args{ident_separator} = $self->name_sep
70 if $self->has_name_sep;
72 # TODO: this needs improving along with SQL::A::create
73 my $visitor = SQL::Abstract::AST::v1->new(%args);
76 method select(Str|ArrayRef|ScalarRef $from, ArrayRef|Str $fields,
80 my $ast = $self->select_ast($from,$fields,$where,$order);
82 return ($self->visitor->dispatch($ast), @{$self->visitor->binds});
85 method update(Str|ArrayRef|ScalarRef $from,
86 HashRef $fields, WhereType $where? )
88 my $ast = $self->update_ast($from,$fields,$where);
90 return ($self->visitor->dispatch($ast), @{$self->visitor->binds});
93 method update_ast(Str|ArrayRef|ScalarRef $from,
94 HashRef $fields, WhereType $where? )
96 my (@columns, @values);
99 tablespec => $self->tablespec($from),
100 columns => \@columns,
104 for (keys %$fields) {
105 push @columns, $self->mk_name(0, $_);
106 push @values, { -type => 'value', value => $fields->{$_} };
109 $ast->{where} = $self->recurse_where($where)
115 method select_ast(Str|ArrayRef|ScalarRef $from, ArrayRef|Str $fields,
123 $self->mk_name(0, $_)
124 } ( is_Str($fields) ? $fields : @$fields )
126 tablespec => $self->tablespec($from)
130 $ast->{where} = $self->recurse_where($where)
133 if (defined $order) {
134 my @order = is_ArrayRef($order) ? @$order : $order;
135 $ast->{order_by} = [ map { $self->mk_name(0, $_) } @order ];
141 method where(WhereType $where,
147 my $ast = $self->recurse_where($where);
148 $ret .= "WHERE " . $self->visitor->_expr($ast);
155 # method mk_name(Bool $use_convert, Str @names) {
157 my ($self, $use_convert, @names) = @_;
159 @names = split /\Q@{[$self->name_sep]}\E/, $names[0]
160 if (@names == 1 && $self->has_name_sep);
162 my $ast = { -type => 'identifier', elements => [ @names ] };
165 unless $use_convert && $self->has_field_convertor;
167 return $self->apply_convert($ast);
170 method tablespec(Str|ArrayRef|ScalarRef $from) {
171 return $self->mk_name(0, $from)
177 $self->mk_name(0, $_)
182 method recurse_where(WhereType $ast, LogicEnum $logic?) {
183 return $self->recurse_where_hash($logic || 'AND', $ast) if is_HashRef($ast);
184 return $self->recurse_where_array($logic || 'OR', $ast) if is_ArrayRef($ast);
185 croak "Unknown where clause type " . dump($ast);
188 # Deals with where({ .... }) case
189 method recurse_where_hash(LogicEnum $logic, HashRef $ast) {
197 for my $key ( sort keys %$ast ) {
198 my $value = $ast->{$key};
200 if ($key =~ /^-(or|and)$/) {
201 my $val = $self->recurse_where($value, uc $1);
202 if ($val->{op} eq $ret->{op}) {
203 push @args, @{$val->{args}};
211 push @args, $self->field($key, $value);
214 return $args[0] if @args == 1;
219 # Deals with where([ .... ]) case
220 method recurse_where_array(LogicEnum $logic, ArrayRef $ast) {
229 while (my $key = shift @nodes) {
230 if ($key =~ /^-(or|and)$/) {
231 my $value = shift @nodes
232 or confess "missing value after $key at " . dump($ast);
234 my $val = $self->recurse_where($value, uc $1);
235 if ($val->{op} eq $ret->{op}) {
236 push @args, @{$val->{args}};
244 push @args, $self->recurse_where($key);
247 return $args[0] if @args == 1;
252 # { field => { .... } } case
253 method field_hash(Str $key, HashRef $value) {
254 my ($op, @rest) = keys %$value;
256 confess "Don't know how to handle " . dump($value) . " (too many keys)"
259 $value = $value->{$op};
265 $self->mk_name(1, $key)
270 # TODO: Validate the op?
271 # 'word_like' operator
272 if ($op =~ /^-?(?:(not)[_ ])?([a-z_]+)$/i) {
274 $ret->{op} = "not_" . $ret->{op} if $1;
277 if (is_ArrayRef($value)) {
278 push @{$ret->{args}}, $self->value($_) for @{$value};
284 # field => { '!=' => [ 'a','b','c'] }
285 # field => { '<' => [ 'a','b','c'] }
287 # *not* when op is a work or function operator - basic cmp operator only
288 if (is_ArrayRef($value)) {
289 local $self->{cmp} = $op;
295 $self->field($key, $_)
302 push @{$ret->{args}}, $self->value($value);
306 # Handle [ { ... }, { ... } ]
307 method field_array(Str $key, ArrayRef $value) {
308 # Return an or clause, sort of.
313 $self->field($key, $_)
318 method field(Str $key, $value) {
320 if (is_HashRef($value)) {
321 return $self->field_hash($key, $value);
323 elsif (is_ArrayRef($value)) {
324 return $self->field_array($key, $value);
329 op => $CMP_MAP{$self->cmp} || $self->cmp,
331 $self->mk_name(1, $key),
339 method value($value) {
340 return $self->apply_convert( { -type => 'value', value => $value })
343 confess "Don't know how to handle terminal value " . dump($value);
346 method apply_convert(AST $ast) {
347 return $ast unless $self->has_field_convertor;
351 op => $self->convert,
361 SQL::Abstract::Compant - compatibility layer for SQL::Abstrct v 1.xx
365 This class attempts to maintain the original behaviour of version 1 of
366 SQL::Abstract. It does this by internally converting to an AST and then using
367 the standard AST visitor.
369 If so desired, you can get hold of this transformed AST somehow. This is aimed
370 at libraries such as L<DBIx::Class> that use SQL::Abstract-style arrays or
371 hashes as part of their public interface.
375 Ash Berlin C<< <ash@cpan.org> >>