Skip to content
This repository
Browse code

Added where option to add_index to support postgresql partial indices

The `add_index` method now supports a `where` option that receives a
string with the partial index criteria.

    add_index(:accounts, :code, :where => "active")

    Generates

    CREATE INDEX index_accounts_on_code ON accounts(code) WHERE active
  • Loading branch information...
commit d70e0236df61d69c9299fe63df94da35c87ee2d8 1 parent 12c3b3d
Marcelo Silveira authored February 09, 2012
13  activerecord/CHANGELOG.md
Source Rendered
... ...
@@ -1,5 +1,18 @@
1 1
 ## Rails 4.0.0 (unreleased) ##
2 2
 
  3
+*   Added support for partial indices to PostgreSQL adapter
  4
+
  5
+    The `add_index` method now supports a `where` option that receives a
  6
+    string with the partial index criteria.
  7
+
  8
+      add_index(:accounts, :code, :where => "active")
  9
+
  10
+      Generates
  11
+
  12
+      CREATE INDEX index_accounts_on_code ON accounts(code) WHERE active
  13
+
  14
+    *Marcelo Silveira*
  15
+
3 16
 *   Implemented ActiveRecord::Relation#none method
4 17
 
5 18
     The `none` method returns a chainable relation with zero records
16  activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -381,9 +381,16 @@ def rename_column(table_name, column_name, new_column_name)
381 381
       #
382 382
       # Note: mysql doesn't yet support index order (it accepts the syntax but ignores it)
383 383
       #
  384
+      # ====== Creating a partial index
  385
+      #  add_index(:accounts, [:branch_id, :party_id], :unique => true, :where => "active")
  386
+      # generates
  387
+      #  CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
  388
+      #
  389
+      # Note: only supported by PostgreSQL
  390
+      #
384 391
       def add_index(table_name, column_name, options = {})
385  
-        index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
386  
-        execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})"
  392
+        index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
  393
+        execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
387 394
       end
388 395
 
389 396
       # Remove the given index from the table.
@@ -581,6 +588,9 @@ def add_index_options(table_name, column_name, options = {})
581 588
           if Hash === options # legacy support, since this param was a string
582 589
             index_type = options[:unique] ? "UNIQUE" : ""
583 590
             index_name = options[:name].to_s if options.key?(:name)
  591
+            if supports_partial_index?
  592
+              index_options = options[:where] ? " WHERE #{options[:where]}" : ""
  593
+            end
584 594
           else
585 595
             index_type = options
586 596
           end
@@ -593,7 +603,7 @@ def add_index_options(table_name, column_name, options = {})
593 603
           end
594 604
           index_columns = quoted_columns_for_index(column_names, options).join(", ")
595 605
 
596  
-          [index_name, index_type, index_columns]
  606
+          [index_name, index_type, index_columns, index_options]
597 607
         end
598 608
 
599 609
         def index_name_for_remove(table_name, options = {})
5  activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -142,6 +142,11 @@ def supports_index_sort_order?
142 142
         false
143 143
       end
144 144
 
  145
+      # Does this adapter support partial indices?
  146
+      def supports_partial_index?
  147
+        false
  148
+      end
  149
+
145 150
       # Does this adapter support explain? As of this writing sqlite3,
146 151
       # mysql2, and postgresql are the only ones that do.
147 152
       def supports_explain?
4  activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -302,6 +302,10 @@ def supports_index_sort_order?
302 302
         true
303 303
       end
304 304
 
  305
+      def supports_partial_index?
  306
+        true
  307
+      end
  308
+
305 309
       class StatementPool < ConnectionAdapters::StatementPool
306 310
         def initialize(connection, max)
307 311
           super
12  activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -21,6 +21,18 @@ def test_create_database_with_encoding
21 21
     assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
22 22
   end
23 23
 
  24
+  def test_add_index
  25
+    # add_index calls index_name_exists? which can't work since execute is stubbed
  26
+    ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_exists?) do |*|
  27
+      false
  28
+    end
  29
+
  30
+    expected = %(CREATE UNIQUE INDEX "index_people_on_last_name" ON "people" ("last_name") WHERE state = 'active')
  31
+    assert_equal expected, add_index(:people, :last_name, :unique => true, :where => "state = 'active'")
  32
+
  33
+    ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:remove_method, :index_name_exists?)
  34
+  end
  35
+
24 36
   private
25 37
     def method_missing(method_symbol, *arguments)
26 38
       ActiveRecord::Base.connection.send(method_symbol, *arguments)

1 note on commit d70e023

Will Leinweber
will commented on d70e023 March 10, 2012

Great work, thanks @mhfs!

Please sign in to comment.
Something went wrong with that request. Please try again.