torque-postgresql 3.0.1 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +17 -0
- data/lib/torque/postgresql/adapter/database_statements.rb +61 -7
- data/lib/torque/postgresql/adapter/schema_creation.rb +3 -9
- data/lib/torque/postgresql/adapter/schema_dumper.rb +39 -7
- data/lib/torque/postgresql/adapter/schema_statements.rb +40 -0
- data/lib/torque/postgresql/adapter.rb +2 -2
- data/lib/torque/postgresql/associations/preloader/loader_query.rb +1 -1
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +149 -0
- data/lib/torque/postgresql/auxiliary_statement/settings.rb +74 -22
- data/lib/torque/postgresql/auxiliary_statement.rb +39 -40
- data/lib/torque/postgresql/base.rb +29 -25
- data/lib/torque/postgresql/config.rb +17 -0
- data/lib/torque/postgresql/inheritance.rb +3 -1
- data/lib/torque/postgresql/migration/command_recorder.rb +8 -8
- data/lib/torque/postgresql/railtie.rb +5 -1
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +28 -15
- data/lib/torque/postgresql/schema_cache.rb +6 -1
- data/lib/torque/postgresql/table_name.rb +41 -0
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql.rb +2 -1
- data/spec/models/category.rb +2 -0
- data/spec/models/internal/user.rb +5 -0
- data/spec/schema.rb +16 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/tests/auxiliary_statement_spec.rb +374 -35
- data/spec/tests/enum_set_spec.rb +7 -6
- data/spec/tests/schema_spec.rb +92 -0
- data/spec/tests/table_inheritance_spec.rb +11 -15
- metadata +17 -5
@@ -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
|