Module: ActiveRecord::ConnectionAdapters::SchemaStatements

Defined in:
lib/core_ext/active_record/connection_adapters/abstract/schema_statements.rb

Overview

:nodoc:

Constant Summary collapse

FUNCTIONAL_INDEX_REGEXP =

Regexp used to find the function name and function argument of a function call

/(\w+)\(((?:'.+'(?:::\w+)?, *)*)(\w+)\)/

Instance Method Summary collapse

Instance Method Details

#add_index(table_name, column_name, options = {}) ⇒ Object

Adds a new index to the table. column_name can be a single Symbol, or an Array of Symbols.

Creating a partial index
add_index(:accounts, [:branch_id, :party_id], :using => 'BTree'
 :unique => true, :concurrently => true, :where => 'active')

generates

CREATE UNIQUE INDEX CONCURRENTLY
 index_accounts_on_branch_id_and_party_id
ON
  accounts(branch_id, party_id)
WHERE
  active


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/core_ext/active_record/connection_adapters/abstract/schema_statements.rb', line 22

def add_index(table_name, column_name, options = {})
  name, type, creation_method, columns, opts = add_index_options(table_name, column_name, options)

  # GOTCHA:
  #   It ensures that there is no existing index only for the case when the index
  #   is created concurrently to avoid changing the error behavior for default
  #   index creation.
  #   -- zekefast 2012-09-25
  # GOTCHA:
  #   This check prevents invalid index creation, so after migration failed
  #   here there is no need to go to database and clean it from invalid
  #   indexes. But note that this handles only one of the cases when index
  #   creation can fail!!! All other case should be procesed manually.
  #   -- zekefast 2012-09-25
  if options.has_key?(:concurrently) && index_exists?(table_name, column_name, options)
    raise ::PgPower::IndexExistsError, "Index #{name} for `#{table_name}.#{column_name}` " \
      "column can not be created concurrently, because such index already exists."
  end

  sql = ["CREATE #{type} INDEX"]
  sql << creation_method.to_s
  sql << quote_column_name(name)
  sql << "ON #{quote_table_name(table_name)}"
  sql << "USING #{options[:using].to_s.downcase}" if options[:using]
  sql << "(#{columns})#{opts}"

  execute sql.join(" ")
end

#index_exists?(table_name, column_name, options = {}) ⇒ Boolean

Checks to see if an index exists on a table for a given index definition.

Examples

# Check that a partial index exists
index_exists?(:suppliers, :company_id, :where => 'active')

# GIVEN: 'index_suppliers_on_company_id' UNIQUE, btree (company_id) WHERE active
index_exists?(:suppliers, :company_id, :unique => true, :where => 'active') => true
index_exists?(:suppliers, :company_id, :unique => true) => false

Returns:

  • (Boolean)


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/core_ext/active_record/connection_adapters/abstract/schema_statements.rb', line 61

def index_exists?(table_name, column_name, options = {})
  column_names = Array.wrap(column_name)
  index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)

  # Always compare the index name
  default_comparator = lambda { |index| index.name == index_name }
  comparators = [default_comparator]

  # Add a comparator for each index option that is part of the query
  index_options = [:unique, :where]
  index_options.each do |index_option|
    comparators << if options.key?(index_option)
      lambda do |index|
        pg_where_clause = index.send(index_option)
        # pg does nothing to boolean clauses, e.g. 'where active' => 'where active'
        if pg_where_clause.is_a?(TrueClass) or pg_where_clause.is_a?(FalseClass)
          pg_where_clause == options[index_option]
        else
          # pg adds parentheses around non-boolean clauses, e.g. 'where color IS NULL' => 'where (color is NULL)'
          pg_where_clause.gsub!(/[()]/,'')
          # pg casts string comparison ::text. e.g. "where color = 'black'" => "where ((color)::text = 'black'::text)"
          pg_where_clause.gsub!(/::text/,'')
          # prevent case from impacting the comparison
          pg_where_clause.downcase == options[index_option].downcase
        end
      end
    else
      # If the given index_option is not an argument to the index_exists? query,
      # select only those pg indexes that do not have the component
      lambda { |index| index.send(index_option).blank? }
    end
  end

  # Search all indexes for any that match all comparators
  indexes(table_name).any? do |index|
    comparators.inject(true) { |ret, comparator| ret && comparator.call(index) }
  end
end

#index_name(table_name, options) ⇒ Object

Derives the name of the index from the given table name and options hash.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/core_ext/active_record/connection_adapters/abstract/schema_statements.rb', line 101

def index_name(table_name, options) #:nodoc:
  if Hash === options # legacy support
    if options[:column]
      column_names = Array.wrap(options[:column]).map {|c| expression_index_name(c)}
      "index_#{table_name}_on_#{column_names * '_and_'}"
    elsif options[:name]
      options[:name]
    else
      raise ArgumentError, "You must specify the index name"
    end
  else
    index_name(table_name, :column => options)
  end
end