Module: SchemaPlus::ActiveRecord::ConnectionAdapters::PostgresqlAdapter
- Defined in:
- lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb
Overview
The Postgresql adapter implements the SchemaPlus extensions and enhancements
Class Method Summary collapse
-
.included(base) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#add_index(table_name, column_name, options = {}) ⇒ Object
SchemaPlus provides the following extra options for Postgres indexes: *
:conditions- SQL conditions for the WHERE clause of the index *:expression- SQL expression to index. -
#exec_cache_with_schema_plus(sql, binds) ⇒ Object
Prepass to replace each ActiveRecord::DB_DEFAULT with a literal DEFAULT in the sql string.
-
#foreign_keys(table_name, name = nil) ⇒ Object
:nodoc:.
-
#indexes(table_name, name = nil) ⇒ Object
This method entirely duplicated from AR’s postgresql_adapter.c, but includes the extra bit to determine the column name for a case-insensitive index.
-
#rename_table_with_schema_plus(oldname, newname) ⇒ Object
:nodoc:.
-
#reverse_foreign_keys(table_name, name = nil) ⇒ Object
:nodoc:.
-
#supports_partial_indexes? ⇒ Boolean
:nodoc:.
-
#view_definition(view_name, name = nil) ⇒ Object
:nodoc:.
-
#views(name = nil) ⇒ Object
:nodoc:.
Class Method Details
.included(base) ⇒ Object
:nodoc:
50 51 52 53 54 55 56 57 58 59 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 50 def self.included(base) #:nodoc: base.class_eval do if ::ActiveRecord::VERSION::MAJOR.to_i < 4 remove_method :indexes end alias_method_chain :rename_table, :schema_plus alias_method_chain :exec_cache, :schema_plus end ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.send(:include, PostgreSQLColumn) unless ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.include?(PostgreSQLColumn) end |
Instance Method Details
#add_index(table_name, column_name, options = {}) ⇒ Object
SchemaPlus provides the following extra options for Postgres indexes:
-
:conditions- SQL conditions for the WHERE clause of the index -
:expression- SQL expression to index. column_name can be nil or ommitted, in which case :name must be provided -
:kind- index method for Postgresql to use -
+:case_sensitive - setting to
falseis 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 since :case_sensitive => false is implemented by using :expression, this raises an ArgumentError if both are specified simultaneously.
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 75 def add_index(table_name, column_name, = {}) column_name, = [], column_name if column_name.is_a?(Hash) column_names = Array(column_name).compact if column_names.empty? raise ArgumentError, "No columns and :expression missing from options - cannot create index" unless [:expression] raise ArgumentError, "Index name not given. Pass :name option" unless [:name] end index_type = [:unique] ? "UNIQUE" : "" index_name = [:name] || index_name(table_name, column_names) conditions = [:conditions] kind = [:kind] if expression = [:expression] then raise ArgumentError, "Cannot specify :case_sensitive => false with an expression. Use LOWER(column_name)" if [:case_sensitive] == false # Wrap expression in parentheses if necessary expression = "(#{expression})" if expression !~ /(using|with|tablespace|where)/i expression = "USING #{kind} #{expression}" if kind expression = "#{expression} WHERE #{conditions}" if conditions sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{expression}" else option_strings = Hash[column_names.map {|name| [name, '']}] option_strings = add_index_sort_order(option_strings, column_names, ) if [:case_sensitive] == false caseable_columns = columns(table_name).select { |col| [:string, :text].include?(col.type) }.map(&:name) quoted_column_names = column_names.map do |col_name| (caseable_columns.include?(col_name.to_s) ? "LOWER(#{quote_column_name(col_name)})" : quote_column_name(col_name)) + option_strings[col_name] end else quoted_column_names = column_names.map { |col_name| quote_column_name(col_name) + option_strings[col_name] } end expression = "(#{quoted_column_names.join(', ')})" expression = "USING #{kind} #{expression}" if kind sql = "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{expression}" sql += " WHERE (#{ ::ActiveRecord::Base.send(:sanitize_sql, conditions, quote_table_name(table_name)) })" if conditions end execute sql rescue => e SchemaStatements.add_index_exception_handler(self, table_name, column_names, , e) end |
#exec_cache_with_schema_plus(sql, binds) ⇒ Object
Prepass to replace each ActiveRecord::DB_DEFAULT with a literal DEFAULT in the sql string. (The underlying pg gem provides no way to bind a value that will replace $n with DEFAULT)
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 195 def exec_cache_with_schema_plus(sql, binds) if binds.any?{ |col, val| val.equal? ::ActiveRecord::DB_DEFAULT} j = 0 binds.each_with_index do |(col, val), i| if val.equal? ::ActiveRecord::DB_DEFAULT sql = sql.sub(/\$#{i+1}/, 'DEFAULT') else sql = sql.sub(/\$#{i+1}/, "$#{j+1}") if i != j j += 1 end end binds = binds.reject{|col, val| val.equal? ::ActiveRecord::DB_DEFAULT} end exec_cache_without_schema_plus(sql, binds) end |
#foreign_keys(table_name, name = nil) ⇒ Object
:nodoc:
211 212 213 214 215 216 217 218 219 220 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 211 def foreign_keys(table_name, name = nil) #:nodoc: load_foreign_keys("SELECT f.conname, pg_get_constraintdef(f.oid), t.relname\n FROM pg_class t, pg_constraint f\n WHERE f.conrelid = t.oid\n AND f.contype = 'f'\n AND t.relname = '\#{table_name_without_namespace(table_name)}'\n AND t.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = \#{namespace_sql(table_name)} )\n SQL\nend\n", name) |
#indexes(table_name, name = nil) ⇒ Object
This method entirely duplicated from AR’s postgresql_adapter.c, but includes the extra bit to determine the column name for a case-insensitive index. (Haven’t come up with any clever way to only code up the case-insensitive column name bit here and otherwise use the existing method.)
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 129 def indexes(table_name, name = nil) #:nodoc: result = query("\n SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,\n m.amname, pg_get_expr(d.indpred, t.oid), pg_get_expr(d.indexprs, t.oid)\n FROM pg_class t\n INNER JOIN pg_index d ON t.oid = d.indrelid\n INNER JOIN pg_class i ON d.indexrelid = i.oid\n INNER JOIN pg_am m ON i.relam = m.oid\n WHERE i.relkind = 'i'\n AND d.indisprimary = 'f'\n AND t.relname = '\#{table_name_without_namespace(table_name)}'\n AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = \#{namespace_sql(table_name)} )\n ORDER BY i.relname\n SQL\n\n result.map do |(index_name, is_unique, indkey, inddef, oid, kind, conditions, expression)|\n unique = (is_unique == 't')\n index_keys = indkey.split(\" \")\n\n columns = Hash[query(<<-SQL, \"Columns for index \#{index_name} on \#{table_name}\")]\n SELECT a.attnum, a.attname\n FROM pg_attribute a\n WHERE a.attrelid = \#{oid}\n AND a.attnum IN (\#{index_keys.join(\",\")})\n SQL\n\n column_names = columns.values_at(*index_keys).compact\n case_sensitive = true\n\n # extract column names from the expression, for a\n # case-insensitive index\n if expression\n rexp_lower = %r{\\blower\\(\\(?([^)]+)(\\)::text)?\\)}\n if expression.match /\\A\#{rexp_lower}(?:, \#{rexp_lower})*\\z/\n case_insensitive_columns = expression.scan(rexp_lower).map(&:first)\n column_names = index_keys.map do |index_key|\n index_key == '0' ? case_insensitive_columns.shift : columns[index_key]\n end\n case_sensitive = false\n end\n end\n \n # add info on sort order for columns (only desc order is explicitly specified, asc is the default)\n desc_order_columns = inddef.scan(/(\\w+) DESC/).flatten\n orders = desc_order_columns.any? ? Hash[column_names.map {|column| [column, desc_order_columns.include?(column) ? :desc : :asc]}] : {}\n\n ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, column_names,\n :name => index_name,\n :unique => unique,\n :orders => orders,\n :conditions => conditions,\n :case_sensitive => case_sensitive,\n :kind => kind.downcase == \"btree\" ? nil : kind,\n :expression => expression)\n end\nend\n", name) |
#rename_table_with_schema_plus(oldname, newname) ⇒ Object
:nodoc:
187 188 189 190 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 187 def rename_table_with_schema_plus(oldname, newname) #:nodoc: rename_table_without_schema_plus(oldname, newname) rename_indexes_and_foreign_keys(oldname, newname) end |
#reverse_foreign_keys(table_name, name = nil) ⇒ Object
:nodoc:
222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 222 def reverse_foreign_keys(table_name, name = nil) #:nodoc: load_foreign_keys("SELECT f.conname, pg_get_constraintdef(f.oid), t2.relname\n FROM pg_class t, pg_class t2, pg_constraint f\n WHERE f.confrelid = t.oid\n AND f.conrelid = t2.oid\n AND f.contype = 'f'\n AND t.relname = '\#{table_name_without_namespace(table_name)}'\n AND t.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = \#{namespace_sql(table_name)} )\n SQL\nend\n", name) |
#supports_partial_indexes? ⇒ Boolean
:nodoc:
120 121 122 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 120 def supports_partial_indexes? #:nodoc: true end |
#view_definition(view_name, name = nil) ⇒ Object
:nodoc:
244 245 246 247 248 249 250 251 252 253 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 244 def view_definition(view_name, name = nil) #:nodoc: result = query("SELECT pg_get_viewdef(oid)\n FROM pg_class\n WHERE relkind = 'v'\n AND relname = '\#{view_name}'\n SQL\n row = result.first\n row.first.chomp(';') unless row.nil?\nend\n", name) |
#views(name = nil) ⇒ Object
:nodoc:
234 235 236 237 238 239 240 241 242 |
# File 'lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb', line 234 def views(name = nil) #:nodoc: sql = " SELECT viewname\n FROM pg_views\n WHERE schemaname = ANY (current_schemas(false))\n SQL\n sql += \" AND schemaname != 'postgis'\" if adapter_name == 'PostGIS'\n query(sql, name).map { |row| row[0] }\nend\n" |