From: Dagfinn Ilmari Mannsåker Date: Mon, 28 Jul 2014 13:17:08 +0000 (+0100) Subject: Add SQLMaker methods for matching and unquoting quoted identifiers X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=dbsrgits%2FDBIx-Class.git;a=commitdiff_plain;h=cb913aa5b344d8d899bd7756a235d3bbdab0219f Add SQLMaker methods for matching and unquoting quoted identifiers --- diff --git a/lib/DBIx/Class/SQLMaker.pm b/lib/DBIx/Class/SQLMaker.pm index 6213c8b..0d27ec9 100644 --- a/lib/DBIx/Class/SQLMaker.pm +++ b/lib/DBIx/Class/SQLMaker.pm @@ -60,6 +60,40 @@ sub _quote_chars { ; } +sub _escape_char { + $_[0]->{escape_char} || ($_[0]->_quote_chars)[1] || ''; +} + +sub _unquote { + my ($self, $value) = @_; + + return $value unless defined $value; + + my ($l, $r, $e) = map { quotemeta $_ } $self->_quote_chars, $self->_escape_char; + + # no quoting, all bets are off + return $value unless length $e; + + my $re = $self->_quoted_ident_re($l, $r, $e); + + if ($value =~ /\A$re\z/) { + $value =~ s/\A$l//; + $value =~ s/$r\z//; + $value =~ s/( $e [$e$r] )/substr($1, 1)/gex; + return $value; + } + else { + # not a quoted value, assume it's an identifier + return $value; + } +} + +sub _quoted_ident_re { + my $self = shift; + my ($l, $r, $e) = @_ ? @_ : map { quotemeta $_ } $self->_quote_chars, $self->_escape_char; + return qr/ $l (?: [^$e$r] | $e [$e$r] )+ $r /x; +} + # FIXME when we bring in the storage weaklink, check its schema # weaklink and channel through $schema->throw_exception sub throw_exception { DBIx::Class::Exception->throw($_[1]) } diff --git a/lib/DBIx/Class/Storage/DBIHacks.pm b/lib/DBIx/Class/Storage/DBIHacks.pm index dc22244..350bb56 100644 --- a/lib/DBIx/Class/Storage/DBIHacks.pm +++ b/lib/DBIx/Class/Storage/DBIHacks.pm @@ -402,10 +402,12 @@ sub _resolve_aliastypes_from_select_args { # 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} = ''; @@ -482,16 +484,17 @@ sub _resolve_aliastypes_from_select_args { # 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 \Q$alias\E $rquote $sep (?: $lquote ([^$rquote]+) $rquote )? + $lquote \Q$alias\E $rquote $sep ($ident_re)? | \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;