module SchemaPlusPgIndexes::Middleware::Postgresql::Sql::IndexComponents
Public Instance Methods
around(env) { |env| ... }
click to toggle source
SchemaPlusPgIndexes
provides the following extra options for PostgreSQL indexes:
-
:expression
- SQL expression to index. column_name can be nil or ommitted, in which case :name must be provided -
:operator_class
- an operator class name or a hash mapping column name to operator class name -
+:case_sensitive - setting to
false
is a shorthand for :expression => 'LOWER(column_name)'
The :case_sensitive => false
option ties in with Rails built-in support for case-insensitive searching:
validates_uniqueness_of :name, :case_sensitive => false
Since :case_sensitive => false
is implemented by using :expression
, this raises an ArgumentError if both are specified simultaneously.
# File lib/schema_plus_pg_indexes/middleware/postgresql/sql.rb, line 21 def around(env) options = env.options column_names = env.column_names table_name = env.table_name connection = env.connection if env.column_names.empty? raise ArgumentError, "No columns and :expression missing from options - cannot create index" unless options[:expression] raise ArgumentError, "No columns, and index name not given. Pass :name option" unless options[:name] end expression = options.delete(:expression) operator_classes = options.delete(:operator_class) case_insensitive = (options.delete(:case_sensitive) == false) if expression raise ArgumentError, "Cannot specify :case_sensitive => false with an expression. Use LOWER(column_name)" if case_insensitive expression.strip! if m = expression.match(/^using\s+(?<using>\S+)\s*(?<rest>.*)/i) options[:using] = m[:using] expression = m[:rest] end if m = expression.match(/^(?<rest>.*)\s+where\s+(?<where>.*)/i) options[:where] = m[:where] expression = m[:rest] end end yield env if operator_classes and not operator_classes.is_a? Hash val = operator_classes operator_classes = Hash[column_names.map {|name| [name, val]}] operator_classes[nil] = val if expression end if operator_classes or case_insensitive if case_insensitive caseable_columns = connection.columns(table_name).select { |col| [:string, :text].include?(col.type) }.map(&:name) quoted_column_names = column_names.map do |col_name| [col_name, (caseable_columns.include?(col_name.to_s) ? "LOWER(#{connection.quote_column_name(col_name)})" : connection.quote_column_name(col_name))] end quoted_columns = Hash[quoted_column_names] else quoted_columns = Hash[column_names.map { |col_name| [col_name, connection.quote_column_name(col_name)] }] end (operator_classes||{}).stringify_keys.each do |column, opclass| quoted_columns[column] += " #{opclass}" if opclass and column.present? end quoted_columns = connection.send :add_index_sort_order, quoted_columns, **options env.sql.columns = quoted_columns.values.join(', ') end if expression expression = [expression, operator_classes[nil]].compact.join(' ') if operator_classes env.sql.columns = (env.sql.columns.split(/ *, */).reject{|col| expression =~ %r{\b#{col.gsub(/['"]/, '')}\b} } + [expression]).join(', ') end end