insert_select 0.1.0 → 1.0.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: 3ed398397217cbd7d7a636948b89a3cfb32bbfa6c9288619e249e014f895febc
4
- data.tar.gz: 015b72e911c1183215858260e3aa96dc6ec47000f2221ec7c7147929deb94167
3
+ metadata.gz: d101071399faca09bdda11935c64dd05cd278c5d6f09cb868297bd8a39763050
4
+ data.tar.gz: 955f3d4f643fc7d63898d6b790e442a58df26494e66893c4bf6f61cfa10d11fb
5
5
  SHA512:
6
- metadata.gz: 9b5ec0d676bb246917b348e656bcb20b89ea4fdb509ae834dc1f32704f2c8faa97a8ce394b6629deb26001f06cdc59550c01c78f49febbb94e936cc1ec88d9a1
7
- data.tar.gz: 5179a2af5b2b62670f42f1b1fec76f9c38cfc2c9d025e82de60b4d5f6e3a16df27096992896533abf25759b8423dc68afce0939e09b1486392022e74168b50fe
6
+ metadata.gz: e6be590eef3ffd3f4f746acc8334dd21175b1621c4a90a1dff4e8597e7bd6505f3d4b684c16e304fde6f86dd98f67cd80458f28b08fa0c47cb9fe06921caffa0
7
+ data.tar.gz: 75ec347e6e56e2eb405545c741645e81ac04ba1932a5c00515fa0411aebd3b6fc7c38a5213bf5fcce2c4470fd0f926a596a1deb18f8fb08322936ef243cb7758
data/Gemfile CHANGED
@@ -9,3 +9,5 @@ gem "rake", "~> 13.0"
9
9
 
10
10
  gem "rspec", "~> 3.0"
11
11
  gem "sqlite3"
12
+ gem "mysql2"
13
+ gem "pg"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- insert_select (0.1.0)
4
+ insert_select (1.0.0)
5
5
  activerecord
6
6
 
7
7
  GEM
@@ -22,6 +22,8 @@ GEM
22
22
  i18n (1.14.0)
23
23
  concurrent-ruby (~> 1.0)
24
24
  minitest (5.18.0)
25
+ mysql2 (0.5.5)
26
+ pg (1.5.3)
25
27
  rake (13.0.6)
26
28
  rspec (3.12.0)
27
29
  rspec-core (~> 3.12.0)
@@ -48,6 +50,8 @@ PLATFORMS
48
50
 
49
51
  DEPENDENCIES
50
52
  insert_select!
53
+ mysql2
54
+ pg
51
55
  rake (~> 13.0)
52
56
  rspec (~> 3.0)
53
57
  sqlite3
data/README.md CHANGED
@@ -2,7 +2,8 @@
2
2
  [![Ruby](https://github.com/a5-stable/insert_select/actions/workflows/ruby.yml/badge.svg)](https://github.com/a5-stable/insert_select/actions/workflows/ruby.yml)
3
3
  ![Code Climate](https://codeclimate.com/github/a5-stable/insert_select.png)
4
4
 
5
- This is a custom gem that extends ActiveRecord to enable the expression of SQL `INSERT INTO ... SELECT ...` queries in a more convenient way. It allows you to copy data from one table to another based on specified conditions using a simple and expressive syntax.
5
+ This is a custom gem that extends ActiveRecord to enable the expression of SQL `INSERT INTO ... SELECT ...` queries in a more convenient way.
6
+ It allows you to copy data from one table to another based on specified conditions using a simple and expressive syntax.
6
7
 
7
8
  SQL example of `INSERT INTO ... SELECT ...`:
8
9
  ```
@@ -78,8 +79,23 @@ NewUser.insert_select_from(OldUser, returning: [:id])
78
79
 
79
80
  #=> INSERT INTO "new_users" SELECT "old_users".* FROM "old_users" RETURNING "id"
80
81
  ```
82
+ ### bang method
83
+ In default, any duplicated records are skipped in the `insert_select_from` method.
84
+ If you want to raise an error when a duplicated record is found, use the bang method.
85
+
86
+ Assume that `id` is a primary key.
87
+ This example does not raise an `ActiveRecord::RecordNotUnique` exception and no record is inserted.
88
+ ```ruby
89
+ User.insert_select_from(User)
90
+ ```
91
+
92
+ This example raises an `ActiveRecord::RecordNotUnique` exception.
93
+ ```ruby
94
+ User.insert_select_from!(User)
95
+
96
+ #=> ActiveRecord::RecordNotUnique (SQLite3::ConstraintException: UNIQUE constraint failed: users.id)
97
+ ```
81
98
 
82
- Other options, which are enabled in [`insert_all`](https://www.rubydoc.info/github/rails/rails/ActiveRecord%2FPersistence%2FClassMethods:insert_all) should be also supported, but are not yet implemented.
83
99
 
84
100
  ## Development
85
101
 
data/Rakefile CHANGED
@@ -5,4 +5,15 @@ require "rspec/core/rake_task"
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
+ namespace :spec do
9
+ ["postgresql", "mysql2", "sqlite3"].each do |adapter|
10
+ RSpec::Core::RakeTask.new(adapter) do |t|
11
+ ENV["ADAPTER_NAME"] = adapter
12
+ ENV["DATABASE_NAME"] = adapter == "sqlite3" ? ":memory:" : "insert_select_test"
13
+ t.pattern = FileList["spec/*_spec.rb"]
14
+ end
15
+ end
16
+ end
17
+
18
+ task spec: ["spec:postgresql", "spec:mysql2", "spec:sqlite3"]
8
19
  task default: :spec
@@ -34,7 +34,11 @@ module InsertSelect
34
34
  # @return [ActiveRecord::Result] The result of the insert select operation.
35
35
  #
36
36
  def insert_select_from(relation, mapping: {}, returning: nil)
37
- InsertSelect::ActiveRecord::InsertSelectFrom.new(self, relation, mapping: mapping, returning: returning).execute
37
+ InsertSelect::ActiveRecord::InsertSelectFrom.new(self, relation, mapping: mapping, on_duplicate: :skip, returning: returning).execute
38
+ end
39
+
40
+ def insert_select_from!(relation, mapping: {}, returning: nil)
41
+ InsertSelect::ActiveRecord::InsertSelectFrom.new(self, relation, mapping: mapping, on_duplicate: nil, returning: returning).execute
38
42
  end
39
43
 
40
44
  def except(*columns)
@@ -44,20 +48,21 @@ module InsertSelect
44
48
  end
45
49
 
46
50
  class InsertSelectFrom
47
- attr_reader :model, :connection, :relation, :adapter, :mapping, :returning
51
+ attr_reader :model, :connection, :relation, :adapter, :mapping, :returning, :on_duplicate
48
52
 
49
- def initialize(model, relation, mapping:, returning: nil)
53
+ def initialize(model, relation, mapping:, on_duplicate:, returning: nil)
50
54
  @model = model
51
55
  @connection = model.connection
52
56
  @relation = relation
53
57
  @adapter = find_adapter(connection)
54
58
  @mapping = mapping
55
59
  @returning = returning
60
+ @on_duplicate = on_duplicate
56
61
  end
57
62
 
58
63
  def execute
59
64
  sql = model.sanitize_sql_array([to_sql, *builder.constant_values])
60
- connection.exec_insert_all(sql, "")
65
+ connection.exec_insert_all(sql, "Bulk Insert")
61
66
  end
62
67
 
63
68
  def to_sql
@@ -7,7 +7,13 @@ module InsertSelect
7
7
  end
8
8
 
9
9
  def build_sql(builder)
10
- super
10
+ stmt = super
11
+
12
+ if builder.on_duplicate == :skip
13
+ stmt << " ON DUPLICATE KEY UPDATE `id`= VALUES(`id`) "
14
+ end
15
+
16
+ stmt
11
17
  end
12
18
  end
13
19
  end
@@ -7,10 +7,12 @@ module InsertSelect
7
7
  end
8
8
 
9
9
  def build_sql(builder)
10
- sql = super
11
- sql += " RETURNING #{builder.returning}" if builder.returning
10
+ stmt = super
12
11
 
13
- sql
12
+ stmt << " ON CONFLICT DO NOTHING" if builder.on_duplicate == :skip
13
+ stmt << " RETURNING #{builder.returning}" if builder.returning?
14
+
15
+ stmt
14
16
  end
15
17
  end
16
18
  end
@@ -7,7 +7,18 @@ module InsertSelect
7
7
  end
8
8
 
9
9
  def build_sql(builder)
10
- super
10
+ # have to be done before we call super, because super will make relation immutable
11
+ if builder.on_duplicate == :skip
12
+ builder.relation.where!("TRUE") if builder.relation.where_clause.blank?
13
+ end
14
+
15
+ stmt = super
16
+
17
+ if builder.on_duplicate == :skip
18
+ stmt << " ON CONFLICT DO NOTHING"
19
+ end
20
+
21
+ stmt
11
22
  end
12
23
  end
13
24
  end
@@ -1,7 +1,7 @@
1
1
  module InsertSelect
2
2
  module ActiveRecord
3
3
  class Builder
4
- attr_reader :relation, :mapping, :model, :returning, :insert_select_from, :connection
4
+ attr_reader :relation, :mapping, :model,:insert_select_from, :connection, :returning, :on_duplicate
5
5
 
6
6
  def initialize(insert_select_from)
7
7
  @insert_select_from = insert_select_from
@@ -10,6 +10,7 @@ module InsertSelect
10
10
  @mapping = insert_select_from.mapping || {}
11
11
  @model = insert_select_from.model
12
12
  @returning = insert_select_from.returning
13
+ @on_duplicate = insert_select_from.on_duplicate
13
14
  end
14
15
 
15
16
  def mapper
@@ -90,6 +91,10 @@ module InsertSelect
90
91
  end
91
92
  end
92
93
 
94
+ def returning?
95
+ @returning.present?
96
+ end
97
+
93
98
  private
94
99
 
95
100
  def remove_select_values!(column_name)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InsertSelect
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: insert_select
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - a5-stable
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-06-22 00:00:00.000000000 Z
11
+ date: 2023-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord