use List::Util 'first';
use Scalar::Util 'blessed';
-use Sub::Name 'subname';
-use DBIx::Class::_Util qw(is_plain_value is_literal_value UNRESOLVABLE_CONDITION);
+use DBIx::Class::_Util 'UNRESOLVABLE_CONDITION';
+use SQL::Abstract qw(is_plain_value is_literal_value);
use namespace::clean;
#
my $outer_attrs = { %$attrs };
delete @{$outer_attrs}{qw(from bind rows offset group_by _grouped_by_distinct having)};
- my $inner_attrs = { %$attrs };
- delete @{$inner_attrs}{qw(for collapse select as _related_results_construction)};
+ my $inner_attrs = { %$attrs, _simple_passthrough_construction => 1 };
+ delete @{$inner_attrs}{qw(for collapse select as)};
# there is no point of ordering the insides if there is no limit
delete $inner_attrs->{order_by} if (
# name_sep, otherwise sorry nasty legacy syntax like
# { 'count(foo.id)' => { '>' => 3 } } will stop working >:(
local $sql_maker->{quote_char} = $sql_maker->{quote_char};
+ local $sql_maker->{escape_char} = $sql_maker->{escape_char};
local $sql_maker->{name_sep} = $sql_maker->{name_sep};
unless (defined $sql_maker->{quote_char} and length $sql_maker->{quote_char}) {
$sql_maker->{quote_char} = ["\x00", "\xFF"];
+ $sql_maker->{escape_char} = "\xFF";
# if we don't unset it we screw up retarded but unfortunately working
# 'MAX(foo.bar)' => { '>', 3 }
$sql_maker->{name_sep} = '';
# now loop through all fully qualified columns and get the corresponding
# alias (should work even if they are in scalarrefs)
+ my $ident_re = $sql_maker->_quoted_ident_re;
for my $alias (keys %$alias_list) {
my $al_re = qr/
- $lquote $alias $rquote $sep (?: $lquote ([^$rquote]+) $rquote )?
+ $lquote \Q$alias\E $rquote $sep ($ident_re)?
|
- \b $alias \. ([^\s\)\($rquote]+)?
+ \b \Q$alias\E \. ([^\s\)\($rquote]+)?
/x;
for my $type (keys %$to_scan) {
for my $piece (@{$to_scan->{$type}}) {
- if (my @matches = $piece =~ /$al_re/g) {
+ if (my @matches = map { $sql_maker->_unquote($_) } $piece =~ /$al_re/g) {
$aliases_by_type->{$type}{$alias} ||= { -parents => $alias_list->{$alias}{-join_path}||[] };
$aliases_by_type->{$type}{$alias}{-seen_columns}{"$alias.$_"} = "$alias.$_"
for grep { defined $_ } @matches;
for my $col (keys %$colinfo) {
next if $col =~ / \. /x; # if column is qualified it was caught by the above
- my $col_re = qr/ $lquote ($col) $rquote /x;
+ my $col_re = qr/ $lquote (\Q$col\E) $rquote /x;
for my $type (keys %$to_scan) {
for my $piece (@{$to_scan->{$type}}) {
return scalar $sql_maker->_order_by_chunks ($order_by)
unless wantarray;
- my ($lq, $rq, $sep) = map { quotemeta($_) } (
- ($orig_quote_chars ? @$orig_quote_chars : $sql_maker->_quote_chars),
- $sql_maker->name_sep
- );
+ my $sep = quotemeta($sql_maker->name_sep);
+
+ my @quotes = map { quotemeta($_) }
+ $orig_quote_chars
+ ? @$orig_quote_chars
+ : ($sql_maker->_quote_chars, $sql_maker->_escape_char);
+
+ my $quoted_ident_re = $sql_maker->_quoted_ident_re(@quotes);
my @chunks;
for ($sql_maker->_order_by_chunks ($order_by) ) {
# order criteria may have come back pre-quoted (literals and whatnot)
# this is fragile, but the best we can currently do
- $chunk->[0] =~ s/^ $lq (.+?) $rq $sep $lq (.+?) $rq $/"$1.$2"/xe
- or $chunk->[0] =~ s/^ $lq (.+) $rq $/$1/x;
+ if (my @quoted = $chunk->[0] =~ /\A ($quoted_ident_re) (?: $sep ($quoted_ident_re) )? \z/x) {
+ $chunk->[0] = join('.', map { $sql_maker->_unquote($_, @quotes) } grep { defined } @quoted);
+ }
push @chunks, $chunk;
}
# pass these in to deal with literals coming from
# the user or the deep guts of prefetch
- my $orig_quote_chars = [$sql_maker->_quote_chars];
+ my $orig_quote_chars = [$sql_maker->_quote_chars, $sql_maker->_escape_char];
local $sql_maker->{quote_char};
return $parser->($sql_maker, $order_by, $orig_quote_chars);
if (ref $rhs eq 'HASH' and ! keys %$rhs) {
# FIXME - SQLA seems to be doing... nothing...?
}
+ elsif (ref $rhs eq 'HASH' and keys %$rhs == 1 and exists $rhs->{-ident}) {
+ push @conds, { $lhs => { '=', $rhs } };
+ }
+ elsif (ref $rhs eq 'HASH' and keys %$rhs == 1 and exists $rhs->{-value} and is_plain_value $rhs->{-value}) {
+ push @conds, { $lhs => $rhs->{-value} };
+ }
elsif (ref $rhs eq 'HASH' and keys %$rhs == 1 and exists $rhs->{'='}) {
- for my $p ($self->_collapse_cond_unroll_pairs([ [ $lhs => $rhs->{'='} ] ])) {
-
- # extra sanity check
- if (keys %$p > 1) {
- require Data::Dumper::Concise;
- local $Data::Dumper::Deepcopy = 1;
- $self->throw_exception(
- "Internal error: unexpected collapse unroll:"
- . Data::Dumper::Concise::Dumper { in => { $lhs => $rhs }, out => $p }
- );
- }
+ if( is_literal_value $rhs->{'='}) {
+ push @conds, { $lhs => $rhs };
+ }
+ else {
+ for my $p ($self->_collapse_cond_unroll_pairs([ [ $lhs => $rhs->{'='} ] ])) {
+
+ # extra sanity check
+ if (keys %$p > 1) {
+ require Data::Dumper::Concise;
+ local $Data::Dumper::Deepcopy = 1;
+ $self->throw_exception(
+ "Internal error: unexpected collapse unroll:"
+ . Data::Dumper::Concise::Dumper { in => { $lhs => $rhs }, out => $p }
+ );
+ }
- my ($l, $r) = %$p;
+ my ($l, $r) = %$p;
- push @conds, ( ! length ref $r or is_plain_value($r) )
- ? { $l => $r }
- : { $l => { '=' => $r } }
- ;
+ push @conds, ( ! length ref $r or is_plain_value($r) )
+ ? { $l => $r }
+ : { $l => { '=' => $r } }
+ ;
+ }
}
}
elsif (ref $rhs eq 'ARRAY') {
$vals->{$undef_marker} = $v if $consider_nulls
}
elsif (
- ! length ref $v
- or
- is_plain_value ($v)
- ) {
- $vals->{$v} = $v;
- }
- elsif (
ref $v eq 'HASH'
and
keys %$v == 1
- and
- ref $v->{'='}
- and
+ ) {
+ if (exists $v->{-value}) {
+ if (defined $v->{-value}) {
+ $vals->{$v->{-value}} = $v->{-value}
+ }
+ elsif( $consider_nulls ) {
+ $vals->{$undef_marker} = $v->{-value};
+ }
+ }
# do not need to check for plain values - _collapse_cond did it for us
- is_literal_value($v->{'='})
+ elsif(ref $v->{'='} and is_literal_value($v->{'='}) ) {
+ $vals->{$v->{'='}} = $v->{'='};
+ }
+ }
+ elsif (
+ ! length ref $v
+ or
+ is_plain_value ($v)
) {
- $vals->{$v->{'='}} = $v->{'='};
+ $vals->{$v} = $v;
}
elsif (ref $v eq 'ARRAY' and ($v->[0]||'') eq '-and') {
for ( @{$v}[1..$#$v] ) {