Support index hints for mysql people/evaldas/index-hint
Evaldas Imbrasas [Fri, 24 Jun 2011 00:02:13 +0000 (17:02 -0700)]
lib/DBIx/Class/ResultSource.pm
lib/DBIx/Class/SQLMaker/MySQL.pm
t/71mysql.t

index 9489f49..96acbda 100644 (file)
@@ -1186,6 +1186,10 @@ Explicitly specifies the type of join to use in the relationship. Any
 SQL join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in
 the SQL command immediately before C<JOIN>.
 
+=item index_hint
+
+Adds an index hint for the joined table.
+
 =item proxy
 
 An arrayref containing a list of accessors in the foreign class to proxy in
@@ -1509,6 +1513,7 @@ sub _resolve_join {
                 ),
                -alias => $as,
                -relation_chain_depth => $seen->{-relation_chain_depth} || 0,
+               -index_hint => $rel_info->{attrs}{index_hint},
              },
              scalar $self->_resolve_condition($rel_info->{cond}, $as, $alias, $join)
           ];
index fdb2d6b..6357acb 100644 (file)
@@ -46,4 +46,51 @@ sub _lock_select {
    return " $sql";
 }
 
+# Allow index hints.
+# TBD: there must be a cleaner way to do this than overriding
+# this entire method. The problem is that each DBMS implements
+# index hints differently and puts them in different parts of the joins.
+sub _gen_from_blocks {
+  my ($self, $from, @joins) = @_;
+
+  my @fchunks = $self->_from_chunk_to_sql($from);
+
+  for (@joins) {
+    my ($to, $on) = @$_;
+
+    # check whether a join type exists
+    my $to_jt = ref($to) eq 'ARRAY' ? $to->[0] : $to;
+    my $join_type;
+    if (ref($to_jt) eq 'HASH' and defined($to_jt->{-join_type})) {
+      $join_type = $to_jt->{-join_type};
+      $join_type =~ s/^\s+ | \s+$//xg;
+    }
+
+    my @j = $self->_generate_join_clause( $join_type );
+
+    if (ref $to eq 'ARRAY') {
+      push(@j, '(', $self->_recurse_from(@$to), ')');
+    }
+    else {
+      push(@j, $self->_from_chunk_to_sql($to));
+    }
+
+    # add index hint, if specified
+    my $index_hint;
+    if (ref($to_jt) eq 'HASH' and defined($to_jt->{-index_hint})) {
+      $index_hint = $to_jt->{-index_hint};
+      $index_hint =~ s/^\s+ | \s+$//xg;
+    }
+    push(@j, ($index_hint ? " $index_hint" : ''));
+
+    my ($sql, @bind) = $self->_join_condition($on);
+    push(@j, ' ON ', $sql);
+    push @{$self->{from_bind}}, @bind;
+
+    push @fchunks, join '', @j;
+  }
+
+  return @fchunks;
+}
+
 1;
index c624913..2f61e1b 100644 (file)
@@ -240,6 +240,29 @@ lives_ok { $cd->set_producers ([ $producer ]) } 'set_relationship doesnt die';
   );
 }
 
+{
+  # Test support for index hints
+  my $cdsrc = $schema->source('CD');
+  my $artrel_info = $cdsrc->relationship_info ('artist');
+  $cdsrc->add_relationship(
+    'artist_hinted',
+    $artrel_info->{class},
+    $artrel_info->{cond},
+    { %{$artrel_info->{attrs}}, index_hint => 'FORCE INDEX (rank)' },
+  );
+  is_same_sql_bind (
+    $cdsrc->resultset->search({}, { prefetch => 'artist_hinted' })->as_query,
+    '(
+      SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track,
+             artist_hinted.artistid, artist_hinted.name, artist_hinted.rank, artist_hinted.charfield
+        FROM cd me
+        JOIN artist artist_hinted FORCE INDEX (rank) ON artist_hinted.artistid = me.artist
+    )',
+    [],
+    'index hints supported for mysql'
+  );
+}
+
 ## Can we properly deal with the null search problem?
 ##
 ## Only way is to do a SET SQL_AUTO_IS_NULL = 0; on connect