schema_plus 1.5.3 → 1.6.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +8 -8
- data/CHANGELOG.md +10 -4
- data/README.md +24 -4
- data/Rakefile +1 -1
- data/gemfiles/rails-4.1/Gemfile.base +1 -1
- data/lib/schema_plus/active_record/connection_adapters/abstract_adapter.rb +11 -5
- data/lib/schema_plus/active_record/connection_adapters/postgresql_adapter.rb +58 -2
- data/lib/schema_plus/active_record/schema_dumper.rb +10 -0
- data/lib/schema_plus/version.rb +1 -1
- data/runspecs +1 -1
- data/spec/column_default_spec.rb +20 -26
- data/spec/column_spec.rb +22 -24
- data/spec/connections/postgresql/connection.rb +1 -1
- data/spec/enum_spec.rb +132 -0
- data/spec/foreign_key_spec.rb +18 -15
- data/spec/index_definition_spec.rb +97 -102
- data/spec/index_spec.rb +5 -8
- data/spec/migration_spec.rb +288 -303
- data/spec/named_schemas_spec.rb +24 -26
- data/spec/schema_dumper_spec.rb +66 -53
- data/spec/spec_helper.rb +6 -0
- data/spec/views_spec.rb +41 -24
- metadata +24 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 620224f986e74b013fddffb7147eecfabe3f27bd
|
4
|
+
data.tar.gz: 323fdba9ed583753abfbdaee67353391356213f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8510bd7c390e12f5e4f2c113395dcde1831e46c9b525a90bb9a5cb2a4e48095840467e03dcac5229fdcab72abc11089d3d14c72ef535c6751fed159578f41bd6
|
7
|
+
data.tar.gz: 47df08a80fb0257e6cc1fa632005517685424af5f717ad847ef817b579288803c36e14f1cf2a96c7bb9442e288aaae08cd328e16ca0bfadda8a47f0910d8aa13
|
data/.travis.yml
CHANGED
@@ -18,7 +18,7 @@ before_script:
|
|
18
18
|
- rake create_databases
|
19
19
|
after_script:
|
20
20
|
- rake drop_databases
|
21
|
-
env: '
|
21
|
+
env: 'POSTGRESQL_DB_USER=postgres MYSQL_DB_USER="travis"'
|
22
22
|
notifications:
|
23
23
|
recipients:
|
24
24
|
- michal.lomnicki@gmail.com
|
@@ -27,22 +27,22 @@ matrix:
|
|
27
27
|
exclude:
|
28
28
|
- rvm: jruby
|
29
29
|
gemfile: gemfiles/rails-3.2/Gemfile.sqlite3
|
30
|
-
env: '
|
30
|
+
env: 'POSTGRESQL_DB_USER=postgres MYSQL_DB_USER="travis"'
|
31
31
|
- rvm: jruby
|
32
32
|
gemfile: gemfiles/rails-4.0/Gemfile.postgresql
|
33
|
-
env: '
|
33
|
+
env: 'POSTGRESQL_DB_USER=postgres MYSQL_DB_USER="travis"'
|
34
34
|
- rvm: jruby
|
35
35
|
gemfile: gemfiles/rails-4.0/Gemfile.sqlite3
|
36
|
-
env: '
|
36
|
+
env: 'POSTGRESQL_DB_USER=postgres MYSQL_DB_USER="travis"'
|
37
37
|
- rvm: jruby
|
38
38
|
gemfile: gemfiles/rails-4.0/Gemfile.mysql2
|
39
|
-
env: '
|
39
|
+
env: 'POSTGRESQL_DB_USER=postgres MYSQL_DB_USER="travis"'
|
40
40
|
- rvm: jruby
|
41
41
|
gemfile: gemfiles/rails-4.1/Gemfile.postgresql
|
42
|
-
env: '
|
42
|
+
env: 'POSTGRESQL_DB_USER=postgres MYSQL_DB_USER="travis"'
|
43
43
|
- rvm: jruby
|
44
44
|
gemfile: gemfiles/rails-4.1/Gemfile.sqlite3
|
45
|
-
env: '
|
45
|
+
env: 'POSTGRESQL_DB_USER=postgres MYSQL_DB_USER="travis"'
|
46
46
|
- rvm: jruby
|
47
47
|
gemfile: gemfiles/rails-4.1/Gemfile.mysql2
|
48
|
-
env: '
|
48
|
+
env: 'POSTGRESQL_DB_USER=postgres MYSQL_DB_USER="travis"'
|
data/CHANGELOG.md
CHANGED
@@ -2,12 +2,18 @@
|
|
2
2
|
|
3
3
|
## Change Log
|
4
4
|
|
5
|
+
### 1.6.0
|
6
|
+
|
7
|
+
* Added PostgreSQL enum support (thanks to [@juike](https://github.com/juike)) (issue #167)
|
8
|
+
* Added if_exists to drop_view (thanks to [@abrom](https://github.com/abrom)) (issue #171)
|
9
|
+
* Partial support for AR 4.* with jRuby (thanks to [@bacrossland])) (issue #172)
|
10
|
+
|
5
11
|
### 1.5.3
|
6
12
|
|
7
13
|
* No longer limited to rails 4.1.1 (issue #159)
|
8
14
|
* Bug fix: multiple competing indexes created for `t.references... index: :unique` (issue #157)
|
9
|
-
* Now works with rspec 3 (thanks to [@robababa](https://github.com/robababa) (issue #160)
|
10
|
-
* Improvements to ./runspecs (thanks to [@robababa](https://github.com/robababa) (issue #162)
|
15
|
+
* Now works with rspec 3 (thanks to [@robababa](https://github.com/robababa)) (issue #160)
|
16
|
+
* Improvements to ./runspecs (thanks to [@robababa](https://github.com/robababa)) (issue #162)
|
11
17
|
|
12
18
|
### 1.5.2
|
13
19
|
|
@@ -19,7 +25,7 @@
|
|
19
25
|
|
20
26
|
### 1.5.0
|
21
27
|
* Can now be used with activerecord standalone, doesn't need all of rails.
|
22
|
-
* `views` ignores
|
28
|
+
* `views` ignores PostgreSQL internal views, thanks to [@everplays](https://github.com/everplays) (issue #147)
|
23
29
|
|
24
30
|
### 1.4.1
|
25
31
|
|
@@ -103,7 +109,7 @@
|
|
103
109
|
[@zaadjis](https://github.com/zaadjis))
|
104
110
|
* New feature: renaming a table renames its indexes and constraints
|
105
111
|
correspondingly.
|
106
|
-
* Bug fix for
|
112
|
+
* Bug fix for PostgreSQL :kind index attribute (thanks to [@eugenebolshakov](https://github.com/eugenebolshakov))
|
107
113
|
* Sort fks in dump for stability (thanks to [@zephyr-dev](https://github.com/zephyr-dev))
|
108
114
|
* Bug fix: change_column should maintain foreign key constraints even when
|
109
115
|
config.foreign_keys.auto_create is false
|
data/README.md
CHANGED
@@ -211,7 +211,7 @@ a view can be created using a rails relation or literal sql:
|
|
211
211
|
And can be dropped:
|
212
212
|
|
213
213
|
drop_view :posts_commented_by_staff
|
214
|
-
drop_view :uncommented_posts
|
214
|
+
drop_view :uncommented_posts, :if_exists => true
|
215
215
|
|
216
216
|
ActiveRecord works with views the same as with ordinary tables. That is, for
|
217
217
|
the above views you can define
|
@@ -223,7 +223,7 @@ the above views you can define
|
|
223
223
|
class UncommentedPost < ActiveRecord::Base
|
224
224
|
end
|
225
225
|
|
226
|
-
Note: In
|
226
|
+
Note: In PostgreSQL, all internal views (the ones with `pg_` prefix) will be skipped.
|
227
227
|
|
228
228
|
### Column Defaults: Expressions
|
229
229
|
|
@@ -267,6 +267,26 @@ Note that after updating, you would need to reload a record to replace
|
|
267
267
|
Note also that Sqlite3 does not support `ActiveRecord::DB_DEFAULT`; attempting
|
268
268
|
to use it will raise `ActiveRecord::StatementInvalid`
|
269
269
|
|
270
|
+
### Enums (PostgreSQL only)
|
271
|
+
|
272
|
+
SchemaPlus provides support for creating, altering and dropping enums. In a migration,
|
273
|
+
a enum can be created:
|
274
|
+
|
275
|
+
create_enum :color, :red, :green, :blue # default schema is 'public'
|
276
|
+
create_enum :cmyk, :cyan, :magenta, :yellow, :black, :schema => 'color'
|
277
|
+
|
278
|
+
And can be altered: (added a new value)
|
279
|
+
|
280
|
+
alter_enum :color, :black
|
281
|
+
alter_enum :color, :purple, :after => 'red'
|
282
|
+
alter_enum :color, :pink, :before => 'purple'
|
283
|
+
alter_enum :color, :white, :schema => 'public'
|
284
|
+
|
285
|
+
Finally, a enum can be dropped:
|
286
|
+
|
287
|
+
drop_enum :color
|
288
|
+
drop_enum :cmyk, :schema => 'color'
|
289
|
+
|
270
290
|
### Schema Dump and Load (schema.rb)
|
271
291
|
|
272
292
|
When dumping `schema.rb`, SchemaPlus orders the views and tables in the schema
|
@@ -316,9 +336,9 @@ Schema_plus has a full set of rspec tests. [travis-ci](http://travis-ci.org/lom
|
|
316
336
|
|
317
337
|
* Of course you must have installed whichever databases you want to test. The default set is: PostgreSQL, MySQL, and SQLite3.
|
318
338
|
|
319
|
-
* For PostgreSQL and MySQL the tests need a db user with permissions to create and access databases: The default username used by the specs is 'postgres' for
|
339
|
+
* For PostgreSQL and MySQL the tests need a db user with permissions to create and access databases: The default username used by the specs is 'postgres' for PostgreSQL and 'schema_plus' for MySQL; you can change them via:
|
320
340
|
|
321
|
-
$ export
|
341
|
+
$ export POSTGRESQL_DB_USER = pgusername
|
322
342
|
$ export MYSQL_DB_USER = mysqlusername
|
323
343
|
|
324
344
|
* For PostgreSQL and MySQL you must explicitly create the databases used by the tests:
|
data/Rakefile
CHANGED
@@ -37,7 +37,7 @@ end
|
|
37
37
|
|
38
38
|
DATABASES = %w[schema_plus_test]
|
39
39
|
[
|
40
|
-
{ namespace: :postgresql, uservar: '
|
40
|
+
{ namespace: :postgresql, uservar: 'POSTGRESQL_DB_USER', defaultuser: 'postgres', create: "createdb -U '%{user}' %{dbname}", drop: "dropdb -U '%{user}' %{dbname}" },
|
41
41
|
{ namespace: :mysql, uservar: 'MYSQL_DB_USER', defaultuser: 'schema_plus', create: "mysqladmin -u '%{user}' create %{dbname}", drop: "mysqladmin -u '%{user}' -f drop %{dbname}" }
|
42
42
|
].each do |db|
|
43
43
|
namespace db[:namespace] do
|
@@ -27,8 +27,10 @@ module SchemaPlus
|
|
27
27
|
when 'PostgreSQL', 'PostGIS' then 'PostgresqlAdapter'
|
28
28
|
when 'SQLite' then 'Sqlite3Adapter'
|
29
29
|
end
|
30
|
-
|
31
|
-
|
30
|
+
if adapter.nil?
|
31
|
+
unless adapter_name == 'JDBC' # ARJDBC
|
32
|
+
::ActiveRecord::Base.logger.warn "SchemaPlus: Unsupported adapter name #{adapter_name.inspect}. Leaving it alone."
|
33
|
+
end
|
32
34
|
return
|
33
35
|
end
|
34
36
|
adapter_module = SchemaPlus::ActiveRecord::ConnectionAdapters.const_get(adapter)
|
@@ -51,9 +53,13 @@ module SchemaPlus
|
|
51
53
|
execute "CREATE VIEW #{quote_table_name(view_name)} AS #{definition}"
|
52
54
|
end
|
53
55
|
|
54
|
-
# Drop the named view
|
55
|
-
|
56
|
-
|
56
|
+
# Drop the named view. Specify :if_exists => true
|
57
|
+
# to fail silently if the view doesn't exist.
|
58
|
+
def drop_view(view_name, options = {})
|
59
|
+
sql = "DROP VIEW"
|
60
|
+
sql += " IF EXISTS" if options[:if_exists]
|
61
|
+
sql += " #{quote_table_name(view_name)}"
|
62
|
+
execute sql
|
57
63
|
end
|
58
64
|
|
59
65
|
|
@@ -68,7 +68,7 @@ module SchemaPlus
|
|
68
68
|
::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.send(:include, PostgreSQLColumn) unless ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn.include?(PostgreSQLColumn)
|
69
69
|
end
|
70
70
|
|
71
|
-
# SchemaPlus provides the following extra options for
|
71
|
+
# SchemaPlus provides the following extra options for PostgreSQL
|
72
72
|
# indexes:
|
73
73
|
# * +:conditions+ - SQL conditions for the WHERE clause of the index
|
74
74
|
# * +:expression+ - SQL expression to index. column_name can be nil or ommitted, in which case :name must be provided
|
@@ -154,7 +154,7 @@ module SchemaPlus
|
|
154
154
|
SQL
|
155
155
|
|
156
156
|
result.map do |(index_name, is_unique, indkey, inddef, oid, kind, conditions, expression)|
|
157
|
-
unique = (is_unique == 't')
|
157
|
+
unique = (is_unique == 't' || is_unique == true) # The test against true is for JDBC which is returning a boolean and not a String.
|
158
158
|
index_keys = indkey.split(" ")
|
159
159
|
|
160
160
|
rows = query(<<-SQL, "Columns for index #{index_name} on #{table_name}")
|
@@ -282,8 +282,64 @@ module SchemaPlus
|
|
282
282
|
row.first.chomp(';') unless row.nil?
|
283
283
|
end
|
284
284
|
|
285
|
+
def enums #:nodoc:
|
286
|
+
result = query(<<-SQL)
|
287
|
+
SELECT
|
288
|
+
N.nspname AS schema_name,
|
289
|
+
T.typname AS enum_name,
|
290
|
+
E.enumlabel AS enum_label,
|
291
|
+
E.enumsortorder AS enum_sort_order
|
292
|
+
--array_agg(E.enumlabel ORDER BY enumsortorder) AS labels
|
293
|
+
FROM pg_type T
|
294
|
+
JOIN pg_enum E ON E.enumtypid = T.oid
|
295
|
+
JOIN pg_namespace N ON N.oid = T.typnamespace
|
296
|
+
ORDER BY 1, 2, 4
|
297
|
+
SQL
|
298
|
+
|
299
|
+
result.reduce([]) do |res, row|
|
300
|
+
last = res.last
|
301
|
+
if last && last[0] == row[0] && last[1] == row[1]
|
302
|
+
last[2] << row[2]
|
303
|
+
else
|
304
|
+
res << (row[0..1] << [row[2]])
|
305
|
+
end
|
306
|
+
res
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def create_enum(name, *values)
|
311
|
+
options = values.extract_options!
|
312
|
+
list = values.map { |value| escape_enum_value(value) }
|
313
|
+
execute "CREATE TYPE #{enum_name(name, options[:schema])} AS ENUM (#{list.join(',')})"
|
314
|
+
end
|
315
|
+
|
316
|
+
def alter_enum(name, value, options = {})
|
317
|
+
opts = case
|
318
|
+
when options[:before] then "BEFORE #{escape_enum_value(options[:before])}"
|
319
|
+
when options[:after] then "AFTER #{escape_enum_value(options[:after])}"
|
320
|
+
else
|
321
|
+
''
|
322
|
+
end
|
323
|
+
execute "ALTER TYPE #{enum_name(name, options[:schema])} ADD VALUE #{escape_enum_value(value)} #{opts}"
|
324
|
+
end
|
325
|
+
|
326
|
+
def drop_enum(name, options = {})
|
327
|
+
execute "DROP TYPE #{enum_name(name, options[:schema])}"
|
328
|
+
end
|
329
|
+
|
285
330
|
private
|
286
331
|
|
332
|
+
def enum_name(name, schema)
|
333
|
+
[schema || 'public', name].map { |s|
|
334
|
+
%Q{"#{s}"}
|
335
|
+
}.join('.')
|
336
|
+
end
|
337
|
+
|
338
|
+
def escape_enum_value(value)
|
339
|
+
escaped_value = value.sub("'", "''")
|
340
|
+
"'#{escaped_value}'"
|
341
|
+
end
|
342
|
+
|
287
343
|
def namespace_sql(table_name)
|
288
344
|
(table_name.to_s =~ /(.*)[.]/) ? "'#{$1}'" : "ANY (current_schemas(false))"
|
289
345
|
end
|
@@ -49,6 +49,16 @@ module SchemaPlus
|
|
49
49
|
@backref_fks = Hash.new{ |h, k| h[k] = [] }
|
50
50
|
@dump_dependencies = {}
|
51
51
|
|
52
|
+
if @connection.respond_to?(:enums)
|
53
|
+
@connection.enums.each do |schema, name, values|
|
54
|
+
params = [name.inspect]
|
55
|
+
params << values.map(&:inspect).join(', ')
|
56
|
+
params << ":schema => #{schema.inspect}" if schema != 'public'
|
57
|
+
|
58
|
+
stream.puts " create_enum #{params.join(', ')}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
52
62
|
tables_without_schema_plus(nil)
|
53
63
|
|
54
64
|
@connection.views.each do |view_name|
|
data/lib/schema_plus/version.rb
CHANGED
data/runspecs
CHANGED
@@ -47,7 +47,7 @@ OptionParser.new do |opts|
|
|
47
47
|
o.db_adapters = DB_ADAPTERS
|
48
48
|
end
|
49
49
|
|
50
|
-
opts.on("--quick", "quick run on
|
50
|
+
opts.on("--quick", "quick run on PostgreSQL, ruby #{RUBY_VERSIONS.last} and rails #{RAILS_VERSIONS.last}") do
|
51
51
|
o.ruby_versions = [RUBY_VERSIONS.last]
|
52
52
|
o.rails_versions = [RAILS_VERSIONS.last]
|
53
53
|
o.db_adapters = ["postgresql"]
|
data/spec/column_default_spec.rb
CHANGED
@@ -70,14 +70,12 @@ describe "Column definition" do
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
is_expected.to match @nowish
|
80
|
-
end
|
73
|
+
it "should use NOW() as the default", :mysql => :skip do
|
74
|
+
is_expected.to match @nowish
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should raise an error", :mysql => :only do
|
78
|
+
expect(@raised_argument_error).to be_a ArgumentError
|
81
79
|
end
|
82
80
|
end
|
83
81
|
|
@@ -90,29 +88,25 @@ describe "Column definition" do
|
|
90
88
|
end
|
91
89
|
end
|
92
90
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
is_expected.to match @nowish
|
100
|
-
end
|
91
|
+
it "should use NOW() as the default", :mysql => :skip do
|
92
|
+
is_expected.to match @nowish
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should raise an error", :mysql => :only do
|
96
|
+
expect(@raised_argument_error).to be_a ArgumentError
|
101
97
|
end
|
102
98
|
end
|
103
99
|
|
104
100
|
context "valid expr passed as default" do
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
it "uses the expression" do
|
101
|
+
it "uses the expression", :mysql => :skip do
|
102
|
+
define_test_column(:string, :default => { :expr => "(replace('THIS IS A TEST', 'TEST', 'DOG'))" })
|
103
|
+
is_expected.to eq "THIS IS A DOG"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "raises an error", :mysql => :only do
|
107
|
+
expect {
|
113
108
|
define_test_column(:string, :default => { :expr => "(replace('THIS IS A TEST', 'TEST', 'DOG'))" })
|
114
|
-
|
115
|
-
end
|
109
|
+
}.to raise_error ArgumentError
|
116
110
|
end
|
117
111
|
end
|
118
112
|
|
data/spec/column_spec.rb
CHANGED
@@ -101,32 +101,30 @@ describe "Column" do
|
|
101
101
|
create_table(User, :alpha => { :default => "gabba" }, :beta => {})
|
102
102
|
end
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
u = User.create!(:alpha => "hey", :beta => "hello")
|
110
|
-
expect { u.update_attributes(:alpha => ActiveRecord::DB_DEFAULT, :beta => "goodbye") }.to raise_error ActiveRecord::StatementInvalid
|
111
|
-
end
|
112
|
-
else
|
104
|
+
it "creating a record should respect default expression", :sqlite3 => :skip do
|
105
|
+
User.create!(:alpha => ActiveRecord::DB_DEFAULT, :beta => "hello")
|
106
|
+
expect(User.last.alpha).to eq("gabba")
|
107
|
+
expect(User.last.beta).to eq("hello")
|
108
|
+
end
|
113
109
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
expect(User.last.beta).to eq("hello")
|
118
|
-
end
|
110
|
+
it "creating a record should raise an error", :sqlite3 => :only do
|
111
|
+
expect { User.create!(:alpha => ActiveRecord::DB_DEFAULT, :beta => "hello") }.to raise_error ActiveRecord::StatementInvalid
|
112
|
+
end
|
119
113
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
114
|
+
it "updating a record should respect default expression", :sqlite3 => :skip do
|
115
|
+
u = User.create!(:alpha => "hey", :beta => "hello")
|
116
|
+
u.reload
|
117
|
+
expect(u.alpha).to eq("hey")
|
118
|
+
expect(u.beta).to eq("hello")
|
119
|
+
u.update_attributes(:alpha => ActiveRecord::DB_DEFAULT, :beta => "goodbye")
|
120
|
+
u.reload
|
121
|
+
expect(u.alpha).to eq("gabba")
|
122
|
+
expect(u.beta).to eq("goodbye")
|
123
|
+
end
|
124
|
+
|
125
|
+
it "updating a record should raise an error", :sqlite3 => :only do
|
126
|
+
u = User.create!(:alpha => "hey", :beta => "hello")
|
127
|
+
expect { u.update_attributes(:alpha => ActiveRecord::DB_DEFAULT, :beta => "goodbye") }.to raise_error ActiveRecord::StatementInvalid
|
130
128
|
end
|
131
129
|
end
|
132
130
|
|
@@ -6,7 +6,7 @@ ActiveRecord::Base.logger = Logger.new(File.open("postgresql.log", "w"))
|
|
6
6
|
ActiveRecord::Base.configurations = {
|
7
7
|
'schema_plus' => {
|
8
8
|
:adapter => 'postgresql',
|
9
|
-
:username => ENV['
|
9
|
+
:username => ENV['POSTGRESQL_DB_USER'],
|
10
10
|
:database => 'schema_plus_test',
|
11
11
|
:min_messages => 'warning'
|
12
12
|
}
|
data/spec/enum_spec.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
def enum_fields(name, schema = 'public')
|
4
|
+
sql = <<-SQL
|
5
|
+
SELECT array_to_string(array_agg(E.enumlabel ORDER BY enumsortorder), ' ') AS "values"
|
6
|
+
FROM pg_enum E
|
7
|
+
JOIN pg_type T ON E.enumtypid = T.oid
|
8
|
+
JOIN pg_namespace N ON N.oid = T.typnamespace
|
9
|
+
WHERE N.nspname = '#{schema}' AND T.typname = '#{name}'
|
10
|
+
GROUP BY T.oid;
|
11
|
+
SQL
|
12
|
+
|
13
|
+
data = ActiveRecord::Base.connection.select_all(sql)
|
14
|
+
return nil if data.empty?
|
15
|
+
data[0]['values'].split(' ')
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'enum', :postgresql => :only do
|
19
|
+
before(:all) do ActiveRecord::Migration.verbose = false end
|
20
|
+
|
21
|
+
let(:migration) { ActiveRecord::Migration }
|
22
|
+
|
23
|
+
describe 'enums' do
|
24
|
+
it 'should return all enums' do
|
25
|
+
begin
|
26
|
+
migration.execute 'create schema cmyk'
|
27
|
+
migration.create_enum 'color', 'red', 'green', 'blue'
|
28
|
+
migration.create_enum 'color', 'cyan', 'magenta', 'yellow', 'black', schema: 'cmyk'
|
29
|
+
|
30
|
+
expect(migration.enums).to match_array [['cmyk', 'color', %w|cyan magenta yellow black|], ['public', 'color', %w|red green blue|]]
|
31
|
+
ensure
|
32
|
+
migration.drop_enum 'color'
|
33
|
+
migration.execute 'drop schema cmyk cascade'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'create_enum' do
|
39
|
+
it 'should create enum with given values' do
|
40
|
+
begin
|
41
|
+
migration.create_enum 'color', *%w|red green blue|
|
42
|
+
expect(enum_fields('color')).to eq(%w|red green blue|)
|
43
|
+
ensure
|
44
|
+
migration.execute 'DROP TYPE IF EXISTS color'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should create enum with schema' do
|
49
|
+
begin
|
50
|
+
migration.execute 'CREATE SCHEMA colors'
|
51
|
+
migration.create_enum 'color', *%|red green blue|, schema: 'colors'
|
52
|
+
expect(enum_fields('color', 'colors')).to eq(%w|red green blue|)
|
53
|
+
ensure
|
54
|
+
migration.execute 'DROP SCHEMA IF EXISTS colors CASCADE'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should escape enum value' do
|
59
|
+
begin
|
60
|
+
migration.create_enum('names', "O'Neal")
|
61
|
+
expect(enum_fields('names')).to eq(["O'Neal"])
|
62
|
+
ensure
|
63
|
+
migration.execute "DROP TYPE IF EXISTS names"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should escape schame name and enum name' do
|
68
|
+
begin
|
69
|
+
migration.execute 'CREATE SCHEMA "select"'
|
70
|
+
migration.create_enum 'where', *%|red green blue|, schema: 'select'
|
71
|
+
expect(enum_fields('where', 'select')).to eq(%w|red green blue|)
|
72
|
+
ensure
|
73
|
+
migration.execute 'DROP SCHEMA IF EXISTS "select" CASCADE'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'alter_enum' do
|
80
|
+
before(:each) do migration.create_enum('color', 'red', 'green', 'blue') end
|
81
|
+
after(:each) do migration.execute 'DROP TYPE IF EXISTS color' end
|
82
|
+
|
83
|
+
it 'should add new value after all values' do
|
84
|
+
migration.alter_enum('color', 'magenta')
|
85
|
+
expect(enum_fields('color')).to eq(%w|red green blue magenta|)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should add new value after existed' do
|
89
|
+
migration.alter_enum('color', 'magenta', after: 'red')
|
90
|
+
expect(enum_fields('color')).to eq(%w|red magenta green blue|)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should add new value before existed' do
|
94
|
+
migration.alter_enum('color', 'magenta', before: 'green')
|
95
|
+
expect(enum_fields('color')).to eq(%w|red magenta green blue|)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should add new value within given schema' do
|
99
|
+
begin
|
100
|
+
migration.execute 'CREATE SCHEMA colors'
|
101
|
+
migration.create_enum('color', 'red', schema: 'colors')
|
102
|
+
migration.alter_enum('color', 'green', schema: 'colors')
|
103
|
+
|
104
|
+
expect(enum_fields('color', 'colors')).to eq(%w|red green|)
|
105
|
+
ensure
|
106
|
+
migration.execute 'DROP SCHEMA colors CASCADE'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'drop_enum' do
|
112
|
+
it 'should drop enum with given name' do
|
113
|
+
migration.execute "CREATE TYPE color AS ENUM ('red', 'blue')"
|
114
|
+
expect(enum_fields('color')).to eq(%w|red blue|)
|
115
|
+
migration.drop_enum('color')
|
116
|
+
|
117
|
+
expect(enum_fields('color')).to be_nil
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should drop enum within given name and schema' do
|
121
|
+
begin
|
122
|
+
migration.execute "CREATE SCHEMA colors; CREATE TYPE colors.color AS ENUM ('red', 'blue')"
|
123
|
+
expect(enum_fields('color', 'colors')).to eq(%w|red blue|)
|
124
|
+
migration.drop_enum('color', schema: 'colors')
|
125
|
+
|
126
|
+
expect(enum_fields('color', 'colors')).to be_nil
|
127
|
+
ensure
|
128
|
+
migration.execute "DROP SCHEMA colors CASCADE"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/spec/foreign_key_spec.rb
CHANGED
@@ -79,25 +79,12 @@ describe "Foreign Key" do
|
|
79
79
|
class Comment < ::ActiveRecord::Base ; end
|
80
80
|
end
|
81
81
|
|
82
|
-
if SchemaPlusHelpers.sqlite3?
|
83
82
|
|
84
|
-
|
85
|
-
expect {
|
86
|
-
add_foreign_key(:posts, :author_id, :users, :id, :on_update => :cascade, :on_delete => :restrict)
|
87
|
-
}.to raise_error(NotImplementedError)
|
88
|
-
end
|
89
|
-
|
90
|
-
it "raises an exception when attempting to remove" do
|
91
|
-
expect {
|
92
|
-
remove_foreign_key(:posts, "dummy")
|
93
|
-
}.to raise_error(NotImplementedError)
|
94
|
-
end
|
95
|
-
|
96
|
-
else
|
83
|
+
context "works", :sqlite3 => :skip do
|
97
84
|
|
98
85
|
context "when is added", "posts(author_id)" do
|
99
86
|
|
100
|
-
before(:each) do
|
87
|
+
before(:each) do
|
101
88
|
add_foreign_key(:posts, :author_id, :users, :id, :on_update => :cascade, :on_delete => :restrict)
|
102
89
|
end
|
103
90
|
|
@@ -186,6 +173,22 @@ describe "Foreign Key" do
|
|
186
173
|
end
|
187
174
|
|
188
175
|
end
|
176
|
+
|
177
|
+
context "raises an exception", :sqlite3 => :only do
|
178
|
+
|
179
|
+
it "when attempting to add" do
|
180
|
+
expect {
|
181
|
+
add_foreign_key(:posts, :author_id, :users, :id, :on_update => :cascade, :on_delete => :restrict)
|
182
|
+
}.to raise_error(NotImplementedError)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "when attempting to remove" do
|
186
|
+
expect {
|
187
|
+
remove_foreign_key(:posts, "dummy")
|
188
|
+
}.to raise_error(NotImplementedError)
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
189
192
|
end
|
190
193
|
|
191
194
|
protected
|