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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/torque/postgresql/adapter/database_statements.rb +63 -84
  3. data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
  4. data/lib/torque/postgresql/adapter/oid/line.rb +2 -6
  5. data/lib/torque/postgresql/adapter/oid/range.rb +4 -4
  6. data/lib/torque/postgresql/adapter/oid.rb +1 -23
  7. data/lib/torque/postgresql/adapter/quoting.rb +13 -7
  8. data/lib/torque/postgresql/adapter/schema_creation.rb +7 -28
  9. data/lib/torque/postgresql/adapter/schema_definitions.rb +36 -0
  10. data/lib/torque/postgresql/adapter/schema_dumper.rb +90 -34
  11. data/lib/torque/postgresql/adapter/schema_overrides.rb +45 -0
  12. data/lib/torque/postgresql/adapter/schema_statements.rb +64 -49
  13. data/lib/torque/postgresql/arel/infix_operation.rb +15 -28
  14. data/lib/torque/postgresql/arel/nodes.rb +2 -2
  15. data/lib/torque/postgresql/arel/operations.rb +7 -1
  16. data/lib/torque/postgresql/arel/visitors.rb +3 -9
  17. data/lib/torque/postgresql/associations/association_scope.rb +23 -31
  18. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +25 -0
  19. data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +16 -0
  20. data/lib/torque/postgresql/attributes/builder/enum.rb +12 -9
  21. data/lib/torque/postgresql/attributes/builder/full_text_search.rb +121 -0
  22. data/lib/torque/postgresql/attributes/builder/period.rb +21 -21
  23. data/lib/torque/postgresql/attributes/builder.rb +49 -11
  24. data/lib/torque/postgresql/attributes/enum.rb +7 -7
  25. data/lib/torque/postgresql/attributes/enum_set.rb +7 -7
  26. data/lib/torque/postgresql/attributes/full_text_search.rb +19 -0
  27. data/lib/torque/postgresql/attributes/period.rb +2 -2
  28. data/lib/torque/postgresql/attributes.rb +0 -4
  29. data/lib/torque/postgresql/auxiliary_statement/recursive.rb +3 -3
  30. data/lib/torque/postgresql/base.rb +3 -10
  31. data/lib/torque/postgresql/collector.rb +1 -1
  32. data/lib/torque/postgresql/config.rb +95 -5
  33. data/lib/torque/postgresql/function.rb +61 -0
  34. data/lib/torque/postgresql/inheritance.rb +52 -36
  35. data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +33 -0
  36. data/lib/torque/postgresql/predicate_builder/array_handler.rb +47 -0
  37. data/lib/torque/postgresql/predicate_builder/enumerator_lazy_handler.rb +37 -0
  38. data/lib/torque/postgresql/predicate_builder/regexp_handler.rb +21 -0
  39. data/lib/torque/postgresql/predicate_builder.rb +35 -0
  40. data/lib/torque/postgresql/railtie.rb +112 -30
  41. data/lib/torque/postgresql/reflection/abstract_reflection.rb +12 -44
  42. data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -0
  43. data/lib/torque/postgresql/reflection/has_many_reflection.rb +4 -0
  44. data/lib/torque/postgresql/reflection/runtime_reflection.rb +1 -1
  45. data/lib/torque/postgresql/relation/inheritance.rb +4 -7
  46. data/lib/torque/postgresql/relation.rb +6 -10
  47. data/lib/torque/postgresql/schema_cache.rb +6 -12
  48. data/lib/torque/postgresql/version.rb +1 -1
  49. data/lib/torque/postgresql.rb +2 -1
  50. data/spec/initialize.rb +58 -0
  51. data/spec/mocks/cache_query.rb +21 -21
  52. data/spec/mocks/create_table.rb +6 -26
  53. data/spec/schema.rb +19 -12
  54. data/spec/spec_helper.rb +5 -1
  55. data/spec/tests/arel_spec.rb +32 -7
  56. data/spec/tests/auxiliary_statement_spec.rb +3 -3
  57. data/spec/tests/belongs_to_many_spec.rb +72 -5
  58. data/spec/tests/enum_set_spec.rb +12 -11
  59. data/spec/tests/enum_spec.rb +4 -2
  60. data/spec/tests/full_text_seach_test.rb +252 -0
  61. data/spec/tests/function_spec.rb +42 -0
  62. data/spec/tests/has_many_spec.rb +21 -8
  63. data/spec/tests/interval_spec.rb +1 -7
  64. data/spec/tests/period_spec.rb +61 -61
  65. data/spec/tests/predicate_builder_spec.rb +132 -0
  66. data/spec/tests/schema_spec.rb +2 -8
  67. data/spec/tests/table_inheritance_spec.rb +25 -26
  68. metadata +34 -39
@@ -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) { "nullif(#{db_field}, #{empty_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) { "lower(#{db_field})::date" }
83
- let(:upper_date) { "upper(#{db_field})::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
- coalesce(#{nullif_condition} @> #{cast_db_value}, #{true_value})
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
- coalesce(#{nullif_condition} @>
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 (coalesce(#{nullif_condition} @>
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
- lower(#{db_field}) > "time_keepers"."test"
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
- lower(#{db_field}) > #{db_value}
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
- lower(#{db_field}) < "time_keepers"."test"
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
- lower(#{db_field}) < #{db_value}
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
- upper(#{db_field}) > "time_keepers"."test"
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
- upper(#{db_field}) > #{db_value}
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
- upper(#{db_field}) < "time_keepers"."test"
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
- upper(#{db_field}) < #{db_value}
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) { "(lower(#{db_field}) - #{threshold_value})" }
309
- let(:upper_db_field) { "(upper(#{db_field}) + #{threshold_value})" }
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) { "nullif(#{threshold_db_field}, #{empty_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
- "daterange(#{lower_db_field}::date, #{upper_db_field}::date, '[]')"
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
- coalesce(#{nullif_condition} @> #{cast_db_value}, #{true_value})
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
- coalesce(#{nullif_condition} @>
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 (coalesce(#{nullif_condition} @>
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) { "(lower(#{db_field}) - #{threshold_value})" }
516
- let(:upper_db_field) { "(upper(#{db_field}) + #{threshold_value})" }
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) { "nullif(#{threshold_db_field}, #{empty_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
- coalesce(#{nullif_condition} @> #{cast_db_value}, #{true_value})
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
- coalesce(#{nullif_condition} @>
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 (coalesce(#{nullif_condition} @>
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) { "nullif(#{threshold_db_field}, #{empty_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) { "(lower(#{db_field}) - #{threshold_value})::date" }
708
- let(:upper_db_field) { "(upper(#{db_field}) + #{threshold_value})::date" }
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
- coalesce(#{nullif_condition} @> #{cast_db_value}, #{false_value})
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
- coalesce(#{nullif_condition} @>
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 (coalesce(#{nullif_condition} @>
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
- lower(#{db_field}) > "time_keepers"."test"
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
- lower(#{db_field}) > #{db_value}
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
- lower(#{db_field}) < "time_keepers"."test"
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
- lower(#{db_field}) < #{db_value}
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
- upper(#{db_field}) > "time_keepers"."test"
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
- upper(#{db_field}) > #{db_value}
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
- upper(#{db_field}) < "time_keepers"."test"
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
- upper(#{db_field}) < #{db_value}
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
@@ -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) do
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 existance' do
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) do
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
- let(:schema_cache_source) do
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) { Torque::PostgreSQL::AR710 ? [schema_cache_source] : nil }
152
- let(:prepare_method) { Torque::PostgreSQL::AR720 ? :add_all : :prepare_data_sources }
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].class).to eql(Activity)
379
- expect(records[1].class).to eql(ActivityBook)
380
- expect(records[2].class).to eql(ActivityPost)
381
- expect(records[3].class).to eql(ActivityPost::Sample)
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].class).to eql(Activity)
389
- expect(records[1].class).to eql(ActivityBook)
390
- expect(records[2].class).to eql(Activity)
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 identify same name attributes' do
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