Add SQLMaker methods for matching and unquoting quoted identifiers
Dagfinn Ilmari Mannsåker [Mon, 28 Jul 2014 13:17:08 +0000 (14:17 +0100)]
lib/DBIx/Class/SQLMaker.pm
lib/DBIx/Class/Storage/DBIHacks.pm

index 6213c8b..0d27ec9 100644 (file)
@@ -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]) }
index dc22244..350bb56 100644 (file)
@@ -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;