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';
27 isa => 'SQL::Abstract',
28 clearer => 'clear_visitor',
30 builder => '_build_visitor',
47 predicate => 'has_field_convertor'
50 # TODO: a metaclass trait to automatically use this on vistior construction
55 predicate => "has_quote_chars"
61 predicate => "has_name_sep"
64 method _build_visitor() {
68 $args{quote_chars} = $self->quote_char
69 if $self->has_quote_chars;
70 $args{ident_separator} = $self->name_sep
71 if $self->has_name_sep;
73 # TODO: this needs improving along with SQL::A::create
74 my $visitor = SQL::Abstract::AST::v1->new(%args);
77 method select(Str|ArrayRef|ScalarRef $from, ArrayRef|Str $fields,
81 my $ast = $self->select_ast($from,$fields,$where,$order);
83 return ($self->visitor->dispatch($ast), @{$self->visitor->binds});
86 method update(Str|ArrayRef|ScalarRef $from,
87 HashRef $fields, WhereType $where? )
89 my $ast = $self->update_ast($from,$fields,$where);
91 return ($self->visitor->dispatch($ast), @{$self->visitor->binds});
94 method update_ast(Str|ArrayRef|ScalarRef $from,
95 HashRef $fields, WhereType $where? )
97 my (@columns, @values);
100 tablespec => $self->tablespec($from),
101 columns => \@columns,
105 for (keys %$fields) {
106 push @columns, $self->mk_name(0, $_);
107 push @values, { -type => 'value', value => $fields->{$_} };
113 method select_ast(Str|ArrayRef|ScalarRef $from, ArrayRef|Str $fields,
121 $self->mk_name(0, $_)
122 } ( is_Str($fields) ? $fields : @$fields )
124 tablespec => $self->tablespec($from)
128 $ast->{where} = $self->recurse_where($where)
131 if (defined $order) {
132 my @order = is_ArrayRef($order) ? @$order : $order;
133 $ast->{order_by} = [ map { $self->mk_name(0, $_) } @order ];
139 method where(WhereType $where,
145 my $ast = $self->recurse_where($where);
146 $ret .= "WHERE " . $self->visitor->_expr($ast);
153 # method mk_name(Bool $use_convert, Str @names) {
155 my ($self, $use_convert, @names) = @_;
157 @names = split /\Q@{[$self->name_sep]}\E/, $names[0]
158 if (@names == 1 && $self->has_name_sep);
160 my $ast = { -type => 'identifier', elements => [ @names ] };
163 unless $use_convert && $self->has_field_convertor;
165 return $self->apply_convert($ast);
168 method tablespec(Str|ArrayRef|ScalarRef $from) {
169 return $self->mk_name(0, $from)
175 $self->mk_name(0, $_)
180 method recurse_where(WhereType $ast, LogicEnum $logic?) returns (AST) {
181 return $self->recurse_where_hash($logic || 'AND', $ast) if is_HashRef($ast);
182 return $self->recurse_where_array($logic || 'OR', $ast) if is_ArrayRef($ast);
183 croak "Unknown where clause type " . dump($ast);
186 # Deals with where({ .... }) case
187 method recurse_where_hash(LogicEnum $logic, HashRef $ast) returns (AST) {
195 for my $key ( sort keys %$ast ) {
196 my $value = $ast->{$key};
198 if ($key =~ /^-(or|and)$/) {
199 my $val = $self->recurse_where($value, uc $1);
200 if ($val->{op} eq $ret->{op}) {
201 push @args, @{$val->{args}};
209 push @args, $self->field($key, $value);
212 return $args[0] if @args == 1;
217 # Deals with where([ .... ]) case
218 method recurse_where_array(LogicEnum $logic, ArrayRef $ast) returns (AST) {
227 while (my $key = shift @nodes) {
228 if ($key =~ /^-(or|and)$/) {
229 my $value = shift @nodes
230 or confess "missing value after $key at " . dump($ast);
232 my $val = $self->recurse_where($value, uc $1);
233 if ($val->{op} eq $ret->{op}) {
234 push @args, @{$val->{args}};
242 push @args, $self->recurse_where($key);
245 return $args[0] if @args == 1;
250 # { field => { .... } } case
251 method field_hash(Str $key, HashRef $value) returns (AST) {
252 my ($op, @rest) = keys %$value;
254 confess "Don't know how to handle " . dump($value) . " (too many keys)"
257 $value = $value->{$op};
263 $self->mk_name(1, $key)
268 # TODO: Validate the op?
269 # 'word_like' operator
270 if ($op =~ /^-?(?:(not)[_ ])?([a-z_]+)$/i) {
272 $ret->{op} = "not_" . $ret->{op} if $1;
275 if (is_ArrayRef($value)) {
276 push @{$ret->{args}}, $self->value($_) for @{$value};
282 # field => { '!=' => [ 'a','b','c'] }
283 # field => { '<' => [ 'a','b','c'] }
285 # *not* when op is a work or function operator - basic cmp operator only
286 if (is_ArrayRef($value)) {
287 local $self->{cmp} = $op;
293 $self->field($key, $_)
300 push @{$ret->{args}}, $self->value($value);
304 # Handle [ { ... }, { ... } ]
305 method field_array(Str $key, ArrayRef $value) {
306 # Return an or clause, sort of.
311 $self->field($key, $_)
316 method field(Str $key, $value) returns (AST) {
318 if (is_HashRef($value)) {
319 return $self->field_hash($key, $value);
321 elsif (is_ArrayRef($value)) {
322 return $self->field_array($key, $value);
327 op => $CMP_MAP{$self->cmp} || $self->cmp,
329 $self->mk_name(1, $key),
337 method value($value) returns (AST) {
338 return $self->apply_convert( { -type => 'value', value => $value })
341 confess "Don't know how to handle terminal value " . dump($value);
344 method apply_convert(AST $ast) {
345 return $ast unless $self->has_field_convertor;
349 op => $self->convert,
359 SQL::Abstract::Compant - compatibility layer for SQL::Abstrct v 1.xx
363 This class attempts to maintain the original behaviour of version 1 of
364 SQL::Abstract. It does this by internally converting to an AST and then using
365 the standard AST visitor.
367 If so desired, you can get hold of this transformed AST somehow. This is aimed
368 at libraries such as L<DBIx::Class> that use SQL::Abstract-style arrays or
369 hashes as part of their public interface.
373 Ash Berlin C<< <ash@cpan.org> >>