schema_plus 1.8.9 → 2.0.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -4
  3. data/.travis.yml +1 -47
  4. data/CHANGELOG.md +0 -35
  5. data/README.md +73 -107
  6. data/Rakefile +7 -10
  7. data/TODO.md +51 -0
  8. data/gemfiles/Gemfile.base +2 -0
  9. data/lib/schema_column_plus.rb +7 -0
  10. data/lib/{schema_plus → schema_column_plus}/active_record/connection_adapters/column.rb +13 -11
  11. data/lib/schema_column_plus/middleware/model.rb +22 -0
  12. data/lib/schema_db_default.rb +13 -0
  13. data/lib/{schema_plus → schema_db_default}/active_record/attribute.rb +4 -4
  14. data/lib/schema_db_default/db_default.rb +17 -0
  15. data/lib/schema_db_default/middleware.rb +30 -0
  16. data/lib/schema_default_expr.rb +32 -0
  17. data/lib/schema_default_expr/active_record/connection_adapters/mysql_adapter.rb +17 -0
  18. data/lib/schema_default_expr/active_record/connection_adapters/postgresql_adapter.rb +18 -0
  19. data/lib/schema_default_expr/active_record/connection_adapters/sqlite3_adapter.rb +35 -0
  20. data/lib/schema_default_expr/middleware.rb +54 -0
  21. data/lib/schema_pg_enums.rb +6 -0
  22. data/lib/schema_pg_enums/active_record.rb +69 -0
  23. data/lib/schema_pg_enums/middleware.rb +23 -0
  24. data/lib/schema_plus.rb +17 -45
  25. data/lib/schema_plus/active_record/base.rb +6 -23
  26. data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +80 -181
  27. data/lib/schema_plus/active_record/connection_adapters/foreign_key_definition.rb +78 -99
  28. data/lib/schema_plus/active_record/connection_adapters/mysql_adapter.rb +34 -114
  29. data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +16 -370
  30. data/lib/schema_plus/active_record/connection_adapters/schema_statements.rb +1 -67
  31. data/lib/schema_plus/active_record/connection_adapters/sqlite3_adapter.rb +18 -112
  32. data/lib/schema_plus/active_record/connection_adapters/table_definition.rb +14 -116
  33. data/lib/schema_plus/active_record/migration/command_recorder.rb +8 -59
  34. data/lib/schema_plus/middleware/dumper.rb +94 -0
  35. data/lib/schema_plus/middleware/migration.rb +167 -0
  36. data/lib/schema_plus/middleware/model.rb +17 -0
  37. data/lib/schema_plus/version.rb +1 -1
  38. data/lib/schema_plus_tables.rb +15 -0
  39. data/lib/schema_plus_tables/active_record/connection_adapters/abstract_adapter.rb +20 -0
  40. data/lib/schema_plus_tables/active_record/connection_adapters/mysql_adapter.rb +25 -0
  41. data/lib/schema_plus_tables/active_record/connection_adapters/postgresql_adapter.rb +13 -0
  42. data/lib/schema_plus_tables/active_record/connection_adapters/sqlite3_adapter.rb +12 -0
  43. data/lib/schema_views.rb +16 -0
  44. data/lib/schema_views/active_record/connection_adapters/abstract_adapter.rb +41 -0
  45. data/lib/schema_views/active_record/connection_adapters/mysql_adapter.rb +30 -0
  46. data/lib/schema_views/active_record/connection_adapters/postgresql_adapter.rb +31 -0
  47. data/lib/schema_views/active_record/connection_adapters/sqlite3_adapter.rb +18 -0
  48. data/lib/schema_views/middleware.rb +47 -0
  49. data/schema_dev.yml +1 -31
  50. data/schema_plus.gemspec +11 -9
  51. data/spec/foreign_key_definition_spec.rb +7 -7
  52. data/spec/foreign_key_spec.rb +63 -48
  53. data/spec/migration_spec.rb +58 -203
  54. data/spec/named_schemas_spec.rb +5 -88
  55. data/spec/{column_spec.rb → schema_column_plus/column_spec.rb} +26 -48
  56. data/spec/schema_db_default/column_spec.rb +58 -0
  57. data/spec/{column_default_spec.rb → schema_default_expr/column_default_spec.rb} +1 -2
  58. data/spec/schema_default_expr/schema_dumper_spec.rb +116 -0
  59. data/spec/schema_dumper_spec.rb +22 -327
  60. data/spec/{enum_spec.rb → schema_pg_enums/enum_spec.rb} +1 -1
  61. data/spec/schema_pg_enums/schema_dumper_spec.rb +37 -0
  62. data/spec/schema_views/named_schemas_spec.rb +97 -0
  63. data/spec/{views_spec.rb → schema_views/views_spec.rb} +1 -1
  64. data/spec/spec_helper.rb +2 -1
  65. data/spec/support/matchers/reference.rb +11 -12
  66. metadata +104 -57
  67. data/gemfiles/rails-3.2/Gemfile.base +0 -3
  68. data/gemfiles/rails-3.2/Gemfile.mysql +0 -10
  69. data/gemfiles/rails-3.2/Gemfile.mysql2 +0 -10
  70. data/gemfiles/rails-3.2/Gemfile.postgresql +0 -10
  71. data/gemfiles/rails-3.2/Gemfile.sqlite3 +0 -10
  72. data/gemfiles/rails-4.0/Gemfile.base +0 -3
  73. data/gemfiles/rails-4.0/Gemfile.mysql2 +0 -10
  74. data/gemfiles/rails-4.0/Gemfile.postgresql +0 -10
  75. data/gemfiles/rails-4.0/Gemfile.sqlite3 +0 -10
  76. data/gemfiles/rails-4.1/Gemfile.base +0 -3
  77. data/gemfiles/rails-4.1/Gemfile.mysql2 +0 -10
  78. data/gemfiles/rails-4.1/Gemfile.postgresql +0 -10
  79. data/gemfiles/rails-4.1/Gemfile.sqlite3 +0 -10
  80. data/lib/schema_plus/active_record/column_options_handler.rb +0 -117
  81. data/lib/schema_plus/active_record/connection_adapters/index_definition.rb +0 -70
  82. data/lib/schema_plus/active_record/db_default.rb +0 -19
  83. data/lib/schema_plus/active_record/foreign_keys.rb +0 -137
  84. data/lib/schema_plus/active_record/schema_dumper.rb +0 -171
  85. data/lib/schema_plus/railtie.rb +0 -20
  86. data/spec/index_definition_spec.rb +0 -211
  87. data/spec/index_spec.rb +0 -249
@@ -1,171 +0,0 @@
1
- require 'tsort'
2
-
3
- module SchemaPlus
4
- module ActiveRecord
5
-
6
- # SchemaPlus modifies ActiveRecord's schema dumper to include foreign
7
- # key constraints and views.
8
- #
9
- # Additionally, index and foreign key constraint definitions are dumped
10
- # inline in the create_table block. (This is done for elegance, but
11
- # also because Sqlite3 does not allow foreign key constraints to be
12
- # added to a table after it has been defined.)
13
- #
14
- # The tables and views are dumped in alphabetical order, subject to
15
- # topological sort constraints that a table must be dumped before any
16
- # view that references it or table that has a foreign key constaint to
17
- # it.
18
- #
19
- module SchemaDumper
20
-
21
- include TSort
22
-
23
- def self.included(base) #:nodoc:
24
- base.class_eval do
25
- private
26
- alias_method_chain :table, :schema_plus
27
- alias_method_chain :tables, :schema_plus
28
- alias_method_chain :indexes, :schema_plus
29
- alias_method_chain :foreign_keys, :schema_plus if private_method_defined? :foreign_keys
30
- end
31
- end
32
-
33
- private
34
-
35
- def foreign_keys_with_schema_plus(*)
36
- # do nothing. this overrides AR 4.2's foreign key dumping method, which isn't needed
37
- # because we're dong them inline
38
- end
39
-
40
- def break_fk_cycles #:nodoc:
41
- strongly_connected_components.select{|component| component.size > 1}.each do |tables|
42
- table = tables.sort.last
43
- backref_fks = @inline_fks[table].select{|fk| tables.include?(fk.references_table_name)}
44
- @inline_fks[table] -= backref_fks
45
- @dump_dependencies[table] -= backref_fks.collect(&:references_table_name)
46
- backref_fks.each do |fk|
47
- @backref_fks[fk.references_table_name] << fk
48
- end
49
- end
50
- end
51
-
52
- def tables_with_schema_plus(stream) #:nodoc:
53
- @table_dumps = {}
54
- @inline_fks = Hash.new{ |h, k| h[k] = [] }
55
- @backref_fks = Hash.new{ |h, k| h[k] = [] }
56
- @dump_dependencies = {}
57
-
58
- if @connection.respond_to?(:enums)
59
- @connection.enums.each do |schema, name, values|
60
- params = [name.inspect]
61
- params << values.map(&:inspect).join(', ')
62
- params << ":schema => #{schema.inspect}" if schema != 'public'
63
-
64
- stream.puts " create_enum #{params.join(', ')}"
65
- end
66
- end
67
-
68
- if "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r < "4.2".to_r
69
- tables_without_schema_plus(nil)
70
- else
71
- tables_without_schema_plus(stream)
72
- end
73
-
74
- @connection.views.each do |view_name|
75
- next if Array.wrap(::ActiveRecord::SchemaDumper.ignore_tables).any? {|pattern| view_name.match pattern}
76
- definition = @connection.view_definition(view_name)
77
- @table_dumps[view_name] = " create_view #{view_name.inspect}, #{definition.inspect}, :force => true\n"
78
- end
79
-
80
- re_view_referent = %r{(?:(?i)FROM|JOIN) \S*\b(#{(@table_dumps.keys).join('|')})\b}
81
- @table_dumps.keys.each do |table|
82
- if @connection.views.include?(table)
83
- dependencies = @connection.view_definition(table).scan(re_view_referent).flatten
84
- else
85
- @inline_fks[table] = @connection.foreign_keys(table)
86
- dependencies = @inline_fks[table].collect(&:references_table_name)
87
- end
88
- # select against @table_dumps keys to respect filtering based on
89
- # SchemaDumper.ignore_tables (which was taken into account
90
- # increate @table_dumps)
91
- @dump_dependencies[table] = dependencies.sort.uniq.select {|name| @table_dumps.has_key? name}
92
- end
93
-
94
- # Normally we dump foreign key constraints inline in the table
95
- # definitions, both for visual cleanliness and because sqlite3
96
- # doesn't allow foreign key constraints to be added afterwards.
97
- # But in case there's a cycle in the constraint references, some
98
- # constraints will need to be broken out then added later. (Adding
99
- # constraints later won't work with sqlite3, but that means sqlite3
100
- # won't let you create cycles in the first place.)
101
- break_fk_cycles while strongly_connected_components.any?{|component| component.size > 1}
102
-
103
- tsort().each do |table|
104
- table_dump = @table_dumps[table]
105
- if i = (table_dump =~ /^\s*[e]nd\s*$/)
106
- table_dump.insert i, dump_indexes(table) + dump_foreign_keys(@inline_fks[table], :inline => true)
107
- end
108
- stream.print table_dump
109
- stream.puts dump_foreign_keys(@backref_fks[table], :inline => false)+"\n" if @backref_fks[table].any?
110
- end
111
-
112
- end
113
-
114
- def tsort_each_node(&block) #:nodoc:
115
- @table_dumps.keys.sort.each(&block)
116
- end
117
-
118
- def tsort_each_child(table, &block) #:nodoc:
119
- @dump_dependencies[table].each(&block)
120
- end
121
-
122
- def table_with_schema_plus(table, ignore) #:nodoc:
123
- stream = StringIO.new
124
- table_without_schema_plus(table, stream)
125
- stream_string = stream.string
126
- @connection.columns(table).each do |column|
127
- if "#{::ActiveRecord::VERSION::MAJOR}.#{::ActiveRecord::VERSION::MINOR}".to_r < "4.2".to_r
128
- if !column.default_expr.nil?
129
- stream_string.gsub!("\"#{column.name}\"", "\"#{column.name}\", :default => { :expr => #{column.default_expr.inspect} }")
130
- end
131
- else
132
- if !column.default_function.nil?
133
- stream_string.gsub!("\"#{column.name}\"", "\"#{column.name}\", :default => { :expr => #{column.default_function.inspect} }")
134
- end
135
- end
136
- end
137
- @table_dumps[table] = stream_string
138
- end
139
-
140
- def indexes_with_schema_plus(table, stream) #:nodoc:
141
- # do nothing. we've already taken care of indexes as part of
142
- # dumping the tables
143
- end
144
-
145
- def dump_indexes(table) #:nodoc:
146
- @connection.indexes(table).collect{ |index|
147
- dump = " t.index"
148
- dump << " #{index.columns.inspect}," unless index.columns.blank?
149
- dump << " :name => #{index.name.inspect}"
150
- dump << ", :unique => true" if index.unique
151
- dump << ", :kind => \"#{index.kind}\"" unless index.kind.blank?
152
- unless index.columns.blank?
153
- dump << ", :case_sensitive => false" unless index.case_sensitive?
154
- dump << ", :conditions => #{index.conditions.inspect}" unless index.conditions.blank?
155
- index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
156
- dump << ", :length => #{Hash[*index.columns.zip(index.lengths).flatten].inspect}" if index_lengths.present?
157
- dump << ", :order => {" + index.orders.map{|column, val| "#{column.inspect} => #{val.inspect}"}.join(", ") + "}" unless index.orders.blank?
158
- dump << ", :operator_class => {" + index.operator_classes.map{|column, val| "#{column.inspect} => #{val.inspect}"}.join(", ") + "}" unless index.operator_classes.blank?
159
- else
160
- dump << ", :expression => #{index.expression.inspect}"
161
- end
162
- dump << "\n"
163
- }.sort.join
164
- end
165
-
166
- def dump_foreign_keys(foreign_keys, opts={}) #:nodoc:
167
- foreign_keys.collect{ |foreign_key| " " + foreign_key.to_dump(:inline => opts[:inline]) }.sort.join
168
- end
169
- end
170
- end
171
- end
@@ -1,20 +0,0 @@
1
- module SchemaPlus
2
- class Railtie < Rails::Railtie #:nodoc:
3
-
4
- initializer 'schema_plus.insert', :before => "active_record.initialize_database" do
5
- ActiveSupport.on_load(:active_record) do
6
- SchemaPlus.insert
7
- end
8
- end
9
-
10
- rake_tasks do
11
- load 'rails/tasks/database.rake'
12
- ['db:schema:dump', 'db:schema:load'].each do |name|
13
- if task = Rake.application.tasks.find { |task| task.name == name }
14
- task.enhance(["schema_plus:load"])
15
- end
16
- end
17
- end
18
-
19
- end
20
- end
@@ -1,211 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
-
4
- describe "Index definition" do
5
-
6
- let(:migration) { ::ActiveRecord::Migration }
7
-
8
- before(:all) do
9
- define_schema(:auto_create => false) do
10
- create_table :users, :force => true do |t|
11
- t.string :login
12
- t.datetime :deleted_at
13
- end
14
-
15
- create_table :posts, :force => true do |t|
16
- t.text :body
17
- t.integer :user_id
18
- t.integer :author_id
19
- end
20
-
21
- create_table :comments, :force => true do |t|
22
- t.text :body
23
- t.integer :post_id
24
- t.foreign_key :post_id, :posts, :id
25
- end
26
- end
27
- class User < ::ActiveRecord::Base ; end
28
- class Post < ::ActiveRecord::Base ; end
29
- class Comment < ::ActiveRecord::Base ; end
30
- end
31
-
32
- around(:each) do |example|
33
- migration.suppress_messages do
34
- example.run
35
- end
36
- end
37
-
38
- after(:each) do
39
- migration.remove_index :users, :name => 'users_login_index' if migration.index_name_exists? :users, 'users_login_index', true
40
- end
41
-
42
- context "when index is multicolumn" do
43
- before(:each) do
44
- migration.execute "CREATE INDEX users_login_index ON users (login, deleted_at)"
45
- User.reset_column_information
46
- @index = index_definition(%w[login deleted_at])
47
- end
48
-
49
- it "is included in User.indexes" do
50
- expect(@index).not_to be_nil
51
- end
52
-
53
- end
54
-
55
- it "should correctly report supports_partial_indexes?" do
56
- query = lambda { migration.execute "CREATE INDEX users_login_index ON users(login) WHERE deleted_at IS NULL" }
57
- if migration.supports_partial_indexes?
58
- expect(query).not_to raise_error
59
- else
60
- expect(query).to raise_error
61
- end
62
- end
63
-
64
- it "should not crash on equality test with nil" do
65
- index = ActiveRecord::ConnectionAdapters::IndexDefinition.new(:table, :column)
66
- expect{index == nil}.to_not raise_error
67
- expect(index == nil).to be false
68
- end
69
-
70
-
71
- context "when index is ordered", :mysql => :skip do
72
-
73
- quotes = [
74
- ["unquoted", ''],
75
- ["double-quoted", '"'],
76
- ]
77
- quotes += [
78
- ["single-quoted", "'"],
79
- ["back-quoted", '`']
80
- ] if SchemaDev::Rspec::Helpers.sqlite3?
81
-
82
- quotes.each do |quotename, quote|
83
- it "index definition includes orders for #{quotename} columns" do
84
- migration.execute "CREATE INDEX users_login_index ON users (#{quote}login#{quote} DESC, #{quote}deleted_at#{quote} ASC)"
85
- User.reset_column_information
86
- index = index_definition(%w[login deleted_at])
87
- expect(index.orders).to eq({"login" => :desc, "deleted_at" => :asc})
88
- end
89
-
90
- end
91
- end
92
-
93
-
94
- context "when case insensitive is added", :postgresql => :only do
95
-
96
- before(:each) do
97
- migration.execute "CREATE INDEX users_login_index ON users(LOWER(login))"
98
- User.reset_column_information
99
- @index = User.indexes.detect { |i| i.expression =~ /lower\(\(login\)::text\)/i }
100
- end
101
-
102
- it "is included in User.indexes" do
103
- expect(@index).not_to be_nil
104
- end
105
-
106
- it "is not case_sensitive" do
107
- expect(@index).not_to be_case_sensitive
108
- end
109
-
110
- it "its column should not be case sensitive" do
111
- expect(User.columns.find{|column| column.name == "login"}).not_to be_case_sensitive
112
- end
113
-
114
- it "defines expression" do
115
- expect(@index.expression).to eq("lower((login)::text)")
116
- end
117
-
118
- it "doesn't define conditions" do
119
- expect(@index.conditions).to be_nil
120
- end
121
-
122
- end
123
-
124
-
125
- context "when index is partial" do
126
- before(:each) do
127
- migration.execute "CREATE INDEX users_login_index ON users(login) WHERE deleted_at IS NULL"
128
- User.reset_column_information
129
- @index = index_definition("login")
130
- end
131
-
132
- it "is included in User.indexes" do
133
- expect(User.indexes.select { |index| index.columns == ["login"] }.size).to eq(1)
134
- end
135
-
136
- it "is case_sensitive" do
137
- expect(@index).to be_case_sensitive
138
- end
139
-
140
- it "doesn't define expression" do
141
- expect(@index.expression).to be_nil
142
- end
143
-
144
- it "defines conditions" do
145
- expect(@index.conditions).to match %r{[(]?deleted_at IS NULL[)]?}
146
- end
147
-
148
- end if ::ActiveRecord::Migration.supports_partial_indexes?
149
-
150
- context "when index contains expression", :postgresql => :only do
151
- before(:each) do
152
- migration.execute "CREATE INDEX users_login_index ON users (extract(EPOCH from deleted_at)) WHERE deleted_at IS NULL"
153
- User.reset_column_information
154
- @index = User.indexes.detect { |i| i.expression.present? }
155
- end
156
-
157
- it "exists" do
158
- expect(@index).not_to be_nil
159
- end
160
-
161
- it "doesnt have columns defined" do
162
- expect(@index.columns).to be_empty
163
- end
164
-
165
- it "is case_sensitive" do
166
- expect(@index).to be_case_sensitive
167
- end
168
-
169
- it "defines expression" do
170
- expect(@index.expression).to eq("date_part('epoch'::text, deleted_at)")
171
- end
172
-
173
- it "defines conditions" do
174
- expect(@index.conditions).to eq("(deleted_at IS NULL)")
175
- end
176
-
177
- end
178
-
179
- context "when index has a non-btree type", :postgresql => :only do
180
- before(:each) do
181
- migration.execute "CREATE INDEX users_login_index ON users USING hash(login)"
182
- User.reset_column_information
183
- @index = User.indexes.detect { |i| i.name == "users_login_index" }
184
- end
185
-
186
- it "exists" do
187
- expect(@index).not_to be_nil
188
- end
189
-
190
- it "defines kind" do
191
- expect(@index.kind).to eq("hash")
192
- end
193
-
194
- it "does not define expression" do
195
- expect(@index.expression).to be_nil
196
- end
197
-
198
- it "does not define order" do
199
- expect(@index.orders).to be_blank
200
- end
201
- end
202
-
203
-
204
-
205
- protected
206
- def index_definition(column_names)
207
- User.indexes.detect { |index| index.columns == Array(column_names) }
208
- end
209
-
210
-
211
- end
@@ -1,249 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- describe "index" do
4
-
5
- let(:migration) { ::ActiveRecord::Migration }
6
- let(:connection) { ::ActiveRecord::Base.connection }
7
-
8
- describe "add_index" do
9
-
10
- before(:each) do
11
- connection.tables.each do |table| connection.drop_table table, cascade: true end
12
-
13
- define_schema(:auto_create => false) do
14
- create_table :users, :force => true do |t|
15
- t.string :login
16
- t.text :address
17
- t.datetime :deleted_at
18
- end
19
-
20
- create_table :posts, :force => true do |t|
21
- t.text :body
22
- t.integer :user_id
23
- t.integer :author_id
24
- end
25
-
26
- create_table :comments, :force => true do |t|
27
- t.text :body
28
- t.integer :post_id
29
- t.foreign_key :post_id, :posts, :id
30
- end
31
- end
32
- class User < ::ActiveRecord::Base ; end
33
- class Post < ::ActiveRecord::Base ; end
34
- class Comment < ::ActiveRecord::Base ; end
35
- end
36
-
37
-
38
- after(:each) do
39
- migration.suppress_messages do
40
- migration.remove_index(:users, :name => @index.name) if (@index ||= nil)
41
- end
42
- end
43
-
44
- it "should create index when called without additional options" do
45
- add_index(:users, :login)
46
- expect(index_for(:login)).not_to be_nil
47
- end
48
-
49
- it "should create unique index" do
50
- add_index(:users, :login, :unique => true)
51
- expect(index_for(:login).unique).to eq(true)
52
- end
53
-
54
- it "should assign given name" do
55
- add_index(:users, :login, :name => 'users_login_index')
56
- expect(index_for(:login).name).to eq('users_login_index')
57
- end
58
-
59
- it "should assign order", :mysql => :skip do
60
- add_index(:users, [:login, :deleted_at], :order => {:login => :desc, :deleted_at => :asc})
61
- expect(index_for([:login, :deleted_at]).orders).to eq({"login" => :desc, "deleted_at" => :asc})
62
- end
63
-
64
- it "should respect algorithm: :concurrently", :postgresql => :only do
65
- expect(connection).to receive(:execute).with(/CREATE INDEX CONCURRENTLY/)
66
- add_index(:users, :login, :algorithm => :concurrently)
67
- end if ActiveRecord::VERSION::MAJOR > 3
68
-
69
- context "for duplicate index" do
70
- it "should not complain if the index is the same" do
71
- add_index(:users, :login)
72
- expect(index_for(:login)).not_to be_nil
73
- expect(ActiveRecord::Base.logger).to receive(:warn).with(/login.*Skipping/)
74
- expect { add_index(:users, :login) }.to_not raise_error
75
- expect(index_for(:login)).not_to be_nil
76
- end
77
- it "should complain if the index is different" do
78
- add_index(:users, :login, :unique => true)
79
- expect(index_for(:login)).not_to be_nil
80
- expect { add_index(:users, :login) }.to raise_error
81
- expect(index_for(:login)).not_to be_nil
82
- end
83
- end
84
-
85
- context "extra features", :postgresql => :only do
86
-
87
- it "should assign conditions" do
88
- add_index(:users, :login, :conditions => 'deleted_at IS NULL')
89
- expect(index_for(:login).conditions).to eq('(deleted_at IS NULL)')
90
- end
91
-
92
- it "should assign expression, conditions and kind" do
93
- add_index(:users, :expression => "USING hash (upper(login)) WHERE deleted_at IS NULL", :name => 'users_login_index')
94
- @index = User.indexes.detect { |i| i.expression.present? }
95
- expect(@index.expression).to eq("upper((login)::text)")
96
- expect(@index.conditions).to eq("(deleted_at IS NULL)")
97
- expect(@index.kind).to eq("hash")
98
- end
99
-
100
- it "should allow to specify expression, conditions and kind separately" do
101
- add_index(:users, :kind => "hash", :expression => "upper(login)", :conditions => "deleted_at IS NULL", :name => 'users_login_index')
102
- @index = User.indexes.detect { |i| i.expression.present? }
103
- expect(@index.expression).to eq("upper((login)::text)")
104
- expect(@index.conditions).to eq("(deleted_at IS NULL)")
105
- expect(@index.kind).to eq("hash")
106
- end
107
-
108
- it "should allow to specify kind" do
109
- add_index(:users, :login, :kind => "hash")
110
- expect(index_for(:login).kind).to eq('hash')
111
- end
112
-
113
- it "should assign operator_class" do
114
- add_index(:users, :login, :operator_class => 'varchar_pattern_ops')
115
- expect(index_for(:login).operator_classes).to eq({"login" => 'varchar_pattern_ops'})
116
- end
117
-
118
- it "should assign multiple operator_classes" do
119
- add_index(:users, [:login, :address], :operator_class => {:login => 'varchar_pattern_ops', :address => 'text_pattern_ops'})
120
- expect(index_for([:login, :address]).operator_classes).to eq({"login" => 'varchar_pattern_ops', "address" => 'text_pattern_ops'})
121
- end
122
-
123
- it "should allow to specify actual expression only" do
124
- add_index(:users, :expression => "upper(login)", :name => 'users_login_index')
125
- @index = User.indexes.detect { |i| i.name == 'users_login_index' }
126
- expect(@index.expression).to eq("upper((login)::text)")
127
- end
128
-
129
- it "should raise if no column given and expression is missing" do
130
- expect { add_index(:users, :name => 'users_login_index') }.to raise_error(ArgumentError, /expression/)
131
- end
132
-
133
- it "should raise if expression without name is given" do
134
- expect { add_index(:users, :expression => "USING btree (login)") }.to raise_error(ArgumentError, /name/)
135
- end
136
-
137
- it "should raise if expression is given and case_sensitive is false" do
138
- expect { add_index(:users, :name => 'users_login_index', :expression => "USING btree (login)", :case_sensitive => false) }.to raise_error(ArgumentError, /use LOWER/i)
139
- end
140
-
141
- end
142
-
143
- protected
144
-
145
- def index_for(column_names)
146
- @index = User.indexes.detect { |i| i.columns == Array(column_names).collect(&:to_s) }
147
- end
148
-
149
- end
150
-
151
- describe "remove_index" do
152
-
153
- before(:each) do
154
- connection.tables.each do |table| connection.drop_table table, cascade: true end
155
- define_schema(:auto_create => false) do
156
- create_table :users, :force => true do |t|
157
- t.string :login
158
- t.datetime :deleted_at
159
- end
160
- end
161
- class User < ::ActiveRecord::Base ; end
162
- end
163
-
164
-
165
- it "removes index by column name (symbols)" do
166
- add_index :users, :login
167
- expect(User.indexes.length).to eq(1)
168
- remove_index :users, :login
169
- expect(User.indexes.length).to eq(0)
170
- end
171
-
172
- it "removes index by column name (symbols)" do
173
- add_index :users, :login
174
- expect(User.indexes.length).to eq(1)
175
- remove_index 'users', 'login'
176
- expect(User.indexes.length).to eq(0)
177
- end
178
-
179
- it "removes multi-column index by column names (symbols)" do
180
- add_index :users, [:login, :deleted_at]
181
- expect(User.indexes.length).to eq(1)
182
- remove_index :users, [:login, :deleted_at]
183
- expect(User.indexes.length).to eq(0)
184
- end
185
-
186
- it "removes multi-column index by column names (strings)" do
187
- add_index 'users', [:login, :deleted_at]
188
- expect(User.indexes.length).to eq(1)
189
- remove_index 'users', ['login', 'deleted_at']
190
- expect(User.indexes.length).to eq(0)
191
- end
192
-
193
- it "removes index using column option" do
194
- add_index :users, :login
195
- expect(User.indexes.length).to eq(1)
196
- remove_index :users, column: :login
197
- expect(User.indexes.length).to eq(0)
198
- end
199
-
200
- it "removes index if_exists" do
201
- add_index :users, :login
202
- expect(User.indexes.length).to eq(1)
203
- remove_index :users, :login, :if_exists => true
204
- expect(User.indexes.length).to eq(0)
205
- end
206
-
207
- it "removes multi-column index if exists" do
208
- add_index :users, [:login, :deleted_at]
209
- expect(User.indexes.length).to eq(1)
210
- remove_index :users, [:login, :deleted_at], :if_exists => true
211
- expect(User.indexes.length).to eq(0)
212
- end
213
-
214
- it "removes index if_exists using column option" do
215
- add_index :users, :login
216
- expect(User.indexes.length).to eq(1)
217
- remove_index :users, column: :login, :if_exists => true
218
- expect(User.indexes.length).to eq(0)
219
- end
220
-
221
- it "raises exception if doesn't exist" do
222
- expect {
223
- remove_index :users, :login
224
- }.to raise_error
225
- end
226
-
227
- it "doesn't raise exception with :if_exists" do
228
- expect {
229
- remove_index :users, :login, :if_exists => true
230
- }.to_not raise_error
231
- end
232
- end
233
-
234
- protected
235
- def add_index(*args)
236
- migration.suppress_messages do
237
- migration.add_index(*args)
238
- end
239
- User.reset_column_information
240
- end
241
-
242
- def remove_index(*args)
243
- migration.suppress_messages do
244
- migration.remove_index(*args)
245
- end
246
- User.reset_column_information
247
- end
248
-
249
- end