active_record_upsert 0.9.4 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.base +1 -1
- data/{Gemfile.rails-5-0 → Gemfile.rails-6-0} +1 -1
- data/{Gemfile.rails-5-1 → Gemfile.rails-6-1} +1 -1
- data/Gemfile.rails-7-0 +5 -0
- data/README.md +76 -8
- data/active_record_upsert.gemspec +3 -4
- data/lib/active_record_upsert/active_record/persistence.rb +16 -13
- data/lib/active_record_upsert/arel/nodes/insert_statement.rb +0 -39
- data/lib/active_record_upsert/arel/table_extensions.rb +23 -0
- data/lib/active_record_upsert/compatibility/rails60.rb +18 -0
- data/lib/active_record_upsert/compatibility/rails70.rb +59 -0
- data/lib/active_record_upsert/version.rb +1 -1
- data/lib/active_record_upsert.rb +2 -2
- metadata +17 -37
- data/lib/active_record_upsert/arel/crud.rb +0 -31
- data/lib/active_record_upsert/compatibility/rails50.rb +0 -1
- data/lib/active_record_upsert/compatibility/rails51.rb +0 -63
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 459493128507f8a6ffd12fca94b8270cc93259601b4136c4686ae11f2acd7433
|
|
4
|
+
data.tar.gz: b3c8961297902a34a92d1f947980d4e1829d8e563401f54a7094d110df77fc66
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9007d2d2c1889c6b2742783319b7d0c878cf47d01bee568126984390b8ba73e52384aa84ebff72f1cd0f895a42833a53bf55cfe6339b5a07de0353fe307b1f5c
|
|
7
|
+
data.tar.gz: 8e643c603a776f476a4a0407a5bfa301b0ef8afa726000a32b79fa95a6b036ab04f8360183cf7d33b694912e2577745efbe17418dc572a9a5b6554a757cad864
|
data/Gemfile.base
CHANGED
data/Gemfile.rails-7-0
ADDED
data/README.md
CHANGED
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
[](https://travis-ci.org/jesjos/active_record_upsert)
|
|
3
3
|
[](https://codeclimate.com/github/jesjos/active_record_upsert)
|
|
4
4
|
|
|
5
|
-
# NB: INCOMPATIBILITY
|
|
6
|
-
B/c of a broken build matrix, versions 0.9.2 - 0.9.3 are incompatible with rails < 5.2.1!
|
|
7
|
-
|
|
8
5
|
# ActiveRecordUpsert
|
|
9
6
|
|
|
10
|
-
Real upsert for PostgreSQL 9.5+ and Rails 5 / ActiveRecord 5. Uses [ON CONFLICT DO UPDATE](http://www.postgresql.org/docs/9.5/static/sql-insert.html).
|
|
7
|
+
Real upsert for PostgreSQL 9.5+ and Rails 5.2+ / ActiveRecord 5.2+. Uses [ON CONFLICT DO UPDATE](http://www.postgresql.org/docs/9.5/static/sql-insert.html).
|
|
11
8
|
|
|
12
9
|
## Main points
|
|
13
10
|
|
|
@@ -17,11 +14,29 @@ Real upsert for PostgreSQL 9.5+ and Rails 5 / ActiveRecord 5. Uses [ON CONFLICT
|
|
|
17
14
|
|
|
18
15
|
## Prerequisites
|
|
19
16
|
|
|
20
|
-
- PostgreSQL 9.5+
|
|
21
|
-
- ActiveRecord
|
|
22
|
-
-
|
|
17
|
+
- PostgreSQL 9.5+ (that's when UPSERT support was added; see Wikipedia's [PostgreSQL Release History](https://en.wikipedia.org/wiki/PostgreSQL#Release_history))
|
|
18
|
+
- ActiveRecord >= 5.2
|
|
19
|
+
- Ruby MRI, with the `pg` gem
|
|
20
|
+
- _JRuby is currently not supported_
|
|
21
|
+
|
|
22
|
+
### NB: Releases to avoid
|
|
23
|
+
|
|
24
|
+
Due to a broken build matrix, v0.9.2 and v0.9.3 are incompatible with Rails
|
|
25
|
+
< 5.2.1. [v0.9.4](https://github.com/jesjos/active_record_upsert/releases/tag/v0.9.4) fixed this issue.
|
|
26
|
+
|
|
27
|
+
### Supported Rails versions
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
This library is compatible with all major Rails versions covered by the Rails
|
|
30
|
+
["Severe Security Issues" maintenance policy](https://guides.rubyonrails.org/maintenance_policy.html).
|
|
31
|
+
|
|
32
|
+
### Supported Ruby versions
|
|
33
|
+
|
|
34
|
+
This library may be compatible with older versions of Ruby, however we only run automated
|
|
35
|
+
tests using the
|
|
36
|
+
[officially supported Ruby versions](https://www.ruby-lang.org/en/downloads/branches/).
|
|
37
|
+
|
|
38
|
+
Please note that Ruby 3.1 is not currently tested because it is incompatible with Rails
|
|
39
|
+
`7.0.0`, `6.1.4.4`, `6.0.4.4` and `5.2.6` ([issue](https://github.com/rails/rails/issues/43998)).
|
|
25
40
|
|
|
26
41
|
## Installation
|
|
27
42
|
|
|
@@ -121,6 +136,37 @@ r = MyRecord.new(id: 1, name: 'bar')
|
|
|
121
136
|
r.upsert!
|
|
122
137
|
```
|
|
123
138
|
|
|
139
|
+
### Gotcha with database defaults
|
|
140
|
+
|
|
141
|
+
When a table is defined with a database default for a field, this gotcha can occur when trying to explicitly upsert a record _to_ the default value (from a non-default value).
|
|
142
|
+
|
|
143
|
+
**Example**: a table called `hardwares` has a `prio` column with a default value.
|
|
144
|
+
|
|
145
|
+
┌─────────┬─────────┬───────┬
|
|
146
|
+
│ Column │ Type │Default│
|
|
147
|
+
├─────────┼─────────┼───────┼
|
|
148
|
+
│ id │ integer │ ...
|
|
149
|
+
│ prio │ integer │ 999
|
|
150
|
+
|
|
151
|
+
And `hardwares` has a record with a non-default value for `prio`. Say, the record with `id` 1 has a `prio` of `998`.
|
|
152
|
+
|
|
153
|
+
In this situation, upserting like:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
hw = { id: 1, prio: 999 }
|
|
157
|
+
Hardware.new(prio: hw[:prio]).upsert
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
will not mention the `prio` column in the `ON CONFLICT` clause, resulting in no update.
|
|
161
|
+
|
|
162
|
+
However, upserting like so:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
Hardware.upsert(prio: hw[:prio]).id
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
will indeed update the record in the database back to its default value, `999`.
|
|
169
|
+
|
|
124
170
|
### Conflict Clauses
|
|
125
171
|
|
|
126
172
|
It's possible to specify which columns should be used for the conflict clause. **These must comprise a unique index in Postgres.**
|
|
@@ -158,6 +204,24 @@ class Account < ApplicationRecord
|
|
|
158
204
|
end
|
|
159
205
|
```
|
|
160
206
|
|
|
207
|
+
Overriding the models' `upsert_keys` when calling `#upsert` or `.upsert`:
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
Account.upsert(attrs, opts: { upsert_keys: [:foo, :bar] })
|
|
211
|
+
# Or, on an instance:
|
|
212
|
+
account = Account.new(attrs)
|
|
213
|
+
account.upsert(opts: { upsert_keys: [:foo, :bar] })
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Overriding the models' `upsert_options` (partial index) when calling `#upsert` or `.upsert`:
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
Account.upsert(attrs, opts: { upsert_options: { where: 'foo IS NOT NULL' } })
|
|
220
|
+
# Or, on an instance:
|
|
221
|
+
account = Account.new(attrs)
|
|
222
|
+
account.upsert(opts: { upsert_options: { where: 'foo IS NOT NULLL } })
|
|
223
|
+
```
|
|
224
|
+
|
|
161
225
|
## Tests
|
|
162
226
|
|
|
163
227
|
Make sure to have an upsert_test database:
|
|
@@ -188,3 +252,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jesjos
|
|
|
188
252
|
- Daniel Cooper ([@danielcooper](https://github.com/danielcooper))
|
|
189
253
|
- Laurent Vallar ([@val](https://github.com/val))
|
|
190
254
|
- Emmanuel Quentin ([@manuquentin](https://github.com/manuquentin))
|
|
255
|
+
- Jeff Wallace ([@tjwallace](https://github.com/tjwallace))
|
|
256
|
+
- Kirill Zaitsev ([@Bugagazavr](https://github.com/Bugagazavr))
|
|
257
|
+
- Nick Campbell ([@nickcampbell18](https://github.com/nickcampbell18))
|
|
258
|
+
- Mikhail Doronin ([@misdoro](https://github.com/misdoro))
|
|
@@ -13,15 +13,14 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
|
|
14
14
|
spec.summary = %q{Real PostgreSQL 9.5+ upserts using ON CONFLICT for ActiveRecord}
|
|
15
15
|
|
|
16
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(bin|test|spec|features)/}) } -
|
|
17
|
-
%w[.gitignore .rspec
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(.github|bin|test|spec|features)/}) } -
|
|
17
|
+
%w[.gitignore .rspec Dockerfile Gemfile Gemfile.docker docker-compose.yml]
|
|
18
18
|
spec.bindir = "exe"
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
20
20
|
spec.require_paths = ["lib"]
|
|
21
21
|
|
|
22
22
|
spec.platform = Gem::Platform::RUBY
|
|
23
23
|
|
|
24
|
-
spec.add_runtime_dependency 'activerecord', '>= 5.
|
|
25
|
-
spec.add_runtime_dependency 'arel', '> 7.0', '< 10.0'
|
|
24
|
+
spec.add_runtime_dependency 'activerecord', '>= 5.2', '< 7.1'
|
|
26
25
|
spec.add_runtime_dependency 'pg', '>= 0.18', '< 2.0'
|
|
27
26
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module ActiveRecordUpsert
|
|
2
2
|
module ActiveRecord
|
|
3
3
|
module PersistenceExtensions
|
|
4
|
-
def upsert!(attributes: nil, arel_condition: nil, validate: true)
|
|
4
|
+
def upsert!(attributes: nil, arel_condition: nil, validate: true, opts: {})
|
|
5
5
|
raise ::ActiveRecord::ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
|
|
6
6
|
raise ::ActiveRecord::RecordSavedError, "Can't upsert a record that has already been saved" if persisted?
|
|
7
7
|
validate == false || perform_validations || raise_validation_error
|
|
@@ -11,24 +11,26 @@ module ActiveRecordUpsert
|
|
|
11
11
|
attributes = attributes +
|
|
12
12
|
timestamp_attributes_for_create_in_model +
|
|
13
13
|
timestamp_attributes_for_update_in_model
|
|
14
|
-
_upsert_record(attributes.map(&:to_s).uniq, arel_condition)
|
|
14
|
+
_upsert_record(attributes.map(&:to_s).uniq, arel_condition, opts)
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
self
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def upsert(
|
|
22
|
-
upsert!(
|
|
21
|
+
def upsert(**kwargs)
|
|
22
|
+
upsert!(**kwargs)
|
|
23
23
|
rescue ::ActiveRecord::RecordInvalid
|
|
24
24
|
false
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def _upsert_record(upsert_attribute_names = changed, arel_condition = nil)
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
def _upsert_record(upsert_attribute_names = changed, arel_condition = nil, opts = {})
|
|
28
|
+
existing_attribute_names = attributes_for_create(attributes.keys)
|
|
29
|
+
existing_attributes = attributes_with_values(existing_attribute_names)
|
|
30
|
+
values = self.class._upsert_record(existing_attributes, upsert_attribute_names, [arel_condition].compact, opts)
|
|
30
31
|
@attributes = self.class.attributes_builder.build_from_database(values.first.to_h)
|
|
31
32
|
@new_record = false
|
|
33
|
+
changes_applied
|
|
32
34
|
values
|
|
33
35
|
end
|
|
34
36
|
|
|
@@ -39,24 +41,25 @@ module ActiveRecordUpsert
|
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
module ClassMethods
|
|
42
|
-
def upsert!(attributes, arel_condition: nil, validate: true, &block)
|
|
44
|
+
def upsert!(attributes, arel_condition: nil, validate: true, opts: {}, &block)
|
|
43
45
|
if attributes.is_a?(Array)
|
|
44
46
|
attributes.collect { |hash| upsert(hash, &block) }
|
|
45
47
|
else
|
|
46
48
|
new(attributes, &block).upsert!(
|
|
47
|
-
attributes: attributes.keys, arel_condition: arel_condition, validate: validate
|
|
49
|
+
attributes: attributes.keys, arel_condition: arel_condition, validate: validate, opts: opts
|
|
48
50
|
)
|
|
49
51
|
end
|
|
50
52
|
end
|
|
51
53
|
|
|
52
|
-
def upsert(
|
|
53
|
-
upsert!(
|
|
54
|
+
def upsert(attributes, **kwargs, &block)
|
|
55
|
+
upsert!(attributes, **kwargs, &block)
|
|
54
56
|
rescue ::ActiveRecord::RecordInvalid
|
|
55
57
|
false
|
|
56
58
|
end
|
|
57
59
|
|
|
58
|
-
def _upsert_record(existing_attributes, upsert_attributes_names, wheres) # :nodoc:
|
|
59
|
-
upsert_keys = self.upsert_keys || [primary_key]
|
|
60
|
+
def _upsert_record(existing_attributes, upsert_attributes_names, wheres, opts) # :nodoc:
|
|
61
|
+
upsert_keys = opts[:upsert_keys] || self.upsert_keys || [primary_key]
|
|
62
|
+
upsert_options = opts[:upsert_options] || self.upsert_options
|
|
60
63
|
upsert_attributes_names = upsert_attributes_names - [*upsert_keys, 'created_at']
|
|
61
64
|
|
|
62
65
|
existing_attributes = existing_attributes
|
|
@@ -1,47 +1,8 @@
|
|
|
1
|
-
# module ActiveRecordUpsert
|
|
2
|
-
# module Arel
|
|
3
|
-
# module Nodes
|
|
4
|
-
# module InsertStatementExtensions
|
|
5
|
-
# attr_accessor :on_conflict
|
|
6
|
-
#
|
|
7
|
-
# def initialize
|
|
8
|
-
# @on_conflict = nil
|
|
9
|
-
# super()
|
|
10
|
-
# end
|
|
11
|
-
#
|
|
12
|
-
# def hash
|
|
13
|
-
# [@relation, @columns, @values, @select, @on_conflict].hash
|
|
14
|
-
# end
|
|
15
|
-
#
|
|
16
|
-
# def eql? other
|
|
17
|
-
# self.class == other.class &&
|
|
18
|
-
# self.relation == other.relation &&
|
|
19
|
-
# self.columns == other.columns &&
|
|
20
|
-
# self.select == other.select &&
|
|
21
|
-
# self.values == other.values &&
|
|
22
|
-
# self.on_conflict == other.on_conflict
|
|
23
|
-
# end
|
|
24
|
-
# end
|
|
25
|
-
#
|
|
26
|
-
# ::Arel::Nodes::InsertStatement.prepend(InsertStatementExtensions)
|
|
27
|
-
# end
|
|
28
|
-
# end
|
|
29
|
-
# end
|
|
30
|
-
|
|
31
1
|
module Arel
|
|
32
2
|
module Nodes
|
|
33
3
|
class InsertStatement
|
|
34
4
|
attr_accessor :on_conflict
|
|
35
5
|
|
|
36
|
-
def initialize
|
|
37
|
-
super()
|
|
38
|
-
@relation = nil
|
|
39
|
-
@columns = []
|
|
40
|
-
@values = nil
|
|
41
|
-
@select = nil
|
|
42
|
-
@on_conflict = nil
|
|
43
|
-
end
|
|
44
|
-
|
|
45
6
|
def hash
|
|
46
7
|
[@relation, @columns, @values, @select, @on_conflict].hash
|
|
47
8
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module ActiveRecordUpsert
|
|
2
|
+
module Arel
|
|
3
|
+
module TableExtensions
|
|
4
|
+
def compile_upsert(upsert_keys, upsert_options, upsert_values, insert_values, wheres)
|
|
5
|
+
# Support non-attribute key (like `md5(my_attribute)``)
|
|
6
|
+
target = self[upsert_options.key?(:literal) ? ::Arel::Nodes::SqlLiteral.new(upsert_options[:literal]) : upsert_keys.join(',')]
|
|
7
|
+
on_conflict_do_update = ::Arel::OnConflictDoUpdateManager.new
|
|
8
|
+
|
|
9
|
+
on_conflict_do_update.target = target
|
|
10
|
+
on_conflict_do_update.target_condition = upsert_options[:where]
|
|
11
|
+
on_conflict_do_update.wheres = wheres
|
|
12
|
+
on_conflict_do_update.set(upsert_values)
|
|
13
|
+
|
|
14
|
+
insert_manager = ::Arel::InsertManager.new
|
|
15
|
+
insert_manager.on_conflict = on_conflict_do_update.to_node
|
|
16
|
+
insert_manager.into insert_values.first.first.relation
|
|
17
|
+
insert_manager.insert(insert_values)
|
|
18
|
+
insert_manager
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
::Arel::Table.prepend(TableExtensions)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module ActiveRecordUpsert
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module TransactionsExtensions
|
|
4
|
+
def upsert(*args, **kwargs)
|
|
5
|
+
with_transaction_returning_status { super }
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ConnectAdapterExtension
|
|
10
|
+
def upsert(*args, **kwargs)
|
|
11
|
+
::ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(self)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module ActiveRecordUpsert
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module PersistenceExtensions
|
|
4
|
+
module ClassMethods
|
|
5
|
+
def __substitute_values(values, table)
|
|
6
|
+
values.map do |name, value|
|
|
7
|
+
attr = table[name]
|
|
8
|
+
unless ::Arel.arel_node?(value) || value.is_a?(::ActiveModel::Attribute)
|
|
9
|
+
type = type_for_attribute(attr.name)
|
|
10
|
+
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
|
11
|
+
end
|
|
12
|
+
[attr, value]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def _upsert_record(existing_attributes, upsert_attributes_names, wheres, opts) # :nodoc:
|
|
17
|
+
upsert_keys = opts[:upsert_keys] || self.upsert_keys || [primary_key]
|
|
18
|
+
upsert_options = opts[:upsert_options] || self.upsert_options
|
|
19
|
+
upsert_attributes_names = upsert_attributes_names - [*upsert_keys, 'created_at']
|
|
20
|
+
|
|
21
|
+
existing_attributes = existing_attributes
|
|
22
|
+
.transform_keys { |name| _prepare_column(name) }
|
|
23
|
+
.reject { |key, _| key.nil? }
|
|
24
|
+
|
|
25
|
+
upsert_attributes_names = upsert_attributes_names
|
|
26
|
+
.map { |name| _prepare_column(name) }
|
|
27
|
+
.compact
|
|
28
|
+
|
|
29
|
+
values_for_upsert = existing_attributes.select { |(name, _value)| upsert_attributes_names.include?(name) }
|
|
30
|
+
|
|
31
|
+
insert_manager = arel_table.compile_upsert(
|
|
32
|
+
upsert_keys,
|
|
33
|
+
upsert_options,
|
|
34
|
+
__substitute_values(values_for_upsert, arel_table),
|
|
35
|
+
__substitute_values(existing_attributes, arel_table),
|
|
36
|
+
wheres
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
connection.upsert(insert_manager, "#{self} Upsert")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
module TransactionsExtensions
|
|
45
|
+
def upsert(*args, **kwargs)
|
|
46
|
+
with_transaction_returning_status { super }
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module ConnectAdapterExtension
|
|
51
|
+
def upsert(*args, **kwargs)
|
|
52
|
+
::ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
53
|
+
super
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(self)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
data/lib/active_record_upsert.rb
CHANGED
|
@@ -13,8 +13,8 @@ require 'active_record_upsert/active_record'
|
|
|
13
13
|
|
|
14
14
|
version = defined?(Rails) ? Rails.version : ActiveRecord.version.to_s
|
|
15
15
|
|
|
16
|
-
require 'active_record_upsert/compatibility/
|
|
17
|
-
require 'active_record_upsert/compatibility/
|
|
16
|
+
require 'active_record_upsert/compatibility/rails70.rb' if version >= '7.0.0' && version < '7.2.0'
|
|
17
|
+
require 'active_record_upsert/compatibility/rails60.rb' if version >= '6.0.0' && version < '6.2.0'
|
|
18
18
|
|
|
19
19
|
module ActiveRecordUpsert
|
|
20
20
|
# Your code goes here...
|
metadata
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_record_upsert
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jesper Josefsson
|
|
8
8
|
- Olle Jonsson
|
|
9
|
-
autorequire:
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2022-01-07 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activerecord
|
|
@@ -17,40 +17,20 @@ dependencies:
|
|
|
17
17
|
requirements:
|
|
18
18
|
- - ">="
|
|
19
19
|
- !ruby/object:Gem::Version
|
|
20
|
-
version: '5.
|
|
20
|
+
version: '5.2'
|
|
21
21
|
- - "<"
|
|
22
22
|
- !ruby/object:Gem::Version
|
|
23
|
-
version: '
|
|
23
|
+
version: '7.1'
|
|
24
24
|
type: :runtime
|
|
25
25
|
prerelease: false
|
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
|
27
27
|
requirements:
|
|
28
28
|
- - ">="
|
|
29
29
|
- !ruby/object:Gem::Version
|
|
30
|
-
version: '5.
|
|
30
|
+
version: '5.2'
|
|
31
31
|
- - "<"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
34
|
-
- !ruby/object:Gem::Dependency
|
|
35
|
-
name: arel
|
|
36
|
-
requirement: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '7.0'
|
|
41
|
-
- - "<"
|
|
42
|
-
- !ruby/object:Gem::Version
|
|
43
|
-
version: '10.0'
|
|
44
|
-
type: :runtime
|
|
45
|
-
prerelease: false
|
|
46
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
47
|
-
requirements:
|
|
48
|
-
- - ">"
|
|
49
|
-
- !ruby/object:Gem::Version
|
|
50
|
-
version: '7.0'
|
|
51
|
-
- - "<"
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '10.0'
|
|
33
|
+
version: '7.1'
|
|
54
34
|
- !ruby/object:Gem::Dependency
|
|
55
35
|
name: pg
|
|
56
36
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -71,7 +51,7 @@ dependencies:
|
|
|
71
51
|
- - "<"
|
|
72
52
|
- !ruby/object:Gem::Version
|
|
73
53
|
version: '2.0'
|
|
74
|
-
description:
|
|
54
|
+
description:
|
|
75
55
|
email:
|
|
76
56
|
- jesper.josefsson@gmail.com
|
|
77
57
|
- olle.jonsson@gmail.com
|
|
@@ -80,9 +60,10 @@ extensions: []
|
|
|
80
60
|
extra_rdoc_files: []
|
|
81
61
|
files:
|
|
82
62
|
- Gemfile.base
|
|
83
|
-
- Gemfile.rails-5-0
|
|
84
|
-
- Gemfile.rails-5-1
|
|
85
63
|
- Gemfile.rails-5-2
|
|
64
|
+
- Gemfile.rails-6-0
|
|
65
|
+
- Gemfile.rails-6-1
|
|
66
|
+
- Gemfile.rails-7-0
|
|
86
67
|
- Gemfile.rails-master
|
|
87
68
|
- LICENSE
|
|
88
69
|
- README.md
|
|
@@ -96,7 +77,6 @@ files:
|
|
|
96
77
|
- lib/active_record_upsert/active_record/timestamp.rb
|
|
97
78
|
- lib/active_record_upsert/active_record/transactions.rb
|
|
98
79
|
- lib/active_record_upsert/arel.rb
|
|
99
|
-
- lib/active_record_upsert/arel/crud.rb
|
|
100
80
|
- lib/active_record_upsert/arel/insert_manager.rb
|
|
101
81
|
- lib/active_record_upsert/arel/nodes.rb
|
|
102
82
|
- lib/active_record_upsert/arel/nodes/do_nothing.rb
|
|
@@ -106,15 +86,16 @@ files:
|
|
|
106
86
|
- lib/active_record_upsert/arel/nodes/on_conflict.rb
|
|
107
87
|
- lib/active_record_upsert/arel/nodes/on_conflict_action.rb
|
|
108
88
|
- lib/active_record_upsert/arel/on_conflict_do_update_manager.rb
|
|
89
|
+
- lib/active_record_upsert/arel/table_extensions.rb
|
|
109
90
|
- lib/active_record_upsert/arel/visitors/to_sql.rb
|
|
110
|
-
- lib/active_record_upsert/compatibility/
|
|
111
|
-
- lib/active_record_upsert/compatibility/
|
|
91
|
+
- lib/active_record_upsert/compatibility/rails60.rb
|
|
92
|
+
- lib/active_record_upsert/compatibility/rails70.rb
|
|
112
93
|
- lib/active_record_upsert/version.rb
|
|
113
94
|
homepage: https://github.com/jesjos/active_record_upsert/
|
|
114
95
|
licenses:
|
|
115
96
|
- MIT
|
|
116
97
|
metadata: {}
|
|
117
|
-
post_install_message:
|
|
98
|
+
post_install_message:
|
|
118
99
|
rdoc_options: []
|
|
119
100
|
require_paths:
|
|
120
101
|
- lib
|
|
@@ -129,9 +110,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
129
110
|
- !ruby/object:Gem::Version
|
|
130
111
|
version: '0'
|
|
131
112
|
requirements: []
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
signing_key:
|
|
113
|
+
rubygems_version: 3.2.7
|
|
114
|
+
signing_key:
|
|
135
115
|
specification_version: 4
|
|
136
116
|
summary: Real PostgreSQL 9.5+ upserts using ON CONFLICT for ActiveRecord
|
|
137
117
|
test_files: []
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# module ActiveRecordUpsert
|
|
2
|
-
# module Arel
|
|
3
|
-
# module CrudExtensions
|
|
4
|
-
# def create_on_conflict_do_update
|
|
5
|
-
# OnConflictDoUpdateManager.new
|
|
6
|
-
# end
|
|
7
|
-
# end
|
|
8
|
-
#
|
|
9
|
-
# ::Arel::Crud.prepend(CrudExtensions)
|
|
10
|
-
# end
|
|
11
|
-
# end
|
|
12
|
-
module Arel
|
|
13
|
-
module Crud
|
|
14
|
-
def compile_upsert(upsert_keys, upsert_options, upsert_values, insert_values, wheres)
|
|
15
|
-
# Support non-attribute key (like `md5(my_attribute)``)
|
|
16
|
-
target = self[upsert_options.key?(:literal) ? ::Arel::Nodes::SqlLiteral.new(upsert_options[:literal]) : upsert_keys.join(',')]
|
|
17
|
-
on_conflict_do_update = OnConflictDoUpdateManager.new
|
|
18
|
-
|
|
19
|
-
on_conflict_do_update.target = target
|
|
20
|
-
on_conflict_do_update.target_condition = upsert_options[:where]
|
|
21
|
-
on_conflict_do_update.wheres = wheres
|
|
22
|
-
on_conflict_do_update.set(upsert_values)
|
|
23
|
-
|
|
24
|
-
insert_manager = create_insert
|
|
25
|
-
insert_manager.on_conflict = on_conflict_do_update.to_node
|
|
26
|
-
insert_manager.into insert_values.first.first.relation
|
|
27
|
-
insert_manager.insert(insert_values)
|
|
28
|
-
insert_manager
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
require_relative 'rails51'
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
module ActiveRecordUpsert
|
|
2
|
-
module ActiveRecord
|
|
3
|
-
module PersistenceExtensions
|
|
4
|
-
def _upsert_record(upsert_attribute_names = changed, arel_condition = nil)
|
|
5
|
-
upsert_attribute_names = upsert_attribute_names.map { |name| _prepare_column(name) } & self.class.column_names
|
|
6
|
-
existing_attributes = arel_attributes_with_values_for_create(self.class.column_names)
|
|
7
|
-
values = self.class.unscoped.upsert(existing_attributes, upsert_attribute_names, [arel_condition].compact)
|
|
8
|
-
@new_record = false
|
|
9
|
-
@attributes = self.class.attributes_builder.build_from_database(values.first.to_h)
|
|
10
|
-
values
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def _prepare_column(name)
|
|
14
|
-
if self.class.reflections.key?(name)
|
|
15
|
-
self.class.reflections[name].foreign_key
|
|
16
|
-
else
|
|
17
|
-
name
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
module RelationExtensions
|
|
23
|
-
def upsert(existing_attributes, upsert_attributes, wheres) # :nodoc:
|
|
24
|
-
substitutes, binds = substitute_values(existing_attributes)
|
|
25
|
-
upsert_keys = self.klass.upsert_keys || [primary_key]
|
|
26
|
-
|
|
27
|
-
upsert_attributes = upsert_attributes - [*upsert_keys, 'created_at']
|
|
28
|
-
upsert_keys_filter = ->(o) { upsert_attributes.include?(o.name) }
|
|
29
|
-
|
|
30
|
-
on_conflict_binds = binds.select(&upsert_keys_filter)
|
|
31
|
-
vals_for_upsert = substitutes.select { |s| upsert_keys_filter.call(s.first) }
|
|
32
|
-
|
|
33
|
-
target = arel_table[upsert_options.key?(:literal) ? ::Arel::Nodes::SqlLiteral.new(upsert_options[:literal]) : upsert_keys.join(',')]
|
|
34
|
-
|
|
35
|
-
on_conflict_do_update = ::Arel::OnConflictDoUpdateManager.new
|
|
36
|
-
on_conflict_do_update.target = target
|
|
37
|
-
on_conflict_do_update.target_condition = self.klass.upsert_options[:where]
|
|
38
|
-
on_conflict_do_update.wheres = wheres
|
|
39
|
-
on_conflict_do_update.set(vals_for_upsert)
|
|
40
|
-
|
|
41
|
-
insert_manager = arel_table.create_insert
|
|
42
|
-
insert_manager.into arel_table
|
|
43
|
-
insert_manager.on_conflict = on_conflict_do_update.to_node
|
|
44
|
-
insert_manager.insert substitutes
|
|
45
|
-
|
|
46
|
-
@klass.connection.upsert(insert_manager, "#{self} Upsert", binds + on_conflict_binds)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
::ActiveRecord::Relation.include(self)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
module ConnectionAdapters
|
|
53
|
-
module Postgresql
|
|
54
|
-
module DatabaseStatementsExtensions
|
|
55
|
-
def upsert(arel, name = nil, binds = [])
|
|
56
|
-
sql = to_sql(arel, binds)
|
|
57
|
-
exec_upsert(sql, name, binds)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|