rom-sql 2.0.0.beta1 → 2.0.0.beta2
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/CHANGELOG.md +2 -1
- data/lib/rom/plugins/relation/sql/auto_restrictions.rb +19 -6
- data/lib/rom/sql/attribute.rb +9 -0
- data/lib/rom/sql/commands/create.rb +0 -2
- data/lib/rom/sql/commands/delete.rb +0 -2
- data/lib/rom/sql/commands/update.rb +0 -2
- data/lib/rom/sql/extensions/postgres/attributes_inferrer.rb +1 -1
- data/lib/rom/sql/index.rb +16 -0
- data/lib/rom/sql/migration/inline_runner.rb +12 -4
- data/lib/rom/sql/migration/schema_diff.rb +23 -3
- data/lib/rom/sql/plugin/associates.rb +3 -3
- data/lib/rom/sql/plugin/timestamps.rb +2 -2
- data/lib/rom/sql/relation.rb +7 -0
- data/lib/rom/sql/relation/reading.rb +1 -1
- data/lib/rom/sql/schema.rb +2 -1
- data/lib/rom/sql/schema/dsl.rb +26 -0
- data/lib/rom/sql/schema/index_dsl.rb +50 -0
- data/lib/rom/sql/schema/inferrer.rb +11 -5
- data/lib/rom/sql/version.rb +1 -1
- data/spec/integration/auto_migrations/indexes_spec.rb +147 -3
- data/spec/integration/{graph_spec.rb → combine_with_spec.rb} +1 -1
- data/spec/integration/commands/create_spec.rb +37 -62
- data/spec/integration/commands/delete_spec.rb +6 -6
- data/spec/integration/commands/update_spec.rb +6 -8
- data/spec/integration/plugins/associates/many_to_many_spec.rb +2 -2
- data/spec/integration/plugins/associates_spec.rb +7 -7
- data/spec/integration/plugins/auto_restrictions_spec.rb +31 -0
- data/spec/integration/relation_schema_spec.rb +68 -0
- data/spec/integration/schema/inferrer_spec.rb +31 -6
- data/spec/support/helpers.rb +1 -1
- data/spec/unit/plugin/timestamp_spec.rb +2 -2
- data/spec/unit/types_spec.rb +1 -1
- metadata +6 -5
- data/lib/rom/sql/commands/transaction.rb +0 -33
data/lib/rom/sql/version.rb
CHANGED
@@ -6,6 +6,7 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
let(:table_name) { :users }
|
9
|
+
let(:relation_name) { ROM::Relation::Name.new(table_name) }
|
9
10
|
|
10
11
|
subject(:gateway) { container.gateways[:default] }
|
11
12
|
|
@@ -18,6 +19,14 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
|
|
18
19
|
|
19
20
|
let(:attributes) { migrated_schema.to_a }
|
20
21
|
|
22
|
+
def indexdef(index)
|
23
|
+
gateway.connection[<<-SQL, index].first[:indexdef]
|
24
|
+
select indexdef
|
25
|
+
from pg_indexes
|
26
|
+
where indexname = ?
|
27
|
+
SQL
|
28
|
+
end
|
29
|
+
|
21
30
|
describe 'create table' do
|
22
31
|
describe 'one-column indexes' do
|
23
32
|
before do
|
@@ -25,6 +34,10 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
|
|
25
34
|
schema do
|
26
35
|
attribute :id, ROM::SQL::Types::Serial
|
27
36
|
attribute :name, ROM::SQL::Types::String.meta(index: true)
|
37
|
+
|
38
|
+
indexes do
|
39
|
+
index :name, name: :unique_name, unique: true
|
40
|
+
end
|
28
41
|
end
|
29
42
|
end
|
30
43
|
end
|
@@ -43,6 +56,13 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
|
|
43
56
|
[:definition, [String, {}]],
|
44
57
|
source: :users]],
|
45
58
|
])
|
59
|
+
|
60
|
+
expect(migrated_schema.indexes.first).
|
61
|
+
to eql(ROM::SQL::Index.new(
|
62
|
+
[define_attribute(:name, :String, source: relation_name)],
|
63
|
+
name: :unique_name,
|
64
|
+
unique: true
|
65
|
+
))
|
46
66
|
end
|
47
67
|
end
|
48
68
|
end
|
@@ -51,23 +71,54 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
|
|
51
71
|
describe 'one-column indexes' do
|
52
72
|
context 'adding' do
|
53
73
|
before do
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'adds indexed column' do
|
77
|
+
conn.create_table :users do
|
78
|
+
primary_key :id
|
79
|
+
end
|
80
|
+
|
54
81
|
conf.relation(:users) do
|
55
82
|
schema do
|
56
83
|
attribute :id, ROM::SQL::Types::Serial
|
57
84
|
attribute :name, ROM::SQL::Types::String.meta(index: true)
|
58
85
|
end
|
59
86
|
end
|
87
|
+
|
88
|
+
gateway.auto_migrate!(conf)
|
89
|
+
|
90
|
+
name_index = migrated_schema.indexes.first
|
91
|
+
|
92
|
+
expect(migrated_schema.attributes[1].name).to eql(:name)
|
93
|
+
expect(migrated_schema.indexes.size).to eql(1)
|
94
|
+
expect(name_index.name).to eql(:users_name_index)
|
95
|
+
expect(name_index.attributes.map(&:name)).to eql(%i(name))
|
60
96
|
end
|
61
97
|
|
62
|
-
it '
|
98
|
+
it 'supports custom names' do
|
63
99
|
conn.create_table :users do
|
64
100
|
primary_key :id
|
65
101
|
end
|
66
102
|
|
103
|
+
conf.relation(:users) do
|
104
|
+
schema do
|
105
|
+
attribute :id, ROM::SQL::Types::Serial
|
106
|
+
attribute :name, ROM::SQL::Types::String
|
107
|
+
|
108
|
+
indexes do
|
109
|
+
index :name, name: :custom_idx
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
67
114
|
gateway.auto_migrate!(conf)
|
68
115
|
|
116
|
+
name_index = migrated_schema.indexes.first
|
117
|
+
|
69
118
|
expect(migrated_schema.attributes[1].name).to eql(:name)
|
70
|
-
expect(migrated_schema.indexes.
|
119
|
+
expect(migrated_schema.indexes.size).to eql(1)
|
120
|
+
expect(name_index.name).to eql(:custom_idx)
|
121
|
+
expect(name_index.attributes.map(&:name)).to eql(%i(name))
|
71
122
|
end
|
72
123
|
|
73
124
|
it 'adds index to existing column' do
|
@@ -76,9 +127,98 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
|
|
76
127
|
column :name, String
|
77
128
|
end
|
78
129
|
|
130
|
+
conf.relation(:users) do
|
131
|
+
schema do
|
132
|
+
attribute :id, ROM::SQL::Types::Serial
|
133
|
+
attribute :name, ROM::SQL::Types::String
|
134
|
+
|
135
|
+
indexes do
|
136
|
+
index :name
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
gateway.auto_migrate!(conf)
|
142
|
+
|
143
|
+
name_index = migrated_schema.indexes.first
|
144
|
+
|
145
|
+
expect(name_index.name).to eql(:users_name_index)
|
146
|
+
expect(name_index.attributes.map(&:name)).to eql(%i(name))
|
147
|
+
expect(name_index).not_to be_unique
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'supports unique indexes' do
|
151
|
+
conn.create_table :users do
|
152
|
+
primary_key :id
|
153
|
+
column :name, String
|
154
|
+
end
|
155
|
+
|
156
|
+
conf.relation(:users) do
|
157
|
+
schema do
|
158
|
+
attribute :id, ROM::SQL::Types::Serial
|
159
|
+
attribute :name, ROM::SQL::Types::String
|
160
|
+
|
161
|
+
indexes do
|
162
|
+
index :name, unique: true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
79
167
|
gateway.auto_migrate!(conf)
|
80
168
|
|
81
|
-
|
169
|
+
name_index = migrated_schema.indexes.first
|
170
|
+
|
171
|
+
expect(name_index.name).to eql(:users_name_index)
|
172
|
+
expect(name_index.attributes.map(&:name)).to eql(%i(name))
|
173
|
+
expect(name_index).to be_unique
|
174
|
+
end
|
175
|
+
|
176
|
+
if metadata[:postgres]
|
177
|
+
it 'uses index method' do
|
178
|
+
conn.create_table :users do
|
179
|
+
primary_key :id
|
180
|
+
column :props, :jsonb, null: false
|
181
|
+
end
|
182
|
+
|
183
|
+
conf.relation(:users) do
|
184
|
+
schema do
|
185
|
+
attribute :id, ROM::SQL::Types::Serial
|
186
|
+
attribute :props, ROM::SQL::Types::PG::JSONB
|
187
|
+
|
188
|
+
indexes do
|
189
|
+
index :props, type: :gin
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
gateway.auto_migrate!(conf)
|
195
|
+
|
196
|
+
expect(indexdef('users_props_index')).
|
197
|
+
to eql('CREATE INDEX users_props_index ON users USING gin (props)')
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'supports partial indexes' do
|
201
|
+
conn.create_table :users do
|
202
|
+
primary_key :id
|
203
|
+
column :name, String
|
204
|
+
end
|
205
|
+
|
206
|
+
conf.relation(:users) do
|
207
|
+
schema do
|
208
|
+
attribute :id, ROM::SQL::Types::Serial
|
209
|
+
attribute :name, ROM::SQL::Types::String
|
210
|
+
|
211
|
+
indexes do
|
212
|
+
index :name, name: :long_names_only, predicate: 'length(name) > 10'
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
gateway.auto_migrate!(conf)
|
218
|
+
|
219
|
+
expect(indexdef('long_names_only')).
|
220
|
+
to eql('CREATE INDEX long_names_only ON users USING btree (name) WHERE (length(name) > 10)')
|
221
|
+
end
|
82
222
|
end
|
83
223
|
end
|
84
224
|
|
@@ -88,19 +228,23 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do
|
|
88
228
|
schema do
|
89
229
|
attribute :id, ROM::SQL::Types::Serial
|
90
230
|
attribute :name, ROM::SQL::Types::String
|
231
|
+
attribute :email, ROM::SQL::Types::String
|
91
232
|
end
|
92
233
|
end
|
93
234
|
|
94
235
|
conn.create_table :users do
|
95
236
|
primary_key :id
|
96
237
|
column :name, String
|
238
|
+
column :email, String
|
97
239
|
|
98
240
|
index :name
|
241
|
+
index :email, name: :email_idx
|
99
242
|
end
|
100
243
|
end
|
101
244
|
|
102
245
|
it 'removes index' do
|
103
246
|
gateway.auto_migrate!(conf)
|
247
|
+
|
104
248
|
expect(migrated_schema.indexes).to be_empty
|
105
249
|
end
|
106
250
|
end
|
@@ -34,7 +34,7 @@ RSpec.describe 'Eager loading' do
|
|
34
34
|
tasks = container.relations[:tasks]
|
35
35
|
tags = container.relations[:tags]
|
36
36
|
|
37
|
-
relation = users.
|
37
|
+
relation = users.combine_with(tasks.for_users.combine_with(tags.for_tasks))
|
38
38
|
|
39
39
|
# TODO: figure out a way to assert correct number of issued queries
|
40
40
|
expect(relation.call).to be_instance_of(ROM::Relation::Loaded)
|
@@ -46,37 +46,37 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
46
46
|
with_adapters do
|
47
47
|
describe '#transaction' do
|
48
48
|
it 'creates record if nothing was raised' do
|
49
|
-
result =
|
49
|
+
result = users.transaction {
|
50
50
|
create_user.call(name: 'Jane')
|
51
51
|
}
|
52
52
|
|
53
|
-
expect(result
|
53
|
+
expect(result).to eql(id: 1, name: 'Jane')
|
54
54
|
end
|
55
55
|
|
56
56
|
it 'creates multiple records if nothing was raised' do
|
57
|
-
result =
|
57
|
+
result = users.transaction {
|
58
58
|
create_users.call([{ name: 'Jane' }, { name: 'Jack' }])
|
59
59
|
}
|
60
60
|
|
61
|
-
expect(result
|
61
|
+
expect(result).to match_array([
|
62
62
|
{ id: 1, name: 'Jane' }, { id: 2, name: 'Jack' }
|
63
63
|
])
|
64
64
|
end
|
65
65
|
|
66
66
|
it 'allows for nested transactions' do
|
67
|
-
result =
|
68
|
-
|
67
|
+
result = users.transaction {
|
68
|
+
users.transaction {
|
69
69
|
create_user.call(name: 'Jane')
|
70
70
|
}
|
71
71
|
}
|
72
72
|
|
73
|
-
expect(result
|
73
|
+
expect(result).to eql(id: 1, name: 'Jane')
|
74
74
|
end
|
75
75
|
|
76
76
|
it 'creates nothing if command error was raised' do
|
77
77
|
expect {
|
78
78
|
begin
|
79
|
-
|
79
|
+
users.transaction {
|
80
80
|
create_user.call(name: 'Jane')
|
81
81
|
create_user.call(name: nil)
|
82
82
|
}
|
@@ -87,19 +87,13 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
87
87
|
|
88
88
|
it 'creates nothing if rollback was raised' do
|
89
89
|
expect {
|
90
|
-
|
91
|
-
|
92
|
-
result = create_user.transaction {
|
90
|
+
result = users.transaction { |t|
|
93
91
|
create_user.call(name: 'Jane')
|
94
92
|
create_user.call(name: 'John')
|
95
|
-
|
96
|
-
} >-> _value {
|
97
|
-
passed = true
|
93
|
+
t.rollback!
|
98
94
|
}
|
99
95
|
|
100
|
-
expect(result
|
101
|
-
expect(result.error).to be(nil)
|
102
|
-
expect(passed).to be(false)
|
96
|
+
expect(result).to be(nil)
|
103
97
|
}.to_not change { container.relations.users.count }
|
104
98
|
end
|
105
99
|
|
@@ -108,10 +102,9 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
108
102
|
begin
|
109
103
|
passed = false
|
110
104
|
|
111
|
-
|
105
|
+
users.transaction {
|
112
106
|
create_user.call(name: 'Jane')
|
113
107
|
create_user.call(name: 'Jane')
|
114
|
-
} >-> _value {
|
115
108
|
passed = true
|
116
109
|
}
|
117
110
|
rescue => error
|
@@ -124,9 +117,10 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
124
117
|
it 'creates nothing if anything was raised in any nested transaction' do
|
125
118
|
expect {
|
126
119
|
expect {
|
127
|
-
|
120
|
+
users.transaction {
|
128
121
|
create_user.call(name: 'John')
|
129
|
-
|
122
|
+
|
123
|
+
users.transaction {
|
130
124
|
create_user.call(name: 'Jane')
|
131
125
|
raise Exception
|
132
126
|
}
|
@@ -158,24 +152,22 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
158
152
|
end
|
159
153
|
|
160
154
|
it 'returns a single tuple when result is set to :one' do
|
161
|
-
result =
|
155
|
+
result = create_user.call(name: 'Jane')
|
162
156
|
|
163
|
-
expect(result
|
157
|
+
expect(result).to eql(id: 1, name: 'Jane')
|
164
158
|
end
|
165
159
|
|
166
160
|
it 'returns tuples when result is set to :many' do
|
167
|
-
result =
|
168
|
-
create_users.call([{ name: 'Jane' }, { name: 'Jack' }])
|
169
|
-
end
|
161
|
+
result = create_users.call([{ name: 'Jane' }, { name: 'Jack' }])
|
170
162
|
|
171
|
-
expect(result.
|
163
|
+
expect(result.to_a).to match_array([
|
172
164
|
{ id: 1, name: 'Jane' }, { id: 2, name: 'Jack' }
|
173
165
|
])
|
174
166
|
end
|
175
167
|
|
176
168
|
it 're-raises not-null constraint violation error' do
|
177
169
|
expect {
|
178
|
-
|
170
|
+
create_user.call(name: nil)
|
179
171
|
}.to raise_error(ROM::SQL::NotNullConstraintError)
|
180
172
|
end
|
181
173
|
|
@@ -198,34 +190,27 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
198
190
|
it 're-raises not-null constraint violation error with nil boolean' do
|
199
191
|
puppies = commands[:puppies]
|
200
192
|
|
201
|
-
expect {
|
202
|
-
|
203
|
-
}.to raise_error(ROM::SQL::NotNullConstraintError)
|
193
|
+
expect { puppies.create.call(name: 'Charlie', cute: nil) }.
|
194
|
+
to raise_error(ROM::SQL::NotNullConstraintError)
|
204
195
|
end
|
205
196
|
end
|
206
197
|
end
|
207
198
|
|
208
|
-
it '
|
199
|
+
it 'raises uniqueness constraint violation error' do
|
209
200
|
expect {
|
210
|
-
|
211
|
-
|
212
|
-
} >-> user {
|
213
|
-
user_commands.try { create_user.call(name: user[:name]) }
|
214
|
-
}
|
201
|
+
user = create_user.call(name: 'Jane')
|
202
|
+
create_user.call(name: user[:name])
|
215
203
|
}.to raise_error(ROM::SQL::UniqueConstraintError)
|
216
204
|
end
|
217
205
|
|
218
206
|
it 're-raises fk constraint violation error' do |ex|
|
219
|
-
expect {
|
220
|
-
|
221
|
-
create_task.call(user_id: 918_273_645)
|
222
|
-
}
|
223
|
-
}.to raise_error(ROM::SQL::ForeignKeyConstraintError)
|
207
|
+
expect { create_task.call(user_id: 918_273_645) }.
|
208
|
+
to raise_error(ROM::SQL::ForeignKeyConstraintError)
|
224
209
|
end
|
225
210
|
|
226
211
|
it 're-raises database errors' do
|
227
212
|
expect {
|
228
|
-
|
213
|
+
create_user.call(name: nil)
|
229
214
|
}.to raise_error(ROM::SQL::NotNullConstraintError)
|
230
215
|
end
|
231
216
|
|
@@ -233,9 +218,8 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
233
218
|
context 'with a single record' do
|
234
219
|
it 'materializes the result' do
|
235
220
|
result = create_user.execute(name: 'Jane')
|
236
|
-
|
237
|
-
|
238
|
-
])
|
221
|
+
|
222
|
+
expect(result).to eq([{ id: 1, name: 'Jane' }])
|
239
223
|
end
|
240
224
|
end
|
241
225
|
|
@@ -245,10 +229,8 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
245
229
|
{ name: 'Jane' },
|
246
230
|
{ name: 'John' }
|
247
231
|
])
|
248
|
-
|
249
|
-
|
250
|
-
{ id: 2, name: 'John' }
|
251
|
-
])
|
232
|
+
|
233
|
+
expect(result).to eql([{ id: 1, name: 'Jane' }, { id: 2, name: 'John' }])
|
252
234
|
end
|
253
235
|
end
|
254
236
|
|
@@ -288,20 +270,13 @@ RSpec.describe 'Commands / Create', :postgres, seeds: false do
|
|
288
270
|
end
|
289
271
|
|
290
272
|
describe '#call' do
|
291
|
-
it '
|
292
|
-
expect {
|
293
|
-
|
294
|
-
create_user.call(name: 'J')
|
295
|
-
}
|
296
|
-
}.to raise_error(ROM::SQL::CheckConstraintError, /name/)
|
273
|
+
it 'raises check constraint violation error' do
|
274
|
+
expect { create_user.call(name: 'J') }.
|
275
|
+
to raise_error(ROM::SQL::CheckConstraintError, /name/)
|
297
276
|
end
|
298
277
|
|
299
|
-
it '
|
300
|
-
expect {
|
301
|
-
user_commands.try {
|
302
|
-
create_task.call(title: '')
|
303
|
-
}
|
304
|
-
}.to raise_error(ROM::SQL::ConstraintError, /title/)
|
278
|
+
it 'raises constraint violation error' do
|
279
|
+
expect { create_task.call(title: '') }.to raise_error(ROM::SQL::ConstraintError, /title/)
|
305
280
|
end
|
306
281
|
end
|
307
282
|
|