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_aste($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 return { -type => 'update' };
100 method select_ast(Str|ArrayRef|ScalarRef $from, ArrayRef|Str $fields,
108 $self->mk_name(0, $_)
109 } ( is_Str($fields) ? $fields : @$fields )
111 tablespec => $self->tablespec($from)
115 $ast->{where} = $self->recurse_where($where)
118 if (defined $order) {
119 my @order = is_ArrayRef($order) ? @$order : $order;
120 $ast->{order_by} = [ map { $self->mk_name(0, $_) } @order ];
126 method where(WhereType $where,
132 my $ast = $self->recurse_where($where);
133 $ret .= "WHERE " . $self->visitor->_expr($ast);
140 # method mk_name(Bool $use_convert, Str @names) {
142 my ($self, $use_convert, @names) = @_;
144 @names = split /\Q@{[$self->name_sep]}\E/, $names[0]
145 if (@names == 1 && $self->has_name_sep);
147 my $ast = { -type => 'identifier', elements => [ @names ] };
150 unless $use_convert && $self->has_field_convertor;
152 return $self->apply_convert($ast);
155 method tablespec(Str|ArrayRef|ScalarRef $from) {
156 return $self->mk_name(0, $from)
162 $self->mk_name(0, $_)
167 method recurse_where(WhereType $ast, LogicEnum $logic?) returns (AST) {
168 return $self->recurse_where_hash($logic || 'AND', $ast) if is_HashRef($ast);
169 return $self->recurse_where_array($logic || 'OR', $ast) if is_ArrayRef($ast);
170 croak "Unknown where clause type " . dump($ast);
173 # Deals with where({ .... }) case
174 method recurse_where_hash(LogicEnum $logic, HashRef $ast) returns (AST) {
182 for my $key ( sort keys %$ast ) {
183 my $value = $ast->{$key};
185 if ($key =~ /^-(or|and)$/) {
186 my $val = $self->recurse_where($value, uc $1);
187 if ($val->{op} eq $ret->{op}) {
188 push @args, @{$val->{args}};
196 push @args, $self->field($key, $value);
199 return $args[0] if @args == 1;
204 # Deals with where([ .... ]) case
205 method recurse_where_array(LogicEnum $logic, ArrayRef $ast) returns (AST) {
214 while (my $key = shift @nodes) {
215 if ($key =~ /^-(or|and)$/) {
216 my $value = shift @nodes
217 or confess "missing value after $key at " . dump($ast);
219 my $val = $self->recurse_where($value, uc $1);
220 if ($val->{op} eq $ret->{op}) {
221 push @args, @{$val->{args}};
229 push @args, $self->recurse_where($key);
232 return $args[0] if @args == 1;
237 # { field => { .... } } case
238 method field_hash(Str $key, HashRef $value) returns (AST) {
239 my ($op, @rest) = keys %$value;
241 confess "Don't know how to handle " . dump($value) . " (too many keys)"
244 $value = $value->{$op};
250 $self->mk_name(1, $key)
255 # TODO: Validate the op?
256 # 'word_like' operator
257 if ($op =~ /^-?(?:(not)[_ ])?([a-z_]+)$/i) {
259 $ret->{op} = "not_" . $ret->{op} if $1;
262 if (is_ArrayRef($value)) {
263 push @{$ret->{args}}, $self->value($_) for @{$value};
269 # field => { '!=' => [ 'a','b','c'] }
270 # field => { '<' => [ 'a','b','c'] }
272 # *not* when op is a work or function operator - basic cmp operator only
273 if (is_ArrayRef($value)) {
274 local $self->{cmp} = $op;
280 $self->field($key, $_)
287 push @{$ret->{args}}, $self->value($value);
291 # Handle [ { ... }, { ... } ]
292 method field_array(Str $key, ArrayRef $value) {
293 # Return an or clause, sort of.
298 $self->field($key, $_)
303 method field(Str $key, $value) returns (AST) {
305 if (is_HashRef($value)) {
306 return $self->field_hash($key, $value);
308 elsif (is_ArrayRef($value)) {
309 return $self->field_array($key, $value);
314 op => $CMP_MAP{$self->cmp} || $self->cmp,
316 $self->mk_name(1, $key),
324 method value($value) returns (AST) {
325 return $self->apply_convert( { -type => 'value', value => $value })
328 confess "Don't know how to handle terminal value " . dump($value);
331 method apply_convert(AST $ast) {
332 return $ast unless $self->has_field_convertor;
336 op => $self->convert,
346 SQL::Abstract::Compant - compatibility layer for SQL::Abstrct v 1.xx
350 This class attempts to maintain the original behaviour of version 1 of
351 SQL::Abstract. It does this by internally converting to an AST and then using
352 the standard AST visitor.
354 If so desired, you can get hold of this transformed AST somehow. This is aimed
355 at libraries such as L<DBIx::Class> that use SQL::Abstract-style arrays or
356 hashes as part of their public interface.
360 Ash Berlin C<< <ash@cpan.org> >>