torque-postgresql 3.4.1 → 4.0.0.rc1
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/adapter/database_statements.rb +63 -84
- data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +2 -6
- data/lib/torque/postgresql/adapter/oid/range.rb +4 -4
- data/lib/torque/postgresql/adapter/oid.rb +1 -23
- data/lib/torque/postgresql/adapter/quoting.rb +13 -7
- data/lib/torque/postgresql/adapter/schema_creation.rb +7 -28
- data/lib/torque/postgresql/adapter/schema_definitions.rb +36 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +90 -34
- data/lib/torque/postgresql/adapter/schema_overrides.rb +45 -0
- data/lib/torque/postgresql/adapter/schema_statements.rb +64 -49
- data/lib/torque/postgresql/arel/infix_operation.rb +15 -28
- data/lib/torque/postgresql/arel/nodes.rb +2 -2
- data/lib/torque/postgresql/arel/operations.rb +7 -1
- data/lib/torque/postgresql/arel/visitors.rb +3 -9
- data/lib/torque/postgresql/associations/association_scope.rb +23 -31
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +25 -0
- data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +16 -0
- data/lib/torque/postgresql/attributes/builder/enum.rb +12 -9
- data/lib/torque/postgresql/attributes/builder/full_text_search.rb +121 -0
- data/lib/torque/postgresql/attributes/builder/period.rb +21 -21
- data/lib/torque/postgresql/attributes/builder.rb +49 -11
- data/lib/torque/postgresql/attributes/enum.rb +7 -7
- data/lib/torque/postgresql/attributes/enum_set.rb +7 -7
- data/lib/torque/postgresql/attributes/full_text_search.rb +19 -0
- data/lib/torque/postgresql/attributes/period.rb +2 -2
- data/lib/torque/postgresql/attributes.rb +0 -4
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +3 -3
- data/lib/torque/postgresql/base.rb +3 -10
- data/lib/torque/postgresql/collector.rb +1 -1
- data/lib/torque/postgresql/config.rb +95 -5
- data/lib/torque/postgresql/function.rb +61 -0
- data/lib/torque/postgresql/inheritance.rb +52 -36
- data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +33 -0
- data/lib/torque/postgresql/predicate_builder/array_handler.rb +47 -0
- data/lib/torque/postgresql/predicate_builder/enumerator_lazy_handler.rb +37 -0
- data/lib/torque/postgresql/predicate_builder/regexp_handler.rb +21 -0
- data/lib/torque/postgresql/predicate_builder.rb +35 -0
- data/lib/torque/postgresql/railtie.rb +112 -30
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +12 -44
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/has_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/runtime_reflection.rb +1 -1
- data/lib/torque/postgresql/relation/inheritance.rb +4 -7
- data/lib/torque/postgresql/relation.rb +6 -10
- data/lib/torque/postgresql/schema_cache.rb +6 -12
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql.rb +2 -1
- data/spec/initialize.rb +58 -0
- data/spec/mocks/cache_query.rb +21 -21
- data/spec/mocks/create_table.rb +6 -26
- data/spec/schema.rb +19 -12
- data/spec/spec_helper.rb +5 -1
- data/spec/tests/arel_spec.rb +32 -7
- data/spec/tests/auxiliary_statement_spec.rb +3 -3
- data/spec/tests/belongs_to_many_spec.rb +72 -5
- data/spec/tests/enum_set_spec.rb +12 -11
- data/spec/tests/enum_spec.rb +4 -2
- data/spec/tests/full_text_seach_test.rb +252 -0
- data/spec/tests/function_spec.rb +42 -0
- data/spec/tests/has_many_spec.rb +21 -8
- data/spec/tests/interval_spec.rb +1 -7
- data/spec/tests/period_spec.rb +61 -61
- data/spec/tests/predicate_builder_spec.rb +132 -0
- data/spec/tests/schema_spec.rb +2 -8
- data/spec/tests/table_inheritance_spec.rb +25 -26
- metadata +34 -39
data/spec/tests/period_spec.rb
CHANGED
@@ -75,26 +75,26 @@ RSpec.describe 'Period' do
|
|
75
75
|
|
76
76
|
let(:cast_type) { '::timestamp' }
|
77
77
|
let(:cast_db_value) { "#{db_value}#{cast_type}" }
|
78
|
-
let(:empty_condition) { "#{type}(NULL, NULL)" }
|
79
|
-
let(:nullif_condition) { "
|
78
|
+
let(:empty_condition) { "#{type.to_s.upcase}(NULL, NULL)" }
|
79
|
+
let(:nullif_condition) { "NULLIF(#{db_field}, #{empty_condition})" }
|
80
80
|
|
81
81
|
let(:date_type) { :daterange }
|
82
|
-
let(:lower_date) { "
|
83
|
-
let(:upper_date) { "
|
84
|
-
let(:date_db_field) { "#{date_type}(#{lower_date}, #{upper_date}, '[]')" }
|
82
|
+
let(:lower_date) { "LOWER(#{db_field})::date" }
|
83
|
+
let(:upper_date) { "UPPER(#{db_field})::date" }
|
84
|
+
let(:date_db_field) { "#{date_type.to_s.upcase}(#{lower_date}, #{upper_date}, '[]')" }
|
85
85
|
|
86
86
|
context 'on model' do
|
87
87
|
before { decorate(model, :period) }
|
88
88
|
|
89
89
|
it 'queries current on period' do
|
90
90
|
expect(model.period_on(value).to_sql).to include(<<-SQL.squish)
|
91
|
-
|
91
|
+
COALESCE(#{nullif_condition} @> #{cast_db_value}, #{true_value})
|
92
92
|
SQL
|
93
93
|
end
|
94
94
|
|
95
95
|
it 'queries current period' do
|
96
96
|
expect(model.current_period.to_sql).to include(<<-SQL.squish)
|
97
|
-
|
97
|
+
COALESCE(#{nullif_condition} @>
|
98
98
|
SQL
|
99
99
|
|
100
100
|
expect(model.current_period.to_sql).to include(<<-SQL.squish)
|
@@ -104,7 +104,7 @@ RSpec.describe 'Period' do
|
|
104
104
|
|
105
105
|
it 'queries not current period' do
|
106
106
|
expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
|
107
|
-
NOT (
|
107
|
+
NOT (COALESCE(#{nullif_condition} @>
|
108
108
|
SQL
|
109
109
|
|
110
110
|
expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
|
@@ -138,7 +138,7 @@ RSpec.describe 'Period' do
|
|
138
138
|
SQL
|
139
139
|
|
140
140
|
expect(model.period_overlapping(value, value).to_sql).to include(<<-SQL.squish)
|
141
|
-
#{db_field} && #{type}(#{db_value}, #{db_value})
|
141
|
+
#{db_field} && #{type.to_s.upcase}(#{db_value}, #{db_value})
|
142
142
|
SQL
|
143
143
|
end
|
144
144
|
|
@@ -148,47 +148,47 @@ RSpec.describe 'Period' do
|
|
148
148
|
SQL
|
149
149
|
|
150
150
|
expect(model.period_not_overlapping(value, value).to_sql).to include(<<-SQL.squish)
|
151
|
-
NOT (#{db_field} && #{type}(#{db_value}, #{db_value}))
|
151
|
+
NOT (#{db_field} && #{type.to_s.upcase}(#{db_value}, #{db_value}))
|
152
152
|
SQL
|
153
153
|
end
|
154
154
|
|
155
155
|
it 'queries starting after period' do
|
156
156
|
expect(model.period_starting_after(:test).to_sql).to include(<<-SQL.squish)
|
157
|
-
|
157
|
+
LOWER(#{db_field}) > "time_keepers"."test"
|
158
158
|
SQL
|
159
159
|
|
160
160
|
expect(model.period_starting_after(value).to_sql).to include(<<-SQL.squish)
|
161
|
-
|
161
|
+
LOWER(#{db_field}) > #{db_value}
|
162
162
|
SQL
|
163
163
|
end
|
164
164
|
|
165
165
|
it 'queries starting before period' do
|
166
166
|
expect(model.period_starting_before(:test).to_sql).to include(<<-SQL.squish)
|
167
|
-
|
167
|
+
LOWER(#{db_field}) < "time_keepers"."test"
|
168
168
|
SQL
|
169
169
|
|
170
170
|
expect(model.period_starting_before(value).to_sql).to include(<<-SQL.squish)
|
171
|
-
|
171
|
+
LOWER(#{db_field}) < #{db_value}
|
172
172
|
SQL
|
173
173
|
end
|
174
174
|
|
175
175
|
it 'queries finishing after period' do
|
176
176
|
expect(model.period_finishing_after(:test).to_sql).to include(<<-SQL.squish)
|
177
|
-
|
177
|
+
UPPER(#{db_field}) > "time_keepers"."test"
|
178
178
|
SQL
|
179
179
|
|
180
180
|
expect(model.period_finishing_after(value).to_sql).to include(<<-SQL.squish)
|
181
|
-
|
181
|
+
UPPER(#{db_field}) > #{db_value}
|
182
182
|
SQL
|
183
183
|
end
|
184
184
|
|
185
185
|
it 'queries finishing before period' do
|
186
186
|
expect(model.period_finishing_before(:test).to_sql).to include(<<-SQL.squish)
|
187
|
-
|
187
|
+
UPPER(#{db_field}) < "time_keepers"."test"
|
188
188
|
SQL
|
189
189
|
|
190
190
|
expect(model.period_finishing_before(value).to_sql).to include(<<-SQL.squish)
|
191
|
-
|
191
|
+
UPPER(#{db_field}) < #{db_value}
|
192
192
|
SQL
|
193
193
|
end
|
194
194
|
|
@@ -234,7 +234,7 @@ RSpec.describe 'Period' do
|
|
234
234
|
SQL
|
235
235
|
|
236
236
|
expect(model.period_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
|
237
|
-
#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date)
|
237
|
+
#{date_db_field} && #{date_type.to_s.upcase}(#{db_value}::date, #{db_value}::date)
|
238
238
|
SQL
|
239
239
|
end
|
240
240
|
|
@@ -244,7 +244,7 @@ RSpec.describe 'Period' do
|
|
244
244
|
SQL
|
245
245
|
|
246
246
|
expect(model.period_not_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
|
247
|
-
NOT (#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date))
|
247
|
+
NOT (#{date_db_field} && #{date_type.to_s.upcase}(#{db_value}::date, #{db_value}::date))
|
248
248
|
SQL
|
249
249
|
end
|
250
250
|
|
@@ -305,25 +305,25 @@ RSpec.describe 'Period' do
|
|
305
305
|
context 'with field threshold' do
|
306
306
|
before { decorate(model, :period, threshold: :th) }
|
307
307
|
|
308
|
-
let(:lower_db_field) { "(
|
309
|
-
let(:upper_db_field) { "(
|
308
|
+
let(:lower_db_field) { "(LOWER(#{db_field}) - #{threshold_value})" }
|
309
|
+
let(:upper_db_field) { "(UPPER(#{db_field}) + #{threshold_value})" }
|
310
310
|
let(:threshold_value) { '"time_keepers"."th"' }
|
311
|
-
let(:threshold_db_field) { "#{type}(#{lower_db_field}, #{upper_db_field})" }
|
312
|
-
let(:nullif_condition) { "
|
311
|
+
let(:threshold_db_field) { "#{type.to_s.upcase}(#{lower_db_field}, #{upper_db_field})" }
|
312
|
+
let(:nullif_condition) { "NULLIF(#{threshold_db_field}, #{empty_condition})" }
|
313
313
|
let(:threshold_date_db_field) do
|
314
|
-
"
|
314
|
+
"DATERANGE(#{lower_db_field}::date, #{upper_db_field}::date, '[]')"
|
315
315
|
end
|
316
316
|
|
317
317
|
context 'on model' do
|
318
318
|
it 'queries current on period' do
|
319
319
|
expect(model.period_on(value).to_sql).to include(<<-SQL.squish)
|
320
|
-
|
320
|
+
COALESCE(#{nullif_condition} @> #{cast_db_value}, #{true_value})
|
321
321
|
SQL
|
322
322
|
end
|
323
323
|
|
324
324
|
it 'queries current period' do
|
325
325
|
expect(model.current_period.to_sql).to include(<<-SQL.squish)
|
326
|
-
|
326
|
+
COALESCE(#{nullif_condition} @>
|
327
327
|
SQL
|
328
328
|
|
329
329
|
expect(model.current_period.to_sql).to include(<<-SQL.squish)
|
@@ -333,7 +333,7 @@ RSpec.describe 'Period' do
|
|
333
333
|
|
334
334
|
it 'queries not current period' do
|
335
335
|
expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
|
336
|
-
NOT (
|
336
|
+
NOT (COALESCE(#{nullif_condition} @>
|
337
337
|
SQL
|
338
338
|
|
339
339
|
expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
|
@@ -357,7 +357,7 @@ RSpec.describe 'Period' do
|
|
357
357
|
SQL
|
358
358
|
|
359
359
|
expect(model.period_real_overlapping(value, value).to_sql).to include(<<-SQL.squish)
|
360
|
-
#{threshold_db_field} && #{type}(#{db_value}, #{db_value})
|
360
|
+
#{threshold_db_field} && #{type.to_s.upcase}(#{db_value}, #{db_value})
|
361
361
|
SQL
|
362
362
|
end
|
363
363
|
|
@@ -427,7 +427,7 @@ RSpec.describe 'Period' do
|
|
427
427
|
SQL
|
428
428
|
|
429
429
|
expect(model.period_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
|
430
|
-
#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date)
|
430
|
+
#{date_db_field} && #{date_type.to_s.upcase}(#{db_value}::date, #{db_value}::date)
|
431
431
|
SQL
|
432
432
|
end
|
433
433
|
|
@@ -437,7 +437,7 @@ RSpec.describe 'Period' do
|
|
437
437
|
SQL
|
438
438
|
|
439
439
|
expect(model.period_not_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
|
440
|
-
NOT (#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date))
|
440
|
+
NOT (#{date_db_field} && #{date_type.to_s.upcase}(#{db_value}::date, #{db_value}::date))
|
441
441
|
SQL
|
442
442
|
end
|
443
443
|
|
@@ -457,7 +457,7 @@ RSpec.describe 'Period' do
|
|
457
457
|
SQL
|
458
458
|
|
459
459
|
expect(model.period_real_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
|
460
|
-
#{threshold_date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date)
|
460
|
+
#{threshold_date_db_field} && #{date_type.to_s.upcase}(#{db_value}::date, #{db_value}::date)
|
461
461
|
SQL
|
462
462
|
end
|
463
463
|
end
|
@@ -512,22 +512,22 @@ RSpec.describe 'Period' do
|
|
512
512
|
context 'with value threshold' do
|
513
513
|
before { decorate(model, :period, threshold: 5.minutes) }
|
514
514
|
|
515
|
-
let(:lower_db_field) { "(
|
516
|
-
let(:upper_db_field) { "(
|
515
|
+
let(:lower_db_field) { "(LOWER(#{db_field}) - #{threshold_value})" }
|
516
|
+
let(:upper_db_field) { "(UPPER(#{db_field}) + #{threshold_value})" }
|
517
517
|
let(:threshold_value) { "'300 seconds'::interval" }
|
518
|
-
let(:threshold_db_field) { "#{type}(#{lower_db_field}, #{upper_db_field})" }
|
519
|
-
let(:nullif_condition) { "
|
518
|
+
let(:threshold_db_field) { "#{type.to_s.upcase}(#{lower_db_field}, #{upper_db_field})" }
|
519
|
+
let(:nullif_condition) { "NULLIF(#{threshold_db_field}, #{empty_condition})" }
|
520
520
|
|
521
521
|
context 'on model' do
|
522
522
|
it 'queries current on period' do
|
523
523
|
expect(model.period_on(value).to_sql).to include(<<-SQL.squish)
|
524
|
-
|
524
|
+
COALESCE(#{nullif_condition} @> #{cast_db_value}, #{true_value})
|
525
525
|
SQL
|
526
526
|
end
|
527
527
|
|
528
528
|
it 'queries current period' do
|
529
529
|
expect(model.current_period.to_sql).to include(<<-SQL.squish)
|
530
|
-
|
530
|
+
COALESCE(#{nullif_condition} @>
|
531
531
|
SQL
|
532
532
|
|
533
533
|
expect(model.current_period.to_sql).to include(<<-SQL.squish)
|
@@ -537,7 +537,7 @@ RSpec.describe 'Period' do
|
|
537
537
|
|
538
538
|
it 'queries not current period' do
|
539
539
|
expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
|
540
|
-
NOT (
|
540
|
+
NOT (COALESCE(#{nullif_condition} @>
|
541
541
|
SQL
|
542
542
|
|
543
543
|
expect(model.not_current_period.to_sql).to include(<<-SQL.squish)
|
@@ -561,7 +561,7 @@ RSpec.describe 'Period' do
|
|
561
561
|
SQL
|
562
562
|
|
563
563
|
expect(model.period_real_overlapping(value, value).to_sql).to include(<<-SQL.squish)
|
564
|
-
#{threshold_db_field} && #{type}(#{db_value}, #{db_value})
|
564
|
+
#{threshold_db_field} && #{type.to_s.upcase}(#{db_value}, #{db_value})
|
565
565
|
SQL
|
566
566
|
end
|
567
567
|
|
@@ -631,7 +631,7 @@ RSpec.describe 'Period' do
|
|
631
631
|
SQL
|
632
632
|
|
633
633
|
expect(model.period_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
|
634
|
-
#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date)
|
634
|
+
#{date_db_field} && #{date_type.to_s.upcase}(#{db_value}::date, #{db_value}::date)
|
635
635
|
SQL
|
636
636
|
end
|
637
637
|
|
@@ -641,7 +641,7 @@ RSpec.describe 'Period' do
|
|
641
641
|
SQL
|
642
642
|
|
643
643
|
expect(model.period_not_overlapping_date(value, value).to_sql).to include(<<-SQL.squish)
|
644
|
-
NOT (#{date_db_field} && #{date_type}(#{db_value}::date, #{db_value}::date))
|
644
|
+
NOT (#{date_db_field} && #{date_type.to_s.upcase}(#{db_value}::date, #{db_value}::date))
|
645
645
|
SQL
|
646
646
|
end
|
647
647
|
end
|
@@ -701,26 +701,26 @@ RSpec.describe 'Period' do
|
|
701
701
|
|
702
702
|
let(:cast_type) { '::date' }
|
703
703
|
let(:cast_db_value) { "#{db_value}#{cast_type}" }
|
704
|
-
let(:empty_condition) { "#{type}(NULL, NULL)" }
|
705
|
-
let(:nullif_condition) { "
|
704
|
+
let(:empty_condition) { "#{type.to_s.upcase}(NULL, NULL)" }
|
705
|
+
let(:nullif_condition) { "NULLIF(#{threshold_db_field}, #{empty_condition})" }
|
706
706
|
|
707
|
-
let(:lower_db_field) { "(
|
708
|
-
let(:upper_db_field) { "(
|
707
|
+
let(:lower_db_field) { "(LOWER(#{db_field}) - #{threshold_value})::date" }
|
708
|
+
let(:upper_db_field) { "(UPPER(#{db_field}) + #{threshold_value})::date" }
|
709
709
|
let(:threshold_value) { "'86400 seconds'::interval" }
|
710
|
-
let(:threshold_db_field) { "#{type}(#{lower_db_field}, #{upper_db_field})" }
|
710
|
+
let(:threshold_db_field) { "#{type.to_s.upcase}(#{lower_db_field}, #{upper_db_field})" }
|
711
711
|
|
712
712
|
before { decorate(model, :available, pessimistic: true, threshold: 1.day) }
|
713
713
|
|
714
714
|
context 'on model' do
|
715
715
|
it 'queries current on available' do
|
716
716
|
expect(model.available_on(value).to_sql).to include(<<-SQL.squish)
|
717
|
-
|
717
|
+
COALESCE(#{nullif_condition} @> #{cast_db_value}, #{false_value})
|
718
718
|
SQL
|
719
719
|
end
|
720
720
|
|
721
721
|
it 'queries current available' do
|
722
722
|
expect(model.current_available.to_sql).to include(<<-SQL.squish)
|
723
|
-
|
723
|
+
COALESCE(#{nullif_condition} @>
|
724
724
|
SQL
|
725
725
|
|
726
726
|
expect(model.current_available.to_sql).to include(<<-SQL.squish)
|
@@ -730,7 +730,7 @@ RSpec.describe 'Period' do
|
|
730
730
|
|
731
731
|
it 'queries not current available' do
|
732
732
|
expect(model.not_current_available.to_sql).to include(<<-SQL.squish)
|
733
|
-
NOT (
|
733
|
+
NOT (COALESCE(#{nullif_condition} @>
|
734
734
|
SQL
|
735
735
|
|
736
736
|
expect(model.not_current_available.to_sql).to include(<<-SQL.squish)
|
@@ -764,7 +764,7 @@ RSpec.describe 'Period' do
|
|
764
764
|
SQL
|
765
765
|
|
766
766
|
expect(model.available_overlapping(value, value).to_sql).to include(<<-SQL.squish)
|
767
|
-
#{db_field} && #{type}(#{db_value}, #{db_value})
|
767
|
+
#{db_field} && #{type.to_s.upcase}(#{db_value}, #{db_value})
|
768
768
|
SQL
|
769
769
|
end
|
770
770
|
|
@@ -774,47 +774,47 @@ RSpec.describe 'Period' do
|
|
774
774
|
SQL
|
775
775
|
|
776
776
|
expect(model.available_not_overlapping(value, value).to_sql).to include(<<-SQL.squish)
|
777
|
-
NOT (#{db_field} && #{type}(#{db_value}, #{db_value}))
|
777
|
+
NOT (#{db_field} && #{type.to_s.upcase}(#{db_value}, #{db_value}))
|
778
778
|
SQL
|
779
779
|
end
|
780
780
|
|
781
781
|
it 'queries starting after available' do
|
782
782
|
expect(model.available_starting_after(:test).to_sql).to include(<<-SQL.squish)
|
783
|
-
|
783
|
+
LOWER(#{db_field}) > "time_keepers"."test"
|
784
784
|
SQL
|
785
785
|
|
786
786
|
expect(model.available_starting_after(value).to_sql).to include(<<-SQL.squish)
|
787
|
-
|
787
|
+
LOWER(#{db_field}) > #{db_value}
|
788
788
|
SQL
|
789
789
|
end
|
790
790
|
|
791
791
|
it 'queries starting before available' do
|
792
792
|
expect(model.available_starting_before(:test).to_sql).to include(<<-SQL.squish)
|
793
|
-
|
793
|
+
LOWER(#{db_field}) < "time_keepers"."test"
|
794
794
|
SQL
|
795
795
|
|
796
796
|
expect(model.available_starting_before(value).to_sql).to include(<<-SQL.squish)
|
797
|
-
|
797
|
+
LOWER(#{db_field}) < #{db_value}
|
798
798
|
SQL
|
799
799
|
end
|
800
800
|
|
801
801
|
it 'queries finishing after available' do
|
802
802
|
expect(model.available_finishing_after(:test).to_sql).to include(<<-SQL.squish)
|
803
|
-
|
803
|
+
UPPER(#{db_field}) > "time_keepers"."test"
|
804
804
|
SQL
|
805
805
|
|
806
806
|
expect(model.available_finishing_after(value).to_sql).to include(<<-SQL.squish)
|
807
|
-
|
807
|
+
UPPER(#{db_field}) > #{db_value}
|
808
808
|
SQL
|
809
809
|
end
|
810
810
|
|
811
811
|
it 'queries finishing before available' do
|
812
812
|
expect(model.available_finishing_before(:test).to_sql).to include(<<-SQL.squish)
|
813
|
-
|
813
|
+
UPPER(#{db_field}) < "time_keepers"."test"
|
814
814
|
SQL
|
815
815
|
|
816
816
|
expect(model.available_finishing_before(value).to_sql).to include(<<-SQL.squish)
|
817
|
-
|
817
|
+
UPPER(#{db_field}) < #{db_value}
|
818
818
|
SQL
|
819
819
|
end
|
820
820
|
|
@@ -834,7 +834,7 @@ RSpec.describe 'Period' do
|
|
834
834
|
SQL
|
835
835
|
|
836
836
|
expect(model.available_real_overlapping(value, value).to_sql).to include(<<-SQL.squish)
|
837
|
-
#{threshold_db_field} && #{type}(#{db_value}, #{db_value})
|
837
|
+
#{threshold_db_field} && #{type.to_s.upcase}(#{db_value}, #{db_value})
|
838
838
|
SQL
|
839
839
|
end
|
840
840
|
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'PredicateBuilder' do
|
4
|
+
describe 'on enumerator lazy' do
|
5
|
+
let(:timed_out_error) do
|
6
|
+
Torque::PostgreSQL::PredicateBuilder::EnumeratorLazyHandler::Timeout
|
7
|
+
end
|
8
|
+
|
9
|
+
subject { Video.all }
|
10
|
+
|
11
|
+
after do
|
12
|
+
Torque::PostgreSQL.config.predicate_builder.lazy_timeout = 0.02
|
13
|
+
Torque::PostgreSQL.config.predicate_builder.lazy_limit = 2_000
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'works with provided value' do
|
17
|
+
sql = subject.where(id: [1,2,3].lazy).to_sql
|
18
|
+
expect(sql).to include("WHERE \"videos\".\"id\" IN (1, 2, 3)")
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'handles gracefully a timeout' do
|
22
|
+
Torque::PostgreSQL.config.predicate_builder.lazy_timeout = 0.01
|
23
|
+
Torque::PostgreSQL.config.predicate_builder.lazy_limit = nil
|
24
|
+
expect { subject.where(id: (1..).lazy).to_sql }.to raise_error(timed_out_error)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'handles properly a limit' do
|
28
|
+
Torque::PostgreSQL.config.predicate_builder.lazy_timeout = nil
|
29
|
+
Torque::PostgreSQL.config.predicate_builder.lazy_limit = 2
|
30
|
+
|
31
|
+
sql = subject.where(id: [1,2,3].lazy).to_sql
|
32
|
+
expect(sql).to include("WHERE \"videos\".\"id\" IN (1, 2)")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'on arel attribute' do
|
37
|
+
subject { Item.all }
|
38
|
+
|
39
|
+
it 'works with both plain attributes' do
|
40
|
+
sql = subject.where(id: Item.arel_table[:id]).to_sql
|
41
|
+
expect(sql).to include("WHERE \"items\".\"id\" = \"items\".\"id\"")
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'works when when the left side is an array' do
|
45
|
+
sql = subject.where(tag_ids: Item.arel_table[:id]).to_sql
|
46
|
+
expect(sql).to include("WHERE \"items\".\"id\" = ANY(\"items\".\"tag_ids\")")
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'works when the right side is an array' do
|
50
|
+
sql = subject.where(id: Item.arel_table[:tag_ids]).to_sql
|
51
|
+
expect(sql).to include("WHERE \"items\".\"id\" = ANY(\"items\".\"tag_ids\")")
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'works when both are arrays' do
|
55
|
+
sql = subject.where(tag_ids: Item.arel_table[:tag_ids]).to_sql
|
56
|
+
expect(sql).to include("WHERE \"items\".\"tag_ids\" && \"items\".\"tag_ids\"")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'on array' do
|
61
|
+
subject { Item.all }
|
62
|
+
|
63
|
+
before { Torque::PostgreSQL.config.predicate_builder.handle_array_attributes = true }
|
64
|
+
after { Torque::PostgreSQL.config.predicate_builder.handle_array_attributes = false }
|
65
|
+
|
66
|
+
it 'works with plain array when disabled' do
|
67
|
+
Torque::PostgreSQL.config.predicate_builder.handle_array_attributes = false
|
68
|
+
|
69
|
+
sql = subject.where(tag_ids: 1).to_sql
|
70
|
+
expect(sql).to include("WHERE \"items\".\"tag_ids\" = 1")
|
71
|
+
|
72
|
+
sql = subject.where(tag_ids: [1, 2, 3]).to_sql
|
73
|
+
expect(sql).to include("WHERE \"items\".\"tag_ids\" = '{1,2,3}'")
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'works with a single value' do
|
77
|
+
sql = subject.where(tag_ids: 1).to_sql
|
78
|
+
expect(sql).to include("WHERE 1 = ANY(\"items\".\"tag_ids\")")
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'works with an array value' do
|
82
|
+
sql = subject.where(tag_ids: [1, 2, 3]).to_sql
|
83
|
+
expect(sql).to include("WHERE \"items\".\"tag_ids\" && '{1,2,3}'")
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'works with an empty array' do
|
87
|
+
sql = subject.where(tag_ids: []).to_sql
|
88
|
+
expect(sql).to include("WHERE CARDINALITY(\"items\".\"tag_ids\") = 0")
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'properly binds the provided values' do
|
92
|
+
sql, binds = get_query_with_binds { subject.where(tag_ids: 1).load }
|
93
|
+
expect(sql).to include("WHERE $1 = ANY(\"items\".\"tag_ids\")")
|
94
|
+
expect(binds.first.value).to eq(1)
|
95
|
+
|
96
|
+
sql, binds = get_query_with_binds { subject.where(tag_ids: [1, 2, 3]).load }
|
97
|
+
expect(sql).to include("WHERE \"items\".\"tag_ids\" && $1")
|
98
|
+
expect(binds.first.value).to eq([1, 2, 3])
|
99
|
+
|
100
|
+
sql, binds = get_query_with_binds { subject.where(tag_ids: []).load }
|
101
|
+
expect(sql).to include("WHERE CARDINALITY(\"items\".\"tag_ids\") = 0")
|
102
|
+
expect(binds).to be_empty
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe 'on regexp' do
|
107
|
+
subject { Video.all }
|
108
|
+
|
109
|
+
it 'works with a basic regular expression' do
|
110
|
+
sql = subject.where(title: /(a|b)/).to_sql
|
111
|
+
expect(sql).to include("WHERE \"videos\".\"title\" ~ '(a|b)'")
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'works with a case-insensitive regular expression' do
|
115
|
+
sql = subject.where(title: /(a|b)/i).to_sql
|
116
|
+
expect(sql).to include("WHERE \"videos\".\"title\" ~* '(a|b)'")
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'works with characters that need escape' do
|
120
|
+
sql = subject.where(title: %r{a|'|"|\\}).to_sql
|
121
|
+
expect(sql).to include("WHERE \"videos\".\"title\" ~ 'a|''|\"|\\\\'")
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'properly binds the provided value' do
|
125
|
+
query = subject.where(title: /(a|b)/)
|
126
|
+
|
127
|
+
sql, binds = get_query_with_binds { query.load }
|
128
|
+
expect(sql).to include("WHERE \"videos\".\"title\" ~ $1")
|
129
|
+
expect(binds.first.value).to eq('(a|b)')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/spec/tests/schema_spec.rb
CHANGED
@@ -2,13 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe 'Schema' do
|
4
4
|
let(:connection) { ActiveRecord::Base.connection }
|
5
|
-
let(:source)
|
6
|
-
if Torque::PostgreSQL::AR720
|
7
|
-
ActiveRecord::Base.connection_pool
|
8
|
-
else
|
9
|
-
ActiveRecord::Base.connection
|
10
|
-
end
|
11
|
-
end
|
5
|
+
let(:source) { ActiveRecord::Base.connection_pool }
|
12
6
|
|
13
7
|
before do
|
14
8
|
connection.instance_variable_set(:@schemas_blacklist, nil)
|
@@ -16,7 +10,7 @@ RSpec.describe 'Schema' do
|
|
16
10
|
end
|
17
11
|
|
18
12
|
context 'on migration' do
|
19
|
-
it 'can check for
|
13
|
+
it 'can check for existence' do
|
20
14
|
expect(connection.schema_exists?(:information_schema)).to be_falsey
|
21
15
|
expect(connection.schema_exists?(:information_schema, filtered: false)).to be_truthy
|
22
16
|
end
|
@@ -73,14 +73,7 @@ RSpec.describe 'TableInheritance' do
|
|
73
73
|
end
|
74
74
|
|
75
75
|
context 'on schema' do
|
76
|
-
let(:source)
|
77
|
-
if Torque::PostgreSQL::AR720
|
78
|
-
ActiveRecord::Base.connection_pool
|
79
|
-
else
|
80
|
-
ActiveRecord::Base.connection
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
76
|
+
let(:source) { ActiveRecord::Base.connection_pool }
|
84
77
|
let(:dump_result) do
|
85
78
|
ActiveRecord::SchemaDumper.dump(source, (dump_result = StringIO.new))
|
86
79
|
dump_result.string
|
@@ -89,24 +82,24 @@ RSpec.describe 'TableInheritance' do
|
|
89
82
|
it 'dumps single inheritance with body' do
|
90
83
|
parts = '"activity_books"'
|
91
84
|
parts << ', id: false'
|
92
|
-
parts << ', force: :cascade'
|
93
85
|
parts << ', inherits: "activities"'
|
86
|
+
parts << ', force: :cascade'
|
94
87
|
expect(dump_result).to match(/create_table #{parts} do /)
|
95
88
|
end
|
96
89
|
|
97
90
|
it 'dumps single inheritance without body' do
|
98
91
|
parts = '"activity_post_samples"'
|
99
92
|
parts << ', id: false'
|
100
|
-
parts << ', force: :cascade'
|
101
93
|
parts << ', inherits: "activity_posts"'
|
94
|
+
parts << ', force: :cascade'
|
102
95
|
expect(dump_result).to match(/create_table #{parts}(?! do \|t\|)/)
|
103
96
|
end
|
104
97
|
|
105
98
|
it 'dumps multiple inheritance' do
|
106
99
|
parts = '"activity_posts"'
|
107
100
|
parts << ', id: false'
|
108
|
-
parts << ', force: :cascade'
|
109
101
|
parts << ', inherits: (\["images", "activities"\]|\["activities", "images"\])'
|
102
|
+
parts << ', force: :cascade'
|
110
103
|
expect(dump_result).to match(/create_table #{parts}/)
|
111
104
|
end
|
112
105
|
end
|
@@ -115,12 +108,9 @@ RSpec.describe 'TableInheritance' do
|
|
115
108
|
let(:schema_cache) { ActiveRecord::Base.connection.schema_cache }
|
116
109
|
let(:schema_cache_reflection) { schema_cache.instance_variable_get(:@schema_reflection) }
|
117
110
|
let(:new_schema_cache) { schema_cache_reflection.send(:cache, schema_cache_source) }
|
111
|
+
let(:schema_cache_source) { schema_cache.instance_variable_get(:@pool) }
|
118
112
|
|
119
|
-
|
120
|
-
schema_cache.instance_variable_get(Torque::PostgreSQL::AR720 ? :@pool : :@connection)
|
121
|
-
end
|
122
|
-
|
123
|
-
subject { Torque::PostgreSQL::AR710 ? new_schema_cache : schema_cache }
|
113
|
+
subject { new_schema_cache }
|
124
114
|
|
125
115
|
it 'correctly defines the associations' do
|
126
116
|
scenario = {
|
@@ -148,8 +138,8 @@ RSpec.describe 'TableInheritance' do
|
|
148
138
|
end
|
149
139
|
|
150
140
|
context 'on looking up models' do
|
151
|
-
let(:prepare_arguments) {
|
152
|
-
let(:prepare_method) {
|
141
|
+
let(:prepare_arguments) { [schema_cache_source] }
|
142
|
+
let(:prepare_method) { :add_all }
|
153
143
|
|
154
144
|
after(:all) do
|
155
145
|
schema_cache = ActiveRecord::Base.connection.schema_cache
|
@@ -375,28 +365,37 @@ RSpec.describe 'TableInheritance' do
|
|
375
365
|
ActivityPost::Sample.create(title: 'Activity post')
|
376
366
|
records = base.cast_records.order(:id).load.to_a
|
377
367
|
|
378
|
-
expect(records[0]
|
379
|
-
expect(records[1]
|
380
|
-
expect(records[2]
|
381
|
-
expect(records[3]
|
368
|
+
expect(records[0]).to be_instance_of(Activity)
|
369
|
+
expect(records[1]).to be_instance_of(ActivityBook)
|
370
|
+
expect(records[2]).to be_instance_of(ActivityPost)
|
371
|
+
expect(records[3]).to be_instance_of(ActivityPost::Sample)
|
382
372
|
end
|
383
373
|
|
384
374
|
it 'does not cast unnecessary records' do
|
385
375
|
ActivityPost.create(title: 'Activity post')
|
386
376
|
records = base.cast_records(ActivityBook).order(:id).load.to_a
|
387
377
|
|
388
|
-
expect(records[0]
|
389
|
-
expect(records[1]
|
390
|
-
expect(records[2]
|
378
|
+
expect(records[0]).to be_instance_of(Activity)
|
379
|
+
expect(records[1]).to be_instance_of(ActivityBook)
|
380
|
+
expect(records[2]).to be_instance_of(Activity)
|
391
381
|
end
|
392
382
|
|
393
|
-
it 'correctly
|
383
|
+
it 'correctly identifies same name attributes' do
|
394
384
|
ActivityPost.create(title: 'Activity post', url: 'posturl1')
|
395
385
|
records = base.cast_records.order(:id).load.to_a
|
396
386
|
|
397
387
|
expect(records[1].url).to eql('bookurl1')
|
398
388
|
expect(records[2].url).to eql('posturl1')
|
399
389
|
end
|
390
|
+
|
391
|
+
# TODO: Maybe in the future
|
392
|
+
xit 'does not make internal inheritance attributes accessible' do
|
393
|
+
record = base.cast_records.order(:id).load.last
|
394
|
+
|
395
|
+
expect(record).to be_instance_of(ActivityBook)
|
396
|
+
expect(record).not_to respond_to(:_record_class)
|
397
|
+
expect(record).not_to respond_to(:_auto_cast)
|
398
|
+
end
|
400
399
|
end
|
401
400
|
|
402
401
|
context 'cast record' do
|