├── .gitignore ├── .rspec ├── .tachikoma.yml ├── .travis.yml ├── Appraisals ├── ChangeLog.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── activerecord-mysql-index-hint.gemspec ├── gemfiles ├── 4.1.gemfile ├── 4.2.gemfile └── 5.0.gemfile ├── lib ├── activerecord-mysql-index-hint.rb └── activerecord-mysql-index-hint │ └── version.rb └── spec ├── activerecord-mysql-index-hint_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | gemfiles/*.lock 19 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /.tachikoma.yml: -------------------------------------------------------------------------------- 1 | strategy: 'bundler' 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 2.1.8 3 | - 2.2.4 4 | - 2.3.0 5 | - ruby-head 6 | install: bundle install 7 | before_install: gem install bundler -v 1.11.2 8 | script: bundle exec rake spec 9 | gemfile: 10 | - gemfiles/4.1.gemfile 11 | - gemfiles/4.2.gemfile 12 | - gemfiles/5.0.gemfile 13 | matrix: 14 | allow_failures: 15 | - gemfile: gemfiles/5.0.gemfile 16 | - rvm: ruby-head 17 | fast_finish: true 18 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "4.1" do 2 | gem "mysql2", "~> 0.3.13" 3 | gem "activerecord", "~> 4.1.15" 4 | gem "activerecord-mysql-index-hint", :path => "." 5 | end 6 | 7 | appraise "4.2" do 8 | gem "activerecord", "~> 4.2.6" 9 | gem "activerecord-mysql-index-hint", :path => "." 10 | end 11 | 12 | appraise "5.0" do 13 | gem "activerecord", "~> 5.0.0.beta3" 14 | gem "activerecord-mysql-index-hint", :path => "." 15 | end 16 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | ## 0.0.4 3 | - Support Rails 5 (Thanks @adorechic) 4 | 5 | ## 0.0.3 6 | - Fix count with eager_load (Thanks @kamipo) 7 | 8 | ## 0.0.2 9 | - Don't directly touch AR::Base, but do it via AS.on_load (Thanks @amatsuda) 10 | 11 | ## 0.0.1 12 | - First release 13 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in activerecord-mysql-index-hint.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Issei Naruta 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/mirakui/activerecord-mysql-index-hint.png)](https://travis-ci.org/mirakui/activerecord-mysql-index-hint) 2 | 3 | # activerecord-mysql-index-hint 4 | 5 | MySQL index hint support for ActiveRecord 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'activerecord-mysql-index-hint' 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```ruby 18 | class Product < ActiveRecord::Base 19 | end 20 | 21 | Product.where(user_id: 1).force_index(:idx_user_id).first 22 | # => SELECT `products`.* FROM `products` FORCE INDEX(`idx_user_id`) WHERE `products`.`user_id` = 1 LIMIT 1 23 | ``` 24 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'appraisal' 3 | require 'rspec/core/rake_task' 4 | 5 | RSpec::Core::RakeTask.new(:spec) 6 | -------------------------------------------------------------------------------- /activerecord-mysql-index-hint.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'activerecord-mysql-index-hint/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "activerecord-mysql-index-hint" 8 | spec.version = ActiveRecordMysqlIndexHint::VERSION 9 | spec.authors = ["Issei Naruta"] 10 | spec.email = ["naruta@cookpad.com"] 11 | spec.description = %q{MySQL index hint support for ActiveRecord} 12 | spec.summary = %q{MySQL index hint support for ActiveRecord} 13 | spec.homepage = "https://github.com/mirakui/activerecord-mysql-index-hint" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files`.split($/) 17 | spec.test_files = spec.files.grep(%r{^spec/}) 18 | spec.require_paths = ["lib"] 19 | 20 | spec.add_dependency 'activerecord', '>= 3.1' 21 | spec.add_development_dependency 'bundler', '~> 1.3' 22 | spec.add_development_dependency 'rake' 23 | spec.add_development_dependency 'appraisal' 24 | spec.add_development_dependency 'rspec' 25 | spec.add_development_dependency 'mysql2', '>= 0.3.18', '< 0.5' 26 | end 27 | -------------------------------------------------------------------------------- /gemfiles/4.1.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "mysql2", "~> 0.3.13" 6 | gem "activerecord", "~> 4.1.15" 7 | gem "activerecord-mysql-index-hint", :path => "../" 8 | 9 | gemspec :path => "../" 10 | -------------------------------------------------------------------------------- /gemfiles/4.2.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "~> 4.2.6" 6 | gem "activerecord-mysql-index-hint", :path => "../" 7 | 8 | gemspec :path => "../" 9 | -------------------------------------------------------------------------------- /gemfiles/5.0.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "~> 5.0.0.beta3" 6 | gem "activerecord-mysql-index-hint", :path => "../" 7 | 8 | gemspec :path => "../" 9 | -------------------------------------------------------------------------------- /lib/activerecord-mysql-index-hint.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | 3 | module ActiveRecordMysqlIndexHint 4 | def use_index(*args) 5 | from_with_index_hint 'USE', *args 6 | end 7 | 8 | def force_index(*args) 9 | from_with_index_hint 'FORCE', *args 10 | end 11 | 12 | def ignore_index(*args) 13 | from_with_index_hint 'IGNORE', *args 14 | end 15 | 16 | private 17 | def from_with_index_hint(hint_type, *args) 18 | return self if args.blank? 19 | indexes = args.map {|index| connection.quote_column_name index } 20 | self.from([Arel.sql("#{quoted_table_name} #{hint_type} INDEX(#{indexes.join(', ')})")]) 21 | end 22 | end 23 | 24 | ActiveSupport.on_load :active_record do 25 | ActiveRecord::Base.extend ActiveRecordMysqlIndexHint 26 | ActiveRecord::Relation.send :include, ActiveRecordMysqlIndexHint 27 | end 28 | -------------------------------------------------------------------------------- /lib/activerecord-mysql-index-hint/version.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecordMysqlIndexHint 2 | VERSION = "0.0.4" 3 | end 4 | -------------------------------------------------------------------------------- /spec/activerecord-mysql-index-hint_spec.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | require 'active_record/connection_adapters/mysql2_adapter' 3 | require 'activerecord-mysql-index-hint' 4 | 5 | module ActiveRecord 6 | module ConnectionHandling 7 | prepend Module.new { 8 | def mysql2_connection(config) 9 | ConnectionAdapters::Mysql2Adapter.new(nil, logger, nil, config) 10 | end 11 | } 12 | end 13 | 14 | module ConnectionAdapters 15 | class Mysql2Adapter 16 | prepend Module.new { 17 | def quote_string(string) 18 | string 19 | end 20 | 21 | def reconnect! 22 | end 23 | 24 | def configure_connection 25 | end 26 | 27 | def full_version 28 | "5.0.0" 29 | end 30 | } 31 | end 32 | end 33 | end 34 | 35 | Arel::Visitors::ToSql.class_eval do 36 | def column_for o 37 | nil 38 | end 39 | end 40 | 41 | class Product < ActiveRecord::Base 42 | establish_connection 'mysql2://user@host/db' 43 | end 44 | 45 | def normalize_spaces(sql) 46 | sql.gsub(/ {2,}/, ' ') 47 | end 48 | 49 | describe 'ActiveRecord Index Hint' do 50 | context 'with AR::Relation' do 51 | it do 52 | expect( 53 | normalize_spaces Product.limit(1).use_index(:idx1).to_sql 54 | ).to eq('SELECT `products`.* FROM `products` USE INDEX(`idx1`) LIMIT 1') 55 | end 56 | 57 | it do 58 | expect( 59 | normalize_spaces Product.limit(1).force_index(:idx1).to_sql 60 | ).to eq('SELECT `products`.* FROM `products` FORCE INDEX(`idx1`) LIMIT 1') 61 | end 62 | 63 | it do 64 | expect( 65 | normalize_spaces Product.limit(1).ignore_index(:idx1).to_sql 66 | ).to eq('SELECT `products`.* FROM `products` IGNORE INDEX(`idx1`) LIMIT 1') 67 | end 68 | end 69 | 70 | context 'with AR::Base' do 71 | it do 72 | expect( 73 | normalize_spaces Product.use_index(:idx1).limit(1).to_sql 74 | ).to eq('SELECT `products`.* FROM `products` USE INDEX(`idx1`) LIMIT 1') 75 | end 76 | end 77 | 78 | context 'with multiple indexes' do 79 | it do 80 | expect( 81 | normalize_spaces Product.limit(1).use_index(:idx1, :idx2).to_sql 82 | ).to eq('SELECT `products`.* FROM `products` USE INDEX(`idx1`, `idx2`) LIMIT 1') 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # Require this file using `require "spec_helper"` to ensure that it is only 4 | # loaded once. 5 | # 6 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 7 | RSpec.configure do |config| 8 | config.treat_symbols_as_metadata_keys_with_true_values = true 9 | config.run_all_when_everything_filtered = true 10 | config.filter_run :focus 11 | 12 | # Run specs in random order to surface order dependencies. If you find an 13 | # order dependency and want to debug it, you can fix the order by providing 14 | # the seed, which is printed after each run. 15 | # --seed 1234 16 | config.order = 'random' 17 | end 18 | --------------------------------------------------------------------------------