torque-postgresql 2.3.0 → 2.4.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/auxiliary_statement/recursive.rb +149 -0
- data/lib/torque/postgresql/auxiliary_statement/settings.rb +75 -22
- data/lib/torque/postgresql/auxiliary_statement.rb +39 -40
- data/lib/torque/postgresql/base.rb +10 -0
- data/lib/torque/postgresql/config.rb +5 -1
- data/lib/torque/postgresql/inheritance.rb +3 -1
- data/lib/torque/postgresql/railtie.rb +5 -1
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +28 -15
- data/lib/torque/postgresql/version.rb +1 -1
- data/spec/models/category.rb +2 -0
- data/spec/schema.rb +7 -1
- data/spec/tests/auxiliary_statement_spec.rb +374 -35
- metadata +33 -30
@@ -21,7 +21,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
result = 'WITH "comments" AS'
|
24
|
-
result << ' (SELECT "comments"."
|
24
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."user_id" FROM "comments")'
|
25
25
|
result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
|
26
26
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
27
27
|
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
@@ -34,7 +34,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
result = 'WITH "comments" AS (SELECT DISTINCT ON ( "comments"."user_id" )'
|
37
|
-
result << ' "comments"."
|
37
|
+
result << ' "comments"."content" AS last_comment, "comments"."user_id"'
|
38
38
|
result << ' FROM "comments" ORDER BY "comments"."user_id" ASC,'
|
39
39
|
result << ' "comments"."id" DESC) SELECT "users".*,'
|
40
40
|
result << ' "comments"."last_comment" FROM "users" INNER JOIN "comments"'
|
@@ -49,7 +49,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
result = 'WITH "comments" AS'
|
52
|
-
result << ' (SELECT "comments"."
|
52
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."slug" AS comment_slug, "comments"."user_id" FROM "comments")'
|
53
53
|
result << ' SELECT "users".*, "comments"."comment_content", "comments"."comment_slug" FROM "users"'
|
54
54
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
55
55
|
expect(subject.with(:comments, select: {slug: :comment_slug}).arel.to_sql).to eql(result)
|
@@ -62,7 +62,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
result = 'WITH "comments" AS'
|
65
|
-
result << ' (SELECT "comments"."
|
65
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."user_id", "comments"."active" FROM "comments")'
|
66
66
|
result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
|
67
67
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id" AND "comments"."active" = "users"."active"'
|
68
68
|
expect(subject.with(:comments, join: {active: :active}).arel.to_sql).to eql(result)
|
@@ -75,7 +75,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
75
75
|
end
|
76
76
|
|
77
77
|
result = 'WITH "comments" AS'
|
78
|
-
result << ' (SELECT "comments"."
|
78
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."user_id"'
|
79
79
|
result << ' FROM "comments" WHERE "comments"."active" = $1)'
|
80
80
|
result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
|
81
81
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
@@ -91,7 +91,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
91
91
|
query = subject.where(id: 2).with(:comments)
|
92
92
|
|
93
93
|
result = 'WITH "comments" AS'
|
94
|
-
result << ' (SELECT "comments"."
|
94
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."user_id" FROM "comments"'
|
95
95
|
result << ' WHERE "comments"."id" = $1)'
|
96
96
|
result << ' SELECT "users".*, "comments"."comment_content" FROM "users"'
|
97
97
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
@@ -108,7 +108,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
108
108
|
end
|
109
109
|
|
110
110
|
result = 'WITH "comments" AS'
|
111
|
-
result << ' (SELECT
|
111
|
+
result << ' (SELECT MAX(id) AS comment_id, "comments"."user_id" FROM "comments")'
|
112
112
|
result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
|
113
113
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
114
114
|
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
@@ -121,7 +121,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
121
121
|
end
|
122
122
|
|
123
123
|
result = 'WITH "comments" AS'
|
124
|
-
result << ' (SELECT
|
124
|
+
result << ' (SELECT ROW_NUMBER() OVER (PARTITION BY ORDER BY "comments"."id") AS comment_id, "comments"."user_id" FROM "comments")'
|
125
125
|
result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
|
126
126
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
127
127
|
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
@@ -134,7 +134,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
134
134
|
end
|
135
135
|
|
136
136
|
result = 'WITH "comments" AS'
|
137
|
-
result << ' (SELECT "comments"."
|
137
|
+
result << ' (SELECT MIN("comments"."id") AS comment_id, "comments"."user_id" FROM "comments")'
|
138
138
|
result << ' SELECT "users".*, "comments"."comment_id" FROM "users"'
|
139
139
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
140
140
|
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
@@ -147,8 +147,8 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
147
147
|
cte.join name: :id, 'a.col' => :col
|
148
148
|
end
|
149
149
|
|
150
|
-
result = 'WITH "comments" AS (SELECT "comments"."
|
151
|
-
result << ' "comments"."
|
150
|
+
result = 'WITH "comments" AS (SELECT "comments"."content" AS comment_content,'
|
151
|
+
result << ' "comments"."id", "comments"."col" FROM "comments") SELECT "users".*,'
|
152
152
|
result << ' "comments"."comment_content" FROM "users" INNER JOIN "comments"'
|
153
153
|
result << ' ON "comments"."id" = "users"."name" AND "comments"."col" = "a"."col"'
|
154
154
|
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
@@ -161,8 +161,8 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
161
161
|
cte.join_type :left
|
162
162
|
end
|
163
163
|
|
164
|
-
result = 'WITH "comments" AS (SELECT "comments"."
|
165
|
-
result << ' "comments"."
|
164
|
+
result = 'WITH "comments" AS (SELECT "comments"."content" AS comment_content,'
|
165
|
+
result << ' "comments"."user_id" FROM "comments") SELECT "users".*,'
|
166
166
|
result << ' "comments"."comment_content" FROM "users" LEFT OUTER JOIN "comments"'
|
167
167
|
result << ' ON "comments"."user_id" = "users"."id"'
|
168
168
|
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
@@ -177,7 +177,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
177
177
|
end
|
178
178
|
|
179
179
|
result = 'WITH "comments" AS'
|
180
|
-
result << ' (SELECT "comments"."
|
180
|
+
result << ' (SELECT "comments"."content" AS sample_content, "comments"."a_user_id" FROM "comments")'
|
181
181
|
result << ' SELECT "users".*, "comments"."sample_content" FROM "users"'
|
182
182
|
result << ' INNER JOIN "comments" ON "comments"."a_user_id" = "users"."id"'
|
183
183
|
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
@@ -198,8 +198,8 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
198
198
|
query = subject.where(id: 3).with(:comments2)
|
199
199
|
|
200
200
|
result = 'WITH '
|
201
|
-
result << '"comments1" AS (SELECT "comments"."
|
202
|
-
result << '"comments2" AS (SELECT "comments"."
|
201
|
+
result << '"comments1" AS (SELECT "comments"."content" AS comment_content1, "comments"."user_id" FROM "comments" WHERE "comments"."id" = $1), '
|
202
|
+
result << '"comments2" AS (SELECT "comments"."content" AS comment_content2, "comments"."user_id" FROM "comments" WHERE "comments"."id" = $2)'
|
203
203
|
result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
|
204
204
|
result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
|
205
205
|
result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
|
@@ -225,8 +225,8 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
225
225
|
|
226
226
|
it 'can requires another statement as dependency' do
|
227
227
|
result = 'WITH '
|
228
|
-
result << '"comments1" AS (SELECT "comments"."
|
229
|
-
result << '"comments2" AS (SELECT "comments"."
|
228
|
+
result << '"comments1" AS (SELECT "comments"."content" AS comment_content1, "comments"."user_id" FROM "comments"), '
|
229
|
+
result << '"comments2" AS (SELECT "comments"."content" AS comment_content2, "comments"."user_id" FROM "comments")'
|
230
230
|
result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
|
231
231
|
result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
|
232
232
|
result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
|
@@ -235,8 +235,8 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
235
235
|
|
236
236
|
it 'can uses already already set dependent' do
|
237
237
|
result = 'WITH '
|
238
|
-
result << '"comments1" AS (SELECT "comments"."
|
239
|
-
result << '"comments2" AS (SELECT "comments"."
|
238
|
+
result << '"comments1" AS (SELECT "comments"."content" AS comment_content1, "comments"."user_id" FROM "comments"), '
|
239
|
+
result << '"comments2" AS (SELECT "comments"."content" AS comment_content2, "comments"."user_id" FROM "comments")'
|
240
240
|
result << ' SELECT "users".*, "comments1"."comment_content1", "comments2"."comment_content2" FROM "users"'
|
241
241
|
result << ' INNER JOIN "comments1" ON "comments1"."user_id" = "users"."id"'
|
242
242
|
result << ' INNER JOIN "comments2" ON "comments2"."user_id" = "users"."id"'
|
@@ -289,14 +289,14 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
289
289
|
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /join columns/)
|
290
290
|
end
|
291
291
|
|
292
|
-
it 'raises an error when not given the table name as first argument' do
|
292
|
+
it 'not raises an error when not given the table name as first argument' do
|
293
293
|
klass.send(:auxiliary_statement, :comments) do |cte|
|
294
294
|
cte.query 'SELECT * FROM comments'
|
295
295
|
cte.attributes content: :comment
|
296
296
|
cte.join id: :user_id
|
297
297
|
end
|
298
298
|
|
299
|
-
expect{ subject.with(:comments).arel.to_sql }.
|
299
|
+
expect{ subject.with(:comments).arel.to_sql }.not_to raise_error
|
300
300
|
end
|
301
301
|
end
|
302
302
|
|
@@ -309,7 +309,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
309
309
|
end
|
310
310
|
|
311
311
|
result = 'WITH "comments" AS'
|
312
|
-
result << ' (SELECT "comments"."
|
312
|
+
result << ' (SELECT "comments"."content" AS comment, "comments"."user_id" FROM "comments")'
|
313
313
|
result << ' SELECT "users".*, "comments"."comment" FROM "users"'
|
314
314
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
315
315
|
expect(subject.with(:comments).arel.to_sql).to eql(result)
|
@@ -352,7 +352,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
352
352
|
query = subject.with(:comments, args: {id: 1})
|
353
353
|
|
354
354
|
result = 'WITH "comments" AS'
|
355
|
-
result << ' (SELECT "comments"."
|
355
|
+
result << ' (SELECT "comments"."content" AS comment, "comments"."user_id"'
|
356
356
|
result << ' FROM "comments" WHERE "comments"."id" = $1)'
|
357
357
|
result << ' SELECT "users".*, "comments"."comment" FROM "users"'
|
358
358
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
@@ -370,14 +370,14 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
370
370
|
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /join columns/)
|
371
371
|
end
|
372
372
|
|
373
|
-
it 'raises an error when not given the table name as first argument' do
|
373
|
+
it 'not raises an error when not given the table name as first argument' do
|
374
374
|
klass.send(:auxiliary_statement, :comments) do |cte|
|
375
375
|
cte.query -> { Comment.all }
|
376
376
|
cte.attributes content: :comment
|
377
377
|
cte.join id: :user_id
|
378
378
|
end
|
379
379
|
|
380
|
-
expect{ subject.with(:comments).arel.to_sql }.
|
380
|
+
expect{ subject.with(:comments).arel.to_sql }.not_to raise_error
|
381
381
|
end
|
382
382
|
|
383
383
|
it 'raises an error when the result of the proc is an invalid type' do
|
@@ -403,7 +403,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
403
403
|
end
|
404
404
|
|
405
405
|
result = 'WITH "authors" AS'
|
406
|
-
result << ' (SELECT "authors"."
|
406
|
+
result << ' (SELECT "authors"."name" AS author_name, "authors"."id" FROM "authors")'
|
407
407
|
result << ' SELECT "activity_books".*, "authors"."author_name" FROM "activity_books"'
|
408
408
|
result << ' INNER JOIN "authors" ON "authors"."id" = "activity_books"."author_id"'
|
409
409
|
expect(subject.with(:authors).arel.to_sql).to eql(result)
|
@@ -423,7 +423,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
423
423
|
end
|
424
424
|
|
425
425
|
result = 'WITH "authors" AS'
|
426
|
-
result << ' (SELECT "authors"."
|
426
|
+
result << ' (SELECT "authors"."type" AS author_type, "authors"."id" FROM "authors")'
|
427
427
|
result << ' SELECT "activity_books".*, "authors"."author_type" FROM "activity_books"'
|
428
428
|
result << ' INNER JOIN "authors" ON "authors"."id" = "activity_books"."author_id"'
|
429
429
|
expect(subject.with(:authors).arel.to_sql).to eql(result)
|
@@ -434,6 +434,233 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
434
434
|
end
|
435
435
|
end
|
436
436
|
|
437
|
+
context 'recursive' do
|
438
|
+
let(:klass) { Course }
|
439
|
+
|
440
|
+
it 'correctly build a recursive cte' do
|
441
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
442
|
+
cte.query Category.all
|
443
|
+
cte.join id: :parent_id
|
444
|
+
end
|
445
|
+
|
446
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
447
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
448
|
+
result << ' FROM "categories"'
|
449
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
450
|
+
result << ' UNION'
|
451
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
452
|
+
result << ' FROM "categories", "all_categories"'
|
453
|
+
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
454
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
455
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
456
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
457
|
+
end
|
458
|
+
|
459
|
+
it 'allows connect to be set to something different using a single value' do
|
460
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
461
|
+
cte.query Category.all
|
462
|
+
cte.join id: :parent_id
|
463
|
+
cte.connect :name
|
464
|
+
end
|
465
|
+
|
466
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
467
|
+
result << ' SELECT "categories"."name", "categories"."parent_id"'
|
468
|
+
result << ' FROM "categories"'
|
469
|
+
result << ' WHERE "categories"."parent_name" IS NULL'
|
470
|
+
result << ' UNION'
|
471
|
+
result << ' SELECT "categories"."name", "categories"."parent_id"'
|
472
|
+
result << ' FROM "categories", "all_categories"'
|
473
|
+
result << ' WHERE "categories"."parent_name" = "all_categories"."name"'
|
474
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
475
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
476
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
477
|
+
end
|
478
|
+
|
479
|
+
it 'allows a complete different set of connect' do
|
480
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
481
|
+
cte.query Category.all
|
482
|
+
cte.join id: :parent_id
|
483
|
+
cte.connect left: :right
|
484
|
+
end
|
485
|
+
|
486
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
487
|
+
result << ' SELECT "categories"."left", "categories"."parent_id"'
|
488
|
+
result << ' FROM "categories"'
|
489
|
+
result << ' WHERE "categories"."right" IS NULL'
|
490
|
+
result << ' UNION'
|
491
|
+
result << ' SELECT "categories"."left", "categories"."parent_id"'
|
492
|
+
result << ' FROM "categories", "all_categories"'
|
493
|
+
result << ' WHERE "categories"."right" = "all_categories"."left"'
|
494
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
495
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
496
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
497
|
+
end
|
498
|
+
|
499
|
+
it 'allows using an union all' do
|
500
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
501
|
+
cte.query Category.all
|
502
|
+
cte.join id: :parent_id
|
503
|
+
cte.union_all!
|
504
|
+
end
|
505
|
+
|
506
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
507
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
508
|
+
result << ' FROM "categories"'
|
509
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
510
|
+
result << ' UNION ALL'
|
511
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
512
|
+
result << ' FROM "categories", "all_categories"'
|
513
|
+
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
514
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
515
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
516
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
517
|
+
end
|
518
|
+
|
519
|
+
it 'allows having a complete different initiator' do
|
520
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
521
|
+
cte.query Category.where(parent_id: 5)
|
522
|
+
cte.join id: :parent_id
|
523
|
+
end
|
524
|
+
|
525
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
526
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
527
|
+
result << ' FROM "categories"'
|
528
|
+
result << ' WHERE "categories"."parent_id" = $1'
|
529
|
+
result << ' UNION'
|
530
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
531
|
+
result << ' FROM "categories", "all_categories"'
|
532
|
+
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
533
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
534
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
535
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
536
|
+
end
|
537
|
+
|
538
|
+
it 'can process the depth of the query' do
|
539
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
540
|
+
cte.query Category.all
|
541
|
+
cte.join id: :parent_id
|
542
|
+
cte.with_depth
|
543
|
+
end
|
544
|
+
|
545
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
546
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", 0 AS depth'
|
547
|
+
result << ' FROM "categories"'
|
548
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
549
|
+
result << ' UNION'
|
550
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ("all_categories"."depth" + 1) AS depth'
|
551
|
+
result << ' FROM "categories", "all_categories"'
|
552
|
+
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
553
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
554
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
555
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
556
|
+
end
|
557
|
+
|
558
|
+
it 'can process and expose the depth of the query' do
|
559
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
560
|
+
cte.query Category.all
|
561
|
+
cte.join id: :parent_id
|
562
|
+
cte.with_depth 'd', start: 10, as: :category_depth
|
563
|
+
end
|
564
|
+
|
565
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
566
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", 10 AS d'
|
567
|
+
result << ' FROM "categories"'
|
568
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
569
|
+
result << ' UNION'
|
570
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ("all_categories"."d" + 1) AS d'
|
571
|
+
result << ' FROM "categories", "all_categories"'
|
572
|
+
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
573
|
+
result << ' ) SELECT "courses".*, "all_categories"."d" AS category_depth FROM "courses" INNER JOIN "all_categories"'
|
574
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
575
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
576
|
+
end
|
577
|
+
|
578
|
+
it 'can process the path of the query' do
|
579
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
580
|
+
cte.query Category.all
|
581
|
+
cte.join id: :parent_id
|
582
|
+
cte.with_path
|
583
|
+
end
|
584
|
+
|
585
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
586
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY["categories"."id"]::varchar[] AS path'
|
587
|
+
result << ' FROM "categories"'
|
588
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
589
|
+
result << ' UNION'
|
590
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", array_append("all_categories"."path", "categories"."id"::varchar) AS path'
|
591
|
+
result << ' FROM "categories", "all_categories"'
|
592
|
+
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
593
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
594
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
595
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
596
|
+
end
|
597
|
+
|
598
|
+
it 'can process and expose the path of the query' do
|
599
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
600
|
+
cte.query Category.all
|
601
|
+
cte.join id: :parent_id
|
602
|
+
cte.with_path 'p', source: :name, as: :category_path
|
603
|
+
end
|
604
|
+
|
605
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
606
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY["categories"."name"]::varchar[] AS p'
|
607
|
+
result << ' FROM "categories"'
|
608
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
609
|
+
result << ' UNION'
|
610
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", array_append("all_categories"."p", "categories"."name"::varchar) AS p'
|
611
|
+
result << ' FROM "categories", "all_categories"'
|
612
|
+
result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
|
613
|
+
result << ' ) SELECT "courses".*, "all_categories"."p" AS category_path FROM "courses" INNER JOIN "all_categories"'
|
614
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
615
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
616
|
+
end
|
617
|
+
|
618
|
+
it 'works with string queries' do
|
619
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
620
|
+
cte.query 'SELECT * FROM categories WHERE a IS NULL'
|
621
|
+
cte.sub_query 'SELECT * FROM categories, all_categories WHERE all_categories.a = b'
|
622
|
+
cte.join id: :parent_id
|
623
|
+
end
|
624
|
+
|
625
|
+
result = 'WITH RECURSIVE "all_categories" AS ('
|
626
|
+
result << 'SELECT * FROM categories WHERE a IS NULL'
|
627
|
+
result << ' UNION '
|
628
|
+
result << ' SELECT * FROM categories, all_categories WHERE all_categories.a = b'
|
629
|
+
result << ') SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
|
630
|
+
result << ' ON "all_categories"."parent_id" = "courses"."id"'
|
631
|
+
expect(subject.with(:all_categories).arel.to_sql).to eql(result)
|
632
|
+
end
|
633
|
+
|
634
|
+
it 'raises an error when query is a string and there is no sub query' do
|
635
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
636
|
+
cte.query 'SELECT * FROM categories WHERE a IS NULL'
|
637
|
+
cte.join id: :parent_id
|
638
|
+
end
|
639
|
+
|
640
|
+
expect{ subject.with(:all_categories).arel.to_sql }.to raise_error(ArgumentError, /generate sub query/)
|
641
|
+
end
|
642
|
+
|
643
|
+
it 'raises an error when sub query has an invalid type' do
|
644
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
645
|
+
cte.query 'SELECT * FROM categories WHERE a IS NULL'
|
646
|
+
cte.sub_query -> { 1 }
|
647
|
+
cte.join id: :parent_id
|
648
|
+
end
|
649
|
+
|
650
|
+
expect{ subject.with(:all_categories).arel.to_sql }.to raise_error(ArgumentError, /query and sub query objects/)
|
651
|
+
end
|
652
|
+
|
653
|
+
it 'raises an error when connect can be resolved automatically' do
|
654
|
+
allow(klass).to receive(:primary_key).and_return(nil)
|
655
|
+
klass.send(:recursive_auxiliary_statement, :all_categories) do |cte|
|
656
|
+
cte.query Category.all
|
657
|
+
cte.join id: :parent_id
|
658
|
+
end
|
659
|
+
|
660
|
+
expect{ subject.with(:all_categories).arel.to_sql }.to raise_error(ArgumentError, /setting up a proper way to connect/)
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
437
664
|
it 'works with count and does not add extra columns' do
|
438
665
|
klass.send(:auxiliary_statement, :comments) do |cte|
|
439
666
|
cte.query Comment.all
|
@@ -441,7 +668,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
441
668
|
end
|
442
669
|
|
443
670
|
result = 'WITH "comments" AS'
|
444
|
-
result << ' (SELECT "comments"."
|
671
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."user_id" FROM "comments")'
|
445
672
|
result << ' SELECT COUNT(*) FROM "users"'
|
446
673
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
447
674
|
|
@@ -456,7 +683,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
456
683
|
end
|
457
684
|
|
458
685
|
result = 'WITH "comments" AS'
|
459
|
-
result << ' (SELECT "comments"."
|
686
|
+
result << ' (SELECT "comments"."id" AS value, "comments"."user_id" FROM "comments")'
|
460
687
|
result << ' SELECT SUM("comments"."value") FROM "users"'
|
461
688
|
result << ' INNER JOIN "comments" ON "comments"."user_id" = "users"."id"'
|
462
689
|
|
@@ -472,7 +699,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
472
699
|
expect{ subject.with(:comments).arel.to_sql }.to raise_error(ArgumentError, /object types/)
|
473
700
|
end
|
474
701
|
|
475
|
-
it 'raises an error when
|
702
|
+
it 'raises an error when trying to use a statement that is not defined' do
|
476
703
|
expect{ subject.with(:does_not_exist).arel.to_sql }.to raise_error(ArgumentError)
|
477
704
|
end
|
478
705
|
|
@@ -495,7 +722,12 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
495
722
|
expect(subject.protected_methods).to include(:auxiliary_statement)
|
496
723
|
end
|
497
724
|
|
498
|
-
it '
|
725
|
+
it 'has the recursive configuration' do
|
726
|
+
expect(subject.protected_methods).to include(:recursive_cte)
|
727
|
+
expect(subject.protected_methods).to include(:recursive_auxiliary_statement)
|
728
|
+
end
|
729
|
+
|
730
|
+
it 'allows configure new auxiliary statements' do
|
499
731
|
subject.send(:auxiliary_statement, :cte1)
|
500
732
|
expect(subject.auxiliary_statements_list).to include(:cte1)
|
501
733
|
expect(subject.const_defined?('Cte1_AuxiliaryStatement')).to be_truthy
|
@@ -527,7 +759,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
527
759
|
query = subject.with(sample, select: {content: :comment_content}).arel.to_sql
|
528
760
|
|
529
761
|
result = 'WITH "comment" AS'
|
530
|
-
result << ' (SELECT "comments"."
|
762
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."user_id" FROM "comments")'
|
531
763
|
result << ' SELECT "users".*, "comment"."comment_content" FROM "users"'
|
532
764
|
result << ' INNER JOIN "comment" ON "comment"."user_id" = "users"."id"'
|
533
765
|
expect(query).to eql(result)
|
@@ -538,7 +770,7 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
538
770
|
query = subject.with(sample).arel.to_sql
|
539
771
|
|
540
772
|
result = 'WITH "comment" AS'
|
541
|
-
result << ' (SELECT "comments"."
|
773
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."user_id" FROM "comments")'
|
542
774
|
result << ' SELECT "users".*, "comment"."comment_content" FROM "users"'
|
543
775
|
result << ' INNER JOIN "comment" ON "comment"."user_id" = "users"."id"'
|
544
776
|
expect(query).to eql(result)
|
@@ -551,13 +783,120 @@ RSpec.describe 'AuxiliaryStatement' do
|
|
551
783
|
end
|
552
784
|
|
553
785
|
result = 'WITH "all_comments" AS'
|
554
|
-
result << ' (SELECT "comments"."
|
786
|
+
result << ' (SELECT "comments"."content" AS comment_content, "comments"."user_id" FROM "comments")'
|
555
787
|
result << ' SELECT "users".*, "all_comments"."comment_content" FROM "users"'
|
556
788
|
result << ' INNER JOIN "all_comments" ON "all_comments"."user_id" = "users"."id"'
|
557
789
|
|
558
790
|
query = subject.with(sample).arel.to_sql
|
559
791
|
expect(query).to eql(result)
|
560
792
|
end
|
793
|
+
|
794
|
+
context 'recursive' do
|
795
|
+
let(:klass) { Torque::PostgreSQL::AuxiliaryStatement::Recursive }
|
796
|
+
subject { Course }
|
797
|
+
|
798
|
+
it 'has the external method available' do
|
799
|
+
expect(klass).to respond_to(:create)
|
800
|
+
end
|
801
|
+
|
802
|
+
it 'accepts simple recursive auxiliary statement definition' do
|
803
|
+
settings = { join: { id: :parent_id } }
|
804
|
+
query = subject.with(klass.create(Category.all), **settings).arel.to_sql
|
805
|
+
|
806
|
+
result = 'WITH RECURSIVE "category" AS ('
|
807
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
808
|
+
result << ' FROM "categories"'
|
809
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
810
|
+
result << ' UNION'
|
811
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
812
|
+
result << ' FROM "categories", "category"'
|
813
|
+
result << ' WHERE "categories"."parent_id" = "category"."id"'
|
814
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "category"'
|
815
|
+
result << ' ON "category"."parent_id" = "courses"."id"'
|
816
|
+
expect(query).to eql(result)
|
817
|
+
end
|
818
|
+
|
819
|
+
it 'accepts a connect option' do
|
820
|
+
settings = { join: { id: :parent_id }, connect: { a: :b } }
|
821
|
+
query = subject.with(klass.create(Category.all), **settings).arel.to_sql
|
822
|
+
|
823
|
+
result = 'WITH RECURSIVE "category" AS ('
|
824
|
+
result << ' SELECT "categories"."a", "categories"."parent_id"'
|
825
|
+
result << ' FROM "categories"'
|
826
|
+
result << ' WHERE "categories"."b" IS NULL'
|
827
|
+
result << ' UNION'
|
828
|
+
result << ' SELECT "categories"."a", "categories"."parent_id"'
|
829
|
+
result << ' FROM "categories", "category"'
|
830
|
+
result << ' WHERE "categories"."b" = "category"."a"'
|
831
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "category"'
|
832
|
+
result << ' ON "category"."parent_id" = "courses"."id"'
|
833
|
+
expect(query).to eql(result)
|
834
|
+
end
|
835
|
+
|
836
|
+
it 'accepts an union all option' do
|
837
|
+
settings = { join: { id: :parent_id }, union_all: true }
|
838
|
+
query = subject.with(klass.create(Category.all), **settings).arel.to_sql
|
839
|
+
|
840
|
+
result = 'WITH RECURSIVE "category" AS ('
|
841
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
842
|
+
result << ' FROM "categories"'
|
843
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
844
|
+
result << ' UNION ALL'
|
845
|
+
result << ' SELECT "categories"."id", "categories"."parent_id"'
|
846
|
+
result << ' FROM "categories", "category"'
|
847
|
+
result << ' WHERE "categories"."parent_id" = "category"."id"'
|
848
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "category"'
|
849
|
+
result << ' ON "category"."parent_id" = "courses"."id"'
|
850
|
+
expect(query).to eql(result)
|
851
|
+
end
|
852
|
+
|
853
|
+
it 'accepts a sub query option' do
|
854
|
+
settings = { join: { id: :parent_id }, sub_query: Category.where(active: true) }
|
855
|
+
query = subject.with(klass.create(Category.all), **settings).arel.to_sql
|
856
|
+
|
857
|
+
result = 'WITH RECURSIVE "category" AS ('
|
858
|
+
result << ' SELECT "categories"."id", "categories"."parent_id" FROM "categories"'
|
859
|
+
result << ' UNION'
|
860
|
+
result << ' SELECT "categories"."id", "categories"."parent_id" FROM "categories", "category" WHERE "categories"."active" = $1'
|
861
|
+
result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "category"'
|
862
|
+
result << ' ON "category"."parent_id" = "courses"."id"'
|
863
|
+
expect(query).to eql(result)
|
864
|
+
end
|
865
|
+
|
866
|
+
it 'accepts a depth option' do
|
867
|
+
settings = { join: { id: :parent_id }, with_depth: { name: 'a', start: 5, as: 'b' } }
|
868
|
+
query = subject.with(klass.create(Category.all), **settings).arel.to_sql
|
869
|
+
|
870
|
+
result = 'WITH RECURSIVE "category" AS ('
|
871
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", 5 AS a'
|
872
|
+
result << ' FROM "categories"'
|
873
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
874
|
+
result << ' UNION'
|
875
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ("category"."a" + 1) AS a'
|
876
|
+
result << ' FROM "categories", "category"'
|
877
|
+
result << ' WHERE "categories"."parent_id" = "category"."id"'
|
878
|
+
result << ' ) SELECT "courses".*, "category"."a" AS b FROM "courses" INNER JOIN "category"'
|
879
|
+
result << ' ON "category"."parent_id" = "courses"."id"'
|
880
|
+
expect(query).to eql(result)
|
881
|
+
end
|
882
|
+
|
883
|
+
it 'accepts a path option' do
|
884
|
+
settings = { join: { id: :parent_id }, with_path: { name: 'a', source: 'b', as: 'c' } }
|
885
|
+
query = subject.with(klass.create(Category.all), **settings).arel.to_sql
|
886
|
+
|
887
|
+
result = 'WITH RECURSIVE "category" AS ('
|
888
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY["categories"."b"]::varchar[] AS a'
|
889
|
+
result << ' FROM "categories"'
|
890
|
+
result << ' WHERE "categories"."parent_id" IS NULL'
|
891
|
+
result << ' UNION'
|
892
|
+
result << ' SELECT "categories"."id", "categories"."parent_id", array_append("category"."a", "categories"."b"::varchar) AS a'
|
893
|
+
result << ' FROM "categories", "category"'
|
894
|
+
result << ' WHERE "categories"."parent_id" = "category"."id"'
|
895
|
+
result << ' ) SELECT "courses".*, "category"."a" AS c FROM "courses" INNER JOIN "category"'
|
896
|
+
result << ' ON "category"."parent_id" = "courses"."id"'
|
897
|
+
expect(query).to eql(result)
|
898
|
+
end
|
899
|
+
end
|
561
900
|
end
|
562
901
|
|
563
902
|
context 'on settings' do
|