torque-postgresql 1.1.8 → 2.0.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/lib/torque/postgresql.rb +0 -2
- data/lib/torque/postgresql/adapter.rb +0 -1
- data/lib/torque/postgresql/adapter/database_statements.rb +4 -15
- data/lib/torque/postgresql/adapter/schema_creation.rb +13 -23
- data/lib/torque/postgresql/adapter/schema_definitions.rb +7 -21
- data/lib/torque/postgresql/adapter/schema_dumper.rb +71 -11
- data/lib/torque/postgresql/adapter/schema_statements.rb +2 -12
- data/lib/torque/postgresql/associations.rb +0 -3
- data/lib/torque/postgresql/associations/association.rb +0 -4
- data/lib/torque/postgresql/associations/association_scope.rb +18 -60
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +12 -15
- data/lib/torque/postgresql/associations/preloader.rb +0 -32
- data/lib/torque/postgresql/associations/preloader/association.rb +13 -10
- data/lib/torque/postgresql/autosave_association.rb +4 -4
- data/lib/torque/postgresql/auxiliary_statement.rb +1 -13
- data/lib/torque/postgresql/coder.rb +1 -2
- data/lib/torque/postgresql/config.rb +0 -6
- data/lib/torque/postgresql/inheritance.rb +13 -17
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +19 -25
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -38
- data/lib/torque/postgresql/relation.rb +11 -16
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +2 -8
- data/lib/torque/postgresql/relation/distinct_on.rb +1 -1
- data/lib/torque/postgresql/version.rb +1 -1
- data/spec/en.yml +19 -0
- data/spec/factories/authors.rb +6 -0
- data/spec/factories/comments.rb +13 -0
- data/spec/factories/posts.rb +6 -0
- data/spec/factories/tags.rb +5 -0
- data/spec/factories/texts.rb +5 -0
- data/spec/factories/users.rb +6 -0
- data/spec/factories/videos.rb +5 -0
- data/spec/mocks/cache_query.rb +16 -0
- data/spec/mocks/create_table.rb +35 -0
- data/spec/models/activity.rb +3 -0
- data/spec/models/activity_book.rb +4 -0
- data/spec/models/activity_post.rb +7 -0
- data/spec/models/activity_post/sample.rb +4 -0
- data/spec/models/author.rb +4 -0
- data/spec/models/author_journalist.rb +4 -0
- data/spec/models/comment.rb +3 -0
- data/spec/models/course.rb +2 -0
- data/spec/models/geometry.rb +2 -0
- data/spec/models/guest_comment.rb +4 -0
- data/spec/models/post.rb +6 -0
- data/spec/models/tag.rb +2 -0
- data/spec/models/text.rb +2 -0
- data/spec/models/time_keeper.rb +2 -0
- data/spec/models/user.rb +8 -0
- data/spec/models/video.rb +2 -0
- data/spec/schema.rb +141 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/tests/arel_spec.rb +72 -0
- data/spec/tests/auxiliary_statement_spec.rb +593 -0
- data/spec/tests/belongs_to_many_spec.rb +240 -0
- data/spec/tests/coder_spec.rb +367 -0
- data/spec/tests/collector_spec.rb +59 -0
- data/spec/tests/distinct_on_spec.rb +65 -0
- data/spec/tests/enum_set_spec.rb +306 -0
- data/spec/tests/enum_spec.rb +621 -0
- data/spec/tests/geometric_builder_spec.rb +221 -0
- data/spec/tests/has_many_spec.rb +390 -0
- data/spec/tests/interval_spec.rb +167 -0
- data/spec/tests/lazy_spec.rb +24 -0
- data/spec/tests/period_spec.rb +954 -0
- data/spec/tests/quoting_spec.rb +24 -0
- data/spec/tests/range_spec.rb +36 -0
- data/spec/tests/relation_spec.rb +57 -0
- data/spec/tests/table_inheritance_spec.rb +403 -0
- metadata +103 -15
- data/lib/torque/postgresql/associations/join_dependency/join_association.rb +0 -15
- data/lib/torque/postgresql/schema_dumper.rb +0 -101
@@ -0,0 +1,593 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'AuxiliaryStatement' do
|
4
|
+
before :each do
|
5
|
+
User.auxiliary_statements_list = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'on relation' do
|
9
|
+
let(:klass) { User }
|
10
|
+
let(:true_value) { 'TRUE' }
|
11
|
+
subject { klass.unscoped }
|
12
|
+
|
13
|
+
it 'has its method' do
|
14
|
+
expect(subject).to respond_to(:with)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can perform simple queries' do
|
18
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
19
|
+
cte.query Comment.all
|
20
|
+
cte.attributes content: :comment_content
|
21
|
+
end
|
22
|
+
|
23
|
+
result = 'WITH "comments" AS'
|
24
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
|
25
|
+
result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
|
26
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
27
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can perform more complex queries' do
|
31
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
32
|
+
cte.query Comment.distinct_on(:user_id).order(:user_id, id: :desc)
|
33
|
+
cte.attributes content: :last_comment
|
34
|
+
end
|
35
|
+
|
36
|
+
result = 'WITH "comments" AS (SELECT DISTINCT ON ( "comments"."user_id" )'
|
37
|
+
result << ' "comments"."user_id", "comments"."content" AS last_comment'
|
38
|
+
result << ' FROM "comments" ORDER BY "comments"."user_id" ASC,'
|
39
|
+
result << ' "comments"."id" DESC) SELECT "users".*,'
|
40
|
+
result << ' "comments"."last_comment" FROM "users" INNER JOIN "comments"'
|
41
|
+
result << ' ON "comments"."user_id" = "users"."id"'
|
42
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'accepts extra select columns' do
|
46
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
47
|
+
cte.query Comment.all
|
48
|
+
cte.attributes content: :comment_content
|
49
|
+
end
|
50
|
+
|
51
|
+
result = 'WITH "comments" AS'
|
52
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content, "comments"."slug" AS comment_slug FROM "comments")'
|
53
|
+
result << ' SELECT "users".*, "comments"."comment_content", "comments"."comment_slug" FROM "users"'
|
54
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
55
|
+
expect(subject.with(:comments, select: {slug: :comment_slug}).arel.to_sql).to eql(result)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'accepts extra join columns' do
|
59
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
60
|
+
cte.query Comment.all
|
61
|
+
cte.attributes content: :comment_content
|
62
|
+
end
|
63
|
+
|
64
|
+
result = 'WITH "comments" AS'
|
65
|
+
result << ' (SELECT "comments"."user_id", "comments"."active", "comments"."content" AS comment_content FROM "comments")'
|
66
|
+
result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
|
67
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id" AND "comments"."active" = "users"."active"'
|
68
|
+
expect(subject.with(:comments, join: {active: :active}).arel.to_sql).to eql(result)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'accepts extra conditions' do
|
72
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
73
|
+
cte.query Comment.all
|
74
|
+
cte.attributes content: :comment_content
|
75
|
+
end
|
76
|
+
|
77
|
+
result = 'WITH "comments" AS'
|
78
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content'
|
79
|
+
result << ' FROM "comments" WHERE "comments"."active" = $1)'
|
80
|
+
result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
|
81
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
82
|
+
expect(subject.with(:comments, where: {active: true}).arel.to_sql).to eql(result)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'accepts scopes from both sides' do
|
86
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
87
|
+
cte.query Comment.where(id: 1).all
|
88
|
+
cte.attributes content: :comment_content
|
89
|
+
end
|
90
|
+
|
91
|
+
query = subject.where(id: 2).with(:comments)
|
92
|
+
|
93
|
+
result = 'WITH "comments" AS'
|
94
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments"'
|
95
|
+
result << ' WHERE "comments"."id" = $1)'
|
96
|
+
result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
|
97
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
98
|
+
result << ' WHERE "users"."id" = $2'
|
99
|
+
|
100
|
+
expect(query.arel.to_sql).to eql(result)
|
101
|
+
expect(query.send(:bound_attributes).map(&:value_before_type_cast)).to eql([1, 2])
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'accepts string as attributes' do
|
105
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
106
|
+
cte.query Comment.all
|
107
|
+
cte.attributes sql('MAX(id)') => :comment_id
|
108
|
+
end
|
109
|
+
|
110
|
+
result = 'WITH "comments" AS'
|
111
|
+
result << ' (SELECT "comments"."user_id", MAX(id) AS comment_id FROM "comments")'
|
112
|
+
result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
|
113
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
114
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'accepts complex string as attributes' do
|
118
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
119
|
+
cte.query Comment.all
|
120
|
+
cte.attributes sql('ROW_NUMBER() OVER (PARTITION BY ORDER BY "comments"."id")') => :comment_id
|
121
|
+
end
|
122
|
+
|
123
|
+
result = 'WITH "comments" AS'
|
124
|
+
result << ' (SELECT "comments"."user_id", ROW_NUMBER() OVER (PARTITION BY ORDER BY "comments"."id") AS comment_id FROM "comments")'
|
125
|
+
result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
|
126
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
127
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'accepts arel attribute as attributes' do
|
131
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
132
|
+
cte.query Comment.all
|
133
|
+
cte.attributes col(:id).minimum => :comment_id
|
134
|
+
end
|
135
|
+
|
136
|
+
result = 'WITH "comments" AS'
|
137
|
+
result << ' (SELECT "comments"."user_id", MIN("comments"."id") AS comment_id FROM "comments")'
|
138
|
+
result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
|
139
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
140
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'accepts custom join properties' do
|
144
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
145
|
+
cte.query Comment.all
|
146
|
+
cte.attributes content: :comment_content
|
147
|
+
cte.join name: :id, 'a.col' => :col
|
148
|
+
end
|
149
|
+
|
150
|
+
result = 'WITH "comments" AS (SELECT "comments"."id", "comments"."col",'
|
151
|
+
result << ' "comments"."content" AS comment_content FROM "comments") SELECT "users".*,'
|
152
|
+
result << ' "comments"."comment_content" FROM "users" INNER JOIN "comments"'
|
153
|
+
result << ' ON "comments"."id" = "users"."name" AND "comments"."col" = "a"."col"'
|
154
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'can perform other types of joins' do
|
158
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
159
|
+
cte.query Comment.all
|
160
|
+
cte.attributes content: :comment_content
|
161
|
+
cte.join_type :left
|
162
|
+
end
|
163
|
+
|
164
|
+
result = 'WITH "comments" AS (SELECT "comments"."user_id",'
|
165
|
+
result << ' "comments"."content" AS comment_content FROM "comments") SELECT "users".*,'
|
166
|
+
result << ' "comments"."comment_content" FROM "users" LEFT OUTER JOIN "comments"'
|
167
|
+
result << ' ON "comments"."user_id" = "users"."id"'
|
168
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'can manually define the association' do
|
172
|
+
klass.has_many :sample_comment, class_name: 'Comment', foreign_key: :a_user_id
|
173
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
174
|
+
cte.query Comment.all
|
175
|
+
cte.through :sample_comment
|
176
|
+
cte.attributes content: :sample_content
|
177
|
+
end
|
178
|
+
|
179
|
+
result = 'WITH "comments" AS'
|
180
|
+
result << ' (SELECT "comments"."a_user_id", "comments"."content" AS sample_content FROM "comments")'
|
181
|
+
result << ' SELECT "users".*, "comments"."sample_content" FROM "users"'
|
182
|
+
result << ' INNER JOIN "comments" ON "comments"."a_user_id" = "users"."id"'
|
183
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'accepts complex scopes from dependencies' do
|
187
|
+
klass.send(:auxiliary_statement, :comments1) do |cte|
|
188
|
+
cte.query Comment.where(id: 1).all
|
189
|
+
cte.attributes content: :comment_content1
|
190
|
+
end
|
191
|
+
|
192
|
+
klass.send(:auxiliary_statement, :comments2) do |cte|
|
193
|
+
cte.requires :comments1
|
194
|
+
cte.query Comment.where(id: 2).all
|
195
|
+
cte.attributes content: :comment_content2
|
196
|
+
end
|
197
|
+
|
198
|
+
query = subject.where(id: 3).with(:comments2)
|
199
|
+
|
200
|
+
result = 'WITH '
|
201
|
+
result << '"comments1" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content1 FROM "comments" WHERE "comments"."id" = $1), '
|
202
|
+
result << '"comments2" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content2 FROM "comments" WHERE "comments"."id" = $2)'
|
203
|
+
result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
|
204
|
+
result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
|
205
|
+
result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
|
206
|
+
result << ' WHERE "users"."id" = $3'
|
207
|
+
|
208
|
+
expect(query.arel.to_sql).to eql(result)
|
209
|
+
expect(query.send(:bound_attributes).map(&:value_before_type_cast)).to eql([1, 2, 3])
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'with dependency' do
|
213
|
+
before :each do
|
214
|
+
klass.send(:auxiliary_statement, :comments1) do |cte|
|
215
|
+
cte.query Comment.all
|
216
|
+
cte.attributes content: :comment_content1
|
217
|
+
end
|
218
|
+
|
219
|
+
klass.send(:auxiliary_statement, :comments2) do |cte|
|
220
|
+
cte.requires :comments1
|
221
|
+
cte.query Comment.all
|
222
|
+
cte.attributes content: :comment_content2
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'can requires another statement as dependency' do
|
227
|
+
result = 'WITH '
|
228
|
+
result << '"comments1" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content1 FROM "comments"), '
|
229
|
+
result << '"comments2" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content2 FROM "comments")'
|
230
|
+
result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
|
231
|
+
result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
|
232
|
+
result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
|
233
|
+
expect(subject.with(:comments2).arel.to_sql).to eql(result)
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'can uses already already set dependent' do
|
237
|
+
result = 'WITH '
|
238
|
+
result << '"comments1" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content1 FROM "comments"), '
|
239
|
+
result << '"comments2" AS (SELECT "comments"."user_id", "comments"."content" AS comment_content2 FROM "comments")'
|
240
|
+
result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
|
241
|
+
result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
|
242
|
+
result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
|
243
|
+
expect(subject.with(:comments1, :comments2).arel.to_sql).to eql(result)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'raises an error if the dependent does not exist' do
|
247
|
+
klass.send(:auxiliary_statement, :comments2) do |cte|
|
248
|
+
cte.requires :comments3
|
249
|
+
cte.query Comment.all
|
250
|
+
cte.attributes content: :comment_content2
|
251
|
+
end
|
252
|
+
expect{ subject.with(:comments2).arel.to_sql }.to raise_error(ArgumentError)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context 'query as string' do
|
257
|
+
it 'performs correctly' do
|
258
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
259
|
+
cte.query :comments, 'SELECT * FROM comments'
|
260
|
+
cte.attributes content: :comment
|
261
|
+
cte.join id: :user_id
|
262
|
+
end
|
263
|
+
|
264
|
+
result = 'WITH "comments" AS (SELECT * FROM comments)'
|
265
|
+
result << ' SELECT "users".*, "comments"."comment" FROM "users"'
|
266
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
267
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'accepts arguments to format the query' do
|
271
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
272
|
+
cte.query :comments, 'SELECT * FROM comments WHERE active = %{active}'
|
273
|
+
cte.attributes content: :comment
|
274
|
+
cte.join id: :user_id
|
275
|
+
end
|
276
|
+
|
277
|
+
result = "WITH \"comments\" AS (SELECT * FROM comments WHERE active = #{true_value})"
|
278
|
+
result << ' SELECT "users".*, "comments"."comment" FROM "users"'
|
279
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
280
|
+
expect(subject.with(:comments, args: {active: true}).arel.to_sql).to eql(result)
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'raises an error when join columns are not given' do
|
284
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
285
|
+
cte.query :comments, 'SELECT * FROM comments'
|
286
|
+
cte.attributes content: :comment
|
287
|
+
end
|
288
|
+
|
289
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /join columns/)
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'raises an error when not given the table name as first argument' do
|
293
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
294
|
+
cte.query 'SELECT * FROM comments'
|
295
|
+
cte.attributes content: :comment
|
296
|
+
cte.join id: :user_id
|
297
|
+
end
|
298
|
+
|
299
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /table name/)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
context 'query as proc' do
|
304
|
+
it 'performs correctly for result as relation' do
|
305
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
306
|
+
cte.query :comments, -> { Comment.all }
|
307
|
+
cte.attributes content: :comment
|
308
|
+
cte.join id: :user_id
|
309
|
+
end
|
310
|
+
|
311
|
+
result = 'WITH "comments" AS'
|
312
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment FROM "comments")'
|
313
|
+
result << ' SELECT "users".*, "comments"."comment" FROM "users"'
|
314
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
315
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
316
|
+
end
|
317
|
+
|
318
|
+
it 'performs correctly for anything that has a call method' do
|
319
|
+
obj = Struct.new(:call, :arity).new('SELECT * FROM comments', 0)
|
320
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
321
|
+
cte.query :comments, obj
|
322
|
+
cte.attributes content: :comment
|
323
|
+
cte.join id: :user_id
|
324
|
+
end
|
325
|
+
|
326
|
+
result = 'WITH "comments" AS (SELECT * FROM comments)'
|
327
|
+
result << ' SELECT "users".*, "comments"."comment" FROM "users"'
|
328
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
329
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'performs correctly for result as string' do
|
333
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
334
|
+
cte.query :comments, -> { 'SELECT * FROM comments' }
|
335
|
+
cte.attributes content: :comment
|
336
|
+
cte.join id: :user_id
|
337
|
+
end
|
338
|
+
|
339
|
+
result = 'WITH "comments" AS (SELECT * FROM comments)'
|
340
|
+
result << ' SELECT "users".*, "comments"."comment" FROM "users"'
|
341
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
342
|
+
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'performs correctly when the proc requires arguments' do
|
346
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
347
|
+
cte.query :comments, -> (args) { Comment.where(id: args.id) }
|
348
|
+
cte.attributes content: :comment
|
349
|
+
cte.join id: :user_id
|
350
|
+
end
|
351
|
+
|
352
|
+
query = subject.with(:comments, args: {id: 1})
|
353
|
+
|
354
|
+
result = 'WITH "comments" AS'
|
355
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment'
|
356
|
+
result << ' FROM "comments" WHERE "comments"."id" = $1)'
|
357
|
+
result << ' SELECT "users".*, "comments"."comment" FROM "users"'
|
358
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
359
|
+
|
360
|
+
expect(query.arel.to_sql).to eql(result)
|
361
|
+
expect(query.send(:bound_attributes).map(&:value_before_type_cast)).to eql([1])
|
362
|
+
end
|
363
|
+
|
364
|
+
it 'raises an error when join columns are not given' do
|
365
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
366
|
+
cte.query :comments, -> { Author.all }
|
367
|
+
cte.attributes content: :comment
|
368
|
+
end
|
369
|
+
|
370
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /join columns/)
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'raises an error when not given the table name as first argument' do
|
374
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
375
|
+
cte.query -> { Comment.all }
|
376
|
+
cte.attributes content: :comment
|
377
|
+
cte.join id: :user_id
|
378
|
+
end
|
379
|
+
|
380
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /table name/)
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'raises an error when the result of the proc is an invalid type' do
|
384
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
385
|
+
cte.query :comments, -> { false }
|
386
|
+
cte.attributes content: :comment
|
387
|
+
cte.join id: :user_id
|
388
|
+
end
|
389
|
+
|
390
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /query objects/)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
context 'with inheritance' do
|
395
|
+
let(:base) { Activity }
|
396
|
+
let(:klass) { ActivityBook }
|
397
|
+
|
398
|
+
it 'accepts ancestors auxiliary statements' do
|
399
|
+
base.send(:auxiliary_statement, :authors) do |cte|
|
400
|
+
cte.query Author.all
|
401
|
+
cte.attributes name: :author_name
|
402
|
+
cte.join author_id: :id
|
403
|
+
end
|
404
|
+
|
405
|
+
result = 'WITH "authors" AS'
|
406
|
+
result << ' (SELECT "authors"."id", "authors"."name" AS author_name FROM "authors")'
|
407
|
+
result << ' SELECT "activity_books".*, "authors"."author_name" FROM "activity_books"'
|
408
|
+
result << ' INNER JOIN "authors" ON "authors"."id" = "activity_books"."author_id"'
|
409
|
+
expect(subject.with(:authors).arel.to_sql).to eql(result)
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'can replace ancestors auxiliary statements' do
|
413
|
+
base.send(:auxiliary_statement, :authors) do |cte|
|
414
|
+
cte.query Author.all
|
415
|
+
cte.attributes name: :author_name
|
416
|
+
cte.join author_id: :id
|
417
|
+
end
|
418
|
+
|
419
|
+
klass.send(:auxiliary_statement, :authors) do |cte|
|
420
|
+
cte.query Author.all
|
421
|
+
cte.attributes type: :author_type
|
422
|
+
cte.join author_id: :id
|
423
|
+
end
|
424
|
+
|
425
|
+
result = 'WITH "authors" AS'
|
426
|
+
result << ' (SELECT "authors"."id", "authors"."type" AS author_type FROM "authors")'
|
427
|
+
result << ' SELECT "activity_books".*, "authors"."author_type" FROM "activity_books"'
|
428
|
+
result << ' INNER JOIN "authors" ON "authors"."id" = "activity_books"."author_id"'
|
429
|
+
expect(subject.with(:authors).arel.to_sql).to eql(result)
|
430
|
+
end
|
431
|
+
|
432
|
+
it 'raises an error when no class has the auxiliary statement' do
|
433
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'works with count and does not add extra columns' do
|
438
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
439
|
+
cte.query Comment.all
|
440
|
+
cte.attributes content: :comment_content
|
441
|
+
end
|
442
|
+
|
443
|
+
result = 'WITH "comments" AS'
|
444
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
|
445
|
+
result << ' SELECT COUNT(*) FROM "users"'
|
446
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
447
|
+
|
448
|
+
query = get_last_executed_query{ subject.with(:comments).count }
|
449
|
+
expect(query).to eql(result)
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'works with sum and does not add extra columns' do
|
453
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
454
|
+
cte.query Comment.all
|
455
|
+
cte.attributes id: :value
|
456
|
+
end
|
457
|
+
|
458
|
+
result = 'WITH "comments" AS'
|
459
|
+
result << ' (SELECT "comments"."user_id", "comments"."id" AS value FROM "comments")'
|
460
|
+
result << ' SELECT SUM("comments"."value") FROM "users"'
|
461
|
+
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
462
|
+
|
463
|
+
query = get_last_executed_query{ subject.with(:comments).sum(comments: :value) }
|
464
|
+
expect(query).to eql(result)
|
465
|
+
end
|
466
|
+
|
467
|
+
it 'raises an error when using an invalid type of object as query' do
|
468
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
469
|
+
cte.query :string, String
|
470
|
+
end
|
471
|
+
|
472
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /object types/)
|
473
|
+
end
|
474
|
+
|
475
|
+
it 'raises an error when traying to use a statement that is not defined' do
|
476
|
+
expect{ subject.with(:does_not_exist).arel.to_sql }.to raise_error(ArgumentError)
|
477
|
+
end
|
478
|
+
|
479
|
+
it 'raises an error when using an invalid type of join' do
|
480
|
+
klass.send(:auxiliary_statement, :comments) do |cte|
|
481
|
+
cte.query Comment.all
|
482
|
+
cte.attributes content: :comment_content
|
483
|
+
cte.join_type :invalid
|
484
|
+
end
|
485
|
+
|
486
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError)
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
context 'on model' do
|
491
|
+
subject { User }
|
492
|
+
|
493
|
+
it 'has its configurator' do
|
494
|
+
expect(subject.protected_methods).to include(:cte)
|
495
|
+
expect(subject.protected_methods).to include(:auxiliary_statement)
|
496
|
+
end
|
497
|
+
|
498
|
+
it 'allows configurate new auxiliary statements' do
|
499
|
+
subject.send(:auxiliary_statement, :cte1)
|
500
|
+
expect(subject.auxiliary_statements_list).to include(:cte1)
|
501
|
+
expect(subject.const_defined?('Cte1_AuxiliaryStatement')).to be_truthy
|
502
|
+
end
|
503
|
+
|
504
|
+
it 'has its query method' do
|
505
|
+
expect(subject).to respond_to(:with)
|
506
|
+
end
|
507
|
+
|
508
|
+
it 'returns a relation when using the method' do
|
509
|
+
subject.send(:auxiliary_statement, :comments) do |cte|
|
510
|
+
cte.query Comment.all
|
511
|
+
cte.attributes content: :comment_content
|
512
|
+
end
|
513
|
+
expect(subject.with(:comments)).to be_a(ActiveRecord::Relation)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
context 'on external' do
|
518
|
+
let(:klass) { Torque::PostgreSQL::AuxiliaryStatement }
|
519
|
+
subject { User }
|
520
|
+
|
521
|
+
it 'has the external method available' do
|
522
|
+
expect(klass).to respond_to(:create)
|
523
|
+
end
|
524
|
+
|
525
|
+
it 'accepts simple auxiliary statement definition' do
|
526
|
+
sample = klass.create(Comment.all)
|
527
|
+
query = subject.with(sample, select: {content: :comment_content}).arel.to_sql
|
528
|
+
|
529
|
+
result = 'WITH "comment" AS'
|
530
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
|
531
|
+
result << ' SELECT "users".*, "comment"."comment_content" FROM "users"'
|
532
|
+
result << ' INNER JOIN "comment" ON "comment"."user_id" = "users"."id"'
|
533
|
+
expect(query).to eql(result)
|
534
|
+
end
|
535
|
+
|
536
|
+
it 'accepts a hash auxiliary statement definition' do
|
537
|
+
sample = klass.create(query: Comment.all, select: {content: :comment_content})
|
538
|
+
query = subject.with(sample).arel.to_sql
|
539
|
+
|
540
|
+
result = 'WITH "comment" AS'
|
541
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
|
542
|
+
result << ' SELECT "users".*, "comment"."comment_content" FROM "users"'
|
543
|
+
result << ' INNER JOIN "comment" ON "comment"."user_id" = "users"."id"'
|
544
|
+
expect(query).to eql(result)
|
545
|
+
end
|
546
|
+
|
547
|
+
it 'accepts a block when creating the auxiliary statement' do
|
548
|
+
sample = klass.create(:all_comments) do |cte|
|
549
|
+
cte.query Comment.all
|
550
|
+
cte.select content: :comment_content
|
551
|
+
end
|
552
|
+
|
553
|
+
result = 'WITH "all_comments" AS'
|
554
|
+
result << ' (SELECT "comments"."user_id", "comments"."content" AS comment_content FROM "comments")'
|
555
|
+
result << ' SELECT "users".*, "all_comments"."comment_content" FROM "users"'
|
556
|
+
result << ' INNER JOIN "all_comments" ON "all_comments"."user_id" = "users"."id"'
|
557
|
+
|
558
|
+
query = subject.with(sample).arel.to_sql
|
559
|
+
expect(query).to eql(result)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
context 'on settings' do
|
564
|
+
let(:base) { User }
|
565
|
+
let(:statement_klass) do
|
566
|
+
base.send(:auxiliary_statement, :statement)
|
567
|
+
base::Statement_AuxiliaryStatement
|
568
|
+
end
|
569
|
+
|
570
|
+
subject do
|
571
|
+
Torque::PostgreSQL::AuxiliaryStatement::Settings.new(base, statement_klass)
|
572
|
+
end
|
573
|
+
|
574
|
+
it 'has access to base' do
|
575
|
+
expect(subject.base).to eql(User)
|
576
|
+
expect(subject.base_table).to be_a(Arel::Table)
|
577
|
+
end
|
578
|
+
|
579
|
+
it 'has access to statement table' do
|
580
|
+
expect(subject.table_name).to eql('statement')
|
581
|
+
expect(subject.table).to be_a(Arel::Table)
|
582
|
+
end
|
583
|
+
|
584
|
+
it 'has access to the query arel table' do
|
585
|
+
subject.query Comment.all
|
586
|
+
expect(subject.query_table).to be_a(Arel::Table)
|
587
|
+
end
|
588
|
+
|
589
|
+
it 'raises an error when trying to access query table before defining the query' do
|
590
|
+
expect{ subject.with(:comments).arel.to_sql }.to raise_error(StandardError)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|