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);
84 return ($self->visitor->dispatch($ast), @{$self->visitor->binds});
87 method select_ast(Str|ArrayRef|ScalarRef $from, ArrayRef|Str $fields,
96 } ( is_Str($fields) ? $fields : @$fields )
98 tablespec => $self->tablespec($from)
102 $ast->{where} = $self->recurse_where($where)
105 if (defined $order) {
106 my @order = is_ArrayRef($order) ? @$order : $order;
107 $ast->{order_by} = [ map { $self->mk_name(0, $_) } @order ];
113 method where(WhereType $where,
119 my $ast = $self->recurse_where($where);
120 $ret .= "WHERE " . $self->visitor->_expr($ast);
127 # method mk_name(Bool $use_convert, Str @names) {
129 my ($self, $use_convert, @names) = @_;
131 @names = split /\Q@{[$self->name_sep]}\E/, $names[0]
132 if (@names == 1 && $self->has_name_sep);
134 my $ast = { -type => 'identifier', elements => [ @names ] };
137 unless $use_convert && $self->has_field_convertor;
139 return $self->apply_convert($ast);
142 method tablespec(Str|ArrayRef|ScalarRef $from) {
143 return $self->mk_name(0, $from)
149 $self->mk_name(0, $_)
154 method recurse_where(WhereType $ast, LogicEnum $logic?) returns (AST) {
155 return $self->recurse_where_hash($logic || 'AND', $ast) if is_HashRef($ast);
156 return $self->recurse_where_array($logic || 'OR', $ast) if is_ArrayRef($ast);
157 croak "Unknown where clause type " . dump($ast);
160 # Deals with where({ .... }) case
161 method recurse_where_hash(LogicEnum $logic, HashRef $ast) returns (AST) {
169 for my $key ( sort keys %$ast ) {
170 my $value = $ast->{$key};
172 if ($key =~ /^-(or|and)$/) {
173 my $val = $self->recurse_where($value, uc $1);
174 if ($val->{op} eq $ret->{op}) {
175 push @args, @{$val->{args}};
183 push @args, $self->field($key, $value);
186 return $args[0] if @args == 1;
191 # Deals with where([ .... ]) case
192 method recurse_where_array(LogicEnum $logic, ArrayRef $ast) returns (AST) {
201 while (my $key = shift @nodes) {
202 if ($key =~ /^-(or|and)$/) {
203 my $value = shift @nodes
204 or confess "missing value after $key at " . dump($ast);
206 my $val = $self->recurse_where($value, uc $1);
207 if ($val->{op} eq $ret->{op}) {
208 push @args, @{$val->{args}};
216 push @args, $self->recurse_where($key);
219 return $args[0] if @args == 1;
224 # { field => { .... } } case
225 method field_hash(Str $key, HashRef $value) returns (AST) {
226 my ($op, @rest) = keys %$value;
228 confess "Don't know how to handle " . dump($value) . " (too many keys)"
231 $value = $value->{$op};
237 $self->mk_name(1, $key)
242 # TODO: Validate the op?
243 # 'word_like' operator
244 if ($op =~ /^-?(?:(not)[_ ])?([a-z_]+)$/i) {
246 $ret->{op} = "not_" . $ret->{op} if $1;
249 if (is_ArrayRef($value)) {
250 push @{$ret->{args}}, $self->value($_) for @{$value};
256 # field => { '!=' => [ 'a','b','c'] }
257 # field => { '<' => [ 'a','b','c'] }
259 # *not* when op is a work or function operator - basic cmp operator only
260 if (is_ArrayRef($value)) {
261 local $self->{cmp} = $op;
267 $self->field($key, $_)
274 push @{$ret->{args}}, $self->value($value);
278 # Handle [ { ... }, { ... } ]
279 method field_array(Str $key, ArrayRef $value) {
280 # Return an or clause, sort of.
285 $self->field($key, $_)
290 method field(Str $key, $value) returns (AST) {
292 if (is_HashRef($value)) {
293 return $self->field_hash($key, $value);
295 elsif (is_ArrayRef($value)) {
296 return $self->field_array($key, $value);
301 op => $CMP_MAP{$self->cmp} || $self->cmp,
303 $self->mk_name(1, $key),
311 method value($value) returns (AST) {
312 return $self->apply_convert( { -type => 'value', value => $value })
315 confess "Don't know how to handle terminal value " . dump($value);
318 method apply_convert(AST $ast) {
319 return $ast unless $self->has_field_convertor;
323 op => $self->convert,
333 SQL::Abstract::Compant - compatibility layer for SQL::Abstrct v 1.xx
337 This class attempts to maintain the original behaviour of version 1 of
338 SQL::Abstract. It does this by internally converting to an AST and then using
339 the standard AST visitor.
341 If so desired, you can get hold of this transformed AST somehow. This is aimed
342 at libraries such as L<DBIx::Class> that use SQL::Abstract-style arrays or
343 hashes as part of their public interface.
347 Ash Berlin C<< <ash@cpan.org> >>