torque-postgresql 2.1.3 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09e539b25b952fb4d48bd092a90bae032ad8c26f1584cc5a704cdf96985b1e7e'
4
- data.tar.gz: d79aa047ac44dc47a25979f71a0b46dfcbdc573f28d18bfcccbcb59b0a693c7b
3
+ metadata.gz: cb3d81538edfb988d8566295fac7d055208e399d552b619d9d8b3b9234254686
4
+ data.tar.gz: 7c7c62bec966085eccbdc351041a94d04788e40f84b1120d88c1673c9d0e3b3c
5
5
  SHA512:
6
- metadata.gz: e2f75f66a5e0bed5f14006ff4ffd8e32f96bbe17f18ff4603c0706301c87114a1880b1a8f7bd530501d95c8f6dc10c563af6da5a2ba5f4414a31115c5510ca0f
7
- data.tar.gz: 930b6381e8e920a274c411c2ba8abfe96a5b8462227c51dbf5ee9f2a41ac88b6f3a3946d4e49cab0017bb271c176d58c37690e5e646b8536476b83a1cba5c62b
6
+ metadata.gz: 93cf49b971a0013d3f10263917437b7a552632e62f462d43b91aaad86ec66ba10b20a014bf2bbcaa883f1c6801df423fd3d98dd8d9546bd849920bb98b743e6c
7
+ data.tar.gz: 88cf63c02e13e80e48b5dfbe66e9157a52ecc8cc11410f0688e97a6c28e7f25a03adc9c7b71a45ffe9384a81debbf1a29d55363145a4ae6568c82cc9f7207a30
@@ -42,6 +42,8 @@ module Torque
42
42
  m.register_type 'interval', OID::Interval.new
43
43
  m.register_type 'line', OID::Line.new
44
44
  m.register_type 'segment', OID::Segment.new
45
+
46
+ m.alias_type 'regclass', 'varchar'
45
47
  end
46
48
 
47
49
  # :nodoc:
@@ -20,7 +20,7 @@ module Torque
20
20
  end
21
21
 
22
22
  def quote_default_expression(value, column)
23
- if value.class <= Array
23
+ if column.dig(:options, :array) && value.class <= Array
24
24
  quote(value) + '::' + column.sql_type
25
25
  else
26
26
  super
@@ -15,7 +15,14 @@ module Torque
15
15
  include DatabaseStatements
16
16
  include SchemaStatements
17
17
 
18
- INJECT_WHERE_REGEX = /(DO UPDATE SET.*excluded\.[^ ]+) RETURNING/.freeze
18
+ # :nodoc:
19
+ class DeduplicatableArray < ::Array
20
+ def deduplicate
21
+ map { |value| -value }
22
+ end
23
+
24
+ alias :-@ :deduplicate
25
+ end
19
26
 
20
27
  # Get the current PostgreSQL version as a Gem Version.
21
28
  def version
@@ -29,19 +36,28 @@ module Torque
29
36
  super.merge(options.extract!(:inherits))
30
37
  end
31
38
 
32
- # Allow filtered bulk insert by adding the where clause. This method is only used by
33
- # +InsertAll+, so it somewhat safe to override it
39
+ # Allow filtered bulk insert by adding the where clause. This method is
40
+ # only used by +InsertAll+, so it somewhat safe to override it
34
41
  def build_insert_sql(insert)
35
42
  super.tap do |sql|
36
43
  if insert.update_duplicates? && insert.where_condition?
37
44
  if insert.returning
38
- sql.gsub!(INJECT_WHERE_REGEX, "\\1 WHERE #{insert.where} RETURNING")
45
+ sql.sub!(' RETURNING ', " WHERE #{insert.where} RETURNING ")
39
46
  else
40
47
  sql << " WHERE #{insert.where}"
41
48
  end
42
49
  end
43
50
  end
44
51
  end
52
+
53
+ # Extend the extract default value to support array
54
+ def extract_value_from_default(default)
55
+ return super unless Torque::PostgreSQL.config.use_extended_defaults
56
+ return super unless default&.match(/ARRAY\[(.*?)\](?:::"?([\w. ]+)"?(?:\[\])+)?$/)
57
+
58
+ arr = $1.split(/(?!\B\[[^\]]*), ?(?![^\[]*\]\B)/)
59
+ DeduplicatableArray.new(arr.map(&method(:extract_value_from_default)))
60
+ end
45
61
  end
46
62
 
47
63
  ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend Adapter
@@ -41,8 +41,8 @@ module Torque
41
41
  end
42
42
  end
43
43
 
44
- if PostgreSQL::AR610
45
- # This is how Rails 6.1 now load the records
44
+ if PostgreSQL::AR604
45
+ # This is how Rails 6.0.4 and 6.1 now load the records
46
46
  def load_records
47
47
  return super unless connected_through_array?
48
48
 
@@ -452,12 +452,8 @@ module Torque
452
452
  def instance_current_on?
453
453
  attr_value = threshold.present? ? method_names[:real] : attribute
454
454
  default_value = default.inspect
455
- [
456
- "return #{default_value} if #{attr_value}.nil?",
457
- "return #{default_value} if #{attr_value}.min.try(:infinite?)",
458
- "return #{default_value} if #{attr_value}.max.try(:infinite?)",
459
- "#{attr_value}.min < value && #{attr_value}.max > value",
460
- ].join("\n")
455
+
456
+ "#{attr_value}.nil? ? #{default_value} : #{attr_value}.include?(value)"
461
457
  end
462
458
 
463
459
  def instance_start
@@ -5,6 +5,7 @@ module Torque
5
5
  include ActiveSupport::Configurable
6
6
 
7
7
  # Stores a version check for compatibility purposes
8
+ AR604 = (ActiveRecord.gem_version >= Gem::Version.new('6.0.4'))
8
9
  AR610 = (ActiveRecord.gem_version >= Gem::Version.new('6.1.0'))
9
10
 
10
11
  # Use the same logger as the Active Record one
@@ -25,6 +26,11 @@ module Torque
25
26
  # same configuration is set to true
26
27
  config.eager_load = false
27
28
 
29
+ # This allows default values to have extended values like arrays and casted
30
+ # values. Extended defaults are still experimental, so enable and test it
31
+ # before using it in prod
32
+ config.use_extended_defaults = false
33
+
28
34
  # Set a list of irregular model name when associated with table names
29
35
  config.irregular_models = {}
30
36
  def config.irregular_models=(hash)
@@ -146,7 +146,7 @@ module Torque
146
146
  auto_cast = _auto_cast_attribute.to_s
147
147
  record_class = _record_class_attribute.to_s
148
148
  return super unless attributes.key?(record_class) &&
149
- attributes.delete(auto_cast) && attributes[record_class] != table_name
149
+ attributes.delete(auto_cast) && attributes[record_class] != table_name
150
150
 
151
151
  klass = casted_dependents[attributes[record_class]]
152
152
  raise_unable_to_cast(attributes[record_class]) if klass.nil?
@@ -56,8 +56,10 @@ module Torque
56
56
  end
57
57
 
58
58
  ::ActiveRecord::Reflection.const_set(:BelongsToManyReflection, BelongsToManyReflection)
59
- ::ActiveRecord::Reflection::AssociationReflection::VALID_AUTOMATIC_INVERSE_MACROS
60
- .push(:belongs_to_many)
59
+
60
+ reflection_class = ::ActiveRecord::Reflection::AssociationReflection
61
+ reflection_class::VALID_AUTOMATIC_INVERSE_MACROS.push(:belongs_to_many) \
62
+ if reflection_class.const_defined?('VALID_AUTOMATIC_INVERSE_MACROS')
61
63
  end
62
64
  end
63
65
  end
@@ -5,6 +5,8 @@ module Torque
5
5
  module Relation
6
6
  module Inheritance
7
7
 
8
+ # REGCLASS = ::Arel.sql('tableoid').cast('regclass')
9
+
8
10
  # :nodoc:
9
11
  def cast_records_value; get_value(:cast_records); end
10
12
  # :nodoc:
@@ -44,14 +46,8 @@ module Torque
44
46
 
45
47
  # Like #cast_records, but modifies relation in place
46
48
  def cast_records!(*types, **options)
47
- record_class = self.class._record_class_attribute
48
-
49
- with!(record_class)
50
- if options[:filter]
51
- table = record_class.to_s.camelize.underscore
52
- where!(table => { record_class => types.map(&:table_name) })
53
- end
54
-
49
+ where!(regclass.cast(:varchar).in(types.map(&:table_name))) if options[:filter]
50
+ self.select_extra_values += [regclass.as(_record_class_attribute.to_s)]
55
51
  self.cast_records_value = (types.present? ? types : model.casted_dependents.values)
56
52
  self
57
53
  end
@@ -109,13 +105,12 @@ module Torque
109
105
  end
110
106
 
111
107
  def build_auto_caster_marker(arel, types)
112
- types = types.map(&:table_name)
113
- type_attribute = self.class._record_class_attribute.to_s
114
- auto_cast_attribute = self.class._auto_cast_attribute.to_s
108
+ attribute = regclass.cast(:varchar).in(types.map(&:table_name))
109
+ attribute.as(self.class._auto_cast_attribute.to_s)
110
+ end
115
111
 
116
- table = ::Arel::Table.new(type_attribute.camelize.underscore)
117
- column = table[type_attribute].in(types)
118
- ::Arel.sql(column.to_sql).as(auto_cast_attribute)
112
+ def regclass
113
+ arel_table['tableoid'].cast(:regclass)
119
114
  end
120
115
 
121
116
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Torque
4
4
  module PostgreSQL
5
- VERSION = '2.1.3'
5
+ VERSION = '2.2.0'
6
6
  end
7
7
  end
data/lib/torque/range.rb CHANGED
@@ -17,6 +17,4 @@ module Torque
17
17
  end
18
18
  alias_method :|, :union
19
19
  end
20
-
21
- ::Range.include(Range)
22
20
  end
@@ -48,6 +48,36 @@ RSpec.describe 'Arel' do
48
48
  end
49
49
  end
50
50
 
51
+ context 'on default value' do
52
+ let(:connection) { ActiveRecord::Base.connection }
53
+
54
+ before(:context) { Torque::PostgreSQL.config.use_extended_defaults = true }
55
+ after(:context) { Torque::PostgreSQL.config.use_extended_defaults = false }
56
+ after { Author.reset_column_information }
57
+
58
+ it 'does not break jsonb' do
59
+ expect { connection.add_column(:authors, :profile, :jsonb, default: []) }.not_to raise_error
60
+ expect(Author.columns_hash['profile'].default).to eq('[]')
61
+ end
62
+
63
+ it 'works properly when column is an array' do
64
+ expect { connection.add_column(:authors, :tag_ids, :bigint, array: true, default: []) }.not_to raise_error
65
+ expect(Author.columns_hash['tag_ids'].default).to eq([])
66
+ end
67
+
68
+ it 'works with an array with enum values' do
69
+ value = ['visitor', 'assistant']
70
+ expect { connection.add_column(:authors, :roles, :roles, array: true, default: value) }.not_to raise_error
71
+ expect(Author.columns_hash['roles'].default).to eq(value)
72
+ end
73
+
74
+ it 'works with multi dimentional array' do
75
+ value = [['1', '2'], ['3', '4']]
76
+ expect { connection.add_column(:authors, :tag_ids, :string, array: true, default: value) }.not_to raise_error
77
+ expect(Author.columns_hash['tag_ids'].default).to eq(value)
78
+ end
79
+ end
80
+
51
81
  context 'on cast' do
52
82
  it 'provides an array method' do
53
83
  sample1 = ::Arel.array(1, 2, 3, 4)
@@ -266,6 +266,15 @@ RSpec.describe 'Period' do
266
266
 
267
267
  instance.period = 4.hour.from_now.utc..6.hour.from_now.utc
268
268
  expect(instance).not_to be_current_period
269
+
270
+ instance.period = [nil, 4.hours.ago.utc]
271
+ expect(instance).not_to be_current_period
272
+
273
+ instance.period = [4.hours.from_now.utc, nil]
274
+ expect(instance).not_to be_current_period
275
+
276
+ instance.period = [nil, nil]
277
+ expect(instance).to be_current_period
269
278
  end
270
279
 
271
280
  it 'checks fro current based on a value' do
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- RSpec.describe 'Range' do
3
+ RSpec.xdescribe 'Range' do
4
4
  let(:sample) { (5..15) }
5
5
 
6
6
  it 'has intersection' do
@@ -292,14 +292,12 @@ RSpec.describe 'TableInheritance' do
292
292
  end
293
293
 
294
294
  it 'adds all statements to load all the necessary records' do
295
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
296
- result << ' SELECT "activities".*, "record_class"."_record_class", "i_0"."description"'
295
+ result = 'SELECT "activities".*, "activities"."tableoid"::regclass AS _record_class, "i_0"."description"'
297
296
  result << ', COALESCE("i_0"."url", "i_1"."url", "i_2"."url") AS url, "i_0"."activated" AS activity_books__activated'
298
297
  result << ', "i_1"."activated" AS activity_posts__activated, "i_2"."activated" AS activity_post_samples__activated'
299
298
  result << ', COALESCE("i_1"."file", "i_2"."file") AS file, COALESCE("i_1"."post_id", "i_2"."post_id") AS post_id'
300
- result << ", \"record_class\".\"_record_class\" IN ('activity_books', 'activity_posts', 'activity_post_samples') AS _auto_cast"
299
+ result << ", \"activities\".\"tableoid\"::regclass::varchar IN ('activity_books', 'activity_posts', 'activity_post_samples') AS _auto_cast"
301
300
  result << ' FROM "activities"'
302
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
303
301
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
304
302
  result << ' LEFT OUTER JOIN "activity_posts" "i_1" ON "activities"."id" = "i_1"."id"'
305
303
  result << ' LEFT OUTER JOIN "activity_post_samples" "i_2" ON "activities"."id" = "i_2"."id"'
@@ -307,33 +305,27 @@ RSpec.describe 'TableInheritance' do
307
305
  end
308
306
 
309
307
  it 'can be have simplefied joins' do
310
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
311
- result << ' SELECT "activities".*, "record_class"."_record_class"'
308
+ result = 'SELECT "activities".*, "activities"."tableoid"::regclass AS _record_class'
312
309
  result << ', "i_0"."description", "i_0"."url", "i_0"."activated"'
313
- result << ", \"record_class\".\"_record_class\" IN ('activity_books') AS _auto_cast"
310
+ result << ", \"activities\".\"tableoid\"::regclass::varchar IN ('activity_books') AS _auto_cast"
314
311
  result << ' FROM "activities"'
315
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
316
312
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
317
313
  expect(base.cast_records(child).all.to_sql).to eql(result)
318
314
  end
319
315
 
320
316
  it 'can be filtered by record type' do
321
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
322
- result << ' SELECT "activities".*, "record_class"."_record_class"'
317
+ result = 'SELECT "activities".*, "activities"."tableoid"::regclass AS _record_class'
323
318
  result << ', "i_0"."description", "i_0"."url", "i_0"."activated"'
324
- result << ", \"record_class\".\"_record_class\" IN ('activity_books') AS _auto_cast"
319
+ result << ", \"activities\".\"tableoid\"::regclass::varchar IN ('activity_books') AS _auto_cast"
325
320
  result << ' FROM "activities"'
326
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
327
321
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
328
- result << " WHERE \"record_class\".\"_record_class\" = 'activity_books'"
322
+ result << " WHERE \"activities\".\"tableoid\"::regclass::varchar IN ('activity_books')"
329
323
  expect(base.cast_records(child, filter: true).all.to_sql).to eql(result)
330
324
  end
331
325
 
332
326
  it 'works with count and does not add extra columns' do
333
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
334
- result << ' SELECT COUNT(*)'
327
+ result = 'SELECT COUNT(*)'
335
328
  result << ' FROM "activities"'
336
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
337
329
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
338
330
  result << ' LEFT OUTER JOIN "activity_posts" "i_1" ON "activities"."id" = "i_1"."id"'
339
331
  result << ' LEFT OUTER JOIN "activity_post_samples" "i_2" ON "activities"."id" = "i_2"."id"'
@@ -342,10 +334,8 @@ RSpec.describe 'TableInheritance' do
342
334
  end
343
335
 
344
336
  it 'works with sum and does not add extra columns' do
345
- result = 'WITH "record_class" AS (SELECT "pg_class"."oid", "pg_class"."relname" AS _record_class FROM "pg_class")'
346
- result << ' SELECT SUM("activities"."id")'
337
+ result = 'SELECT SUM("activities"."id")'
347
338
  result << ' FROM "activities"'
348
- result << ' INNER JOIN "record_class" ON "record_class"."oid" = "activities"."tableoid"'
349
339
  result << ' LEFT OUTER JOIN "activity_books" "i_0" ON "activities"."id" = "i_0"."id"'
350
340
  result << ' LEFT OUTER JOIN "activity_posts" "i_1" ON "activities"."id" = "i_1"."id"'
351
341
  result << ' LEFT OUTER JOIN "activity_post_samples" "i_2" ON "activities"."id" = "i_2"."id"'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: torque-postgresql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carlos Silva
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-16 00:00:00.000000000 Z
11
+ date: 2021-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails