activerecord-sqlserver-adapter 8.0.11 → 8.0.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab7b74bdd65380e149a3aaf18932da7617fad49c37b1b860ef10075c02e841e6
4
- data.tar.gz: e30b7c80bab6f9fdf6e12be1898ab6c16ba61aed8ced1b6f50dbe0dae8a2c6d1
3
+ metadata.gz: f29d990915f935a12f18189d83a0cc11916810756fdac53a52059fcf7e1b8b6b
4
+ data.tar.gz: a5cc606fc70a25ef265f5905c39488267f9a879a7453e422a233bf1e1310aa4d
5
5
  SHA512:
6
- metadata.gz: 505fed8945112dddbb083dafb7533c3c0901a4ea880244fa2cee278f39984ef4d74ec1a98b31057781591aa9a9c53c8b6d564826677c0b3d57d40dd98274f512
7
- data.tar.gz: 7829782f9cd4a23173c92e5b6265dd184331eeab2b0fedba15fd32b245e60ef0180a76358a6c5b0f9ff8ab38b30f427c8fc37ffb62805985933b89accb12a02d
6
+ metadata.gz: 5e23eb5561377a71f81fb04207bd943b9a30e81b8c01bf7b1a9957a0ade81b7877905dacb9134dce4ad0c129dcb582a487cc2334c1b14e075c804d0a3e803bed
7
+ data.tar.gz: 987035b58b5f7f3fd598d6f819e53a0eb653b8a8390199c56f30765b9f58f461f4a0a44833a3dbf4d6ca1e14f82fb39a3e9c30d469cd3702665dea4103566a69
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## v8.0.12
2
+
3
+ #### Fixed
4
+
5
+ - [#1418](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1418) Fix `change_column_null` to re-declare datetime2 columns correctly.
6
+
7
+ #### Changed
8
+
9
+ - [#1405](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1405) Fix `insert_all`/`upsert_all` for single character, temporary, non-ASCII and three part table names, and stop an aliased target being included in the `MERGE` table name.
10
+
1
11
  ## v8.0.11
2
12
 
3
13
  #### Changed
data/Gemfile CHANGED
@@ -13,6 +13,7 @@ gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
13
13
  gem "benchmark-ips"
14
14
  gem "minitest", "~> 6.0"
15
15
  gem "msgpack", ">= 1.7.0"
16
+ gem "json", ">= 2.0.0", "!=2.7.0", "< 2.20"
16
17
 
17
18
  if ENV["RAILS_SOURCE"]
18
19
  gemspec path: ENV["RAILS_SOURCE"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 8.0.11
1
+ 8.0.12
@@ -4,6 +4,9 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  module SchemaStatements
7
+ MERGE_TARGET_IDENTIFIER = /(?:\[[^\]]+\]|[a-z0-9_-]+)/i # :nodoc:
8
+ MERGE_TARGET_TABLE_NAME = /\A\s*MERGE\s+INTO\s+(#{MERGE_TARGET_IDENTIFIER}(?:\s*\.#{MERGE_TARGET_IDENTIFIER}){0,2})\s+(?:AS|WITH|USING)/i # :nodoc:
9
+
7
10
  def native_database_types
8
11
  @native_database_types ||= initialize_native_database_types.freeze
9
12
  end
@@ -349,12 +352,12 @@ module ActiveRecord
349
352
 
350
353
  def columns_for_distinct(columns, orders)
351
354
  order_columns = orders.reject(&:blank?).map { |s|
352
- s = visitor.compile(s) unless s.is_a?(String)
353
- s.gsub(/\s+(?:ASC|DESC)\b/i, "")
354
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
355
- }
356
- .reject(&:blank?)
357
- .reject { |s| columns.include?(s) }
355
+ s = visitor.compile(s) unless s.is_a?(String)
356
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
357
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
358
+ }
359
+ .reject(&:blank?)
360
+ .reject { |s| columns.include?(s) }
358
361
 
359
362
  order_columns_aliased = order_columns.map.with_index { |column, i| "#{column} AS alias_#{i}" }
360
363
 
@@ -374,7 +377,12 @@ module ActiveRecord
374
377
  if !null.nil? && null == false && !default.nil?
375
378
  execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL")
376
379
  end
377
- sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}"
380
+
381
+ # If datetime column has precision then it should be datetime2 column.
382
+ column_type = column.type
383
+ column_type = :datetime2 if column.type.to_s == "datetime" && !column.precision.nil?
384
+
385
+ sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column_type, limit: column.limit, precision: column.precision, scale: column.scale}"
378
386
  sql += " NOT NULL" if !null.nil? && null == false
379
387
 
380
388
  execute sql
@@ -720,7 +728,7 @@ module ActiveRecord
720
728
  elsif s.match?(/^\s*UPDATE\s+.*/i)
721
729
  s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1]
722
730
  elsif s.match?(/^\s*MERGE INTO.*/i)
723
- s.match(/^\s*MERGE\s+INTO\s+(\[?[a-z0-9_ -]+\]?\.?\[?[a-z0-9_ -]+\]?)\s+(AS|WITH|USING)/i)[1]
731
+ s.match(MERGE_TARGET_TABLE_NAME)[1]
724
732
  else
725
733
  s.match(/FROM[\s|\(]+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1]
726
734
  end.strip
@@ -745,24 +753,24 @@ module ActiveRecord
745
753
  @view_information ||= {}
746
754
 
747
755
  @view_information[table_name] ||= begin
748
- identifier = SQLServer::Utils.extract_identifiers(table_name)
749
- information_query_table = identifier.database.present? ? "[#{identifier.database}].[INFORMATION_SCHEMA].[VIEWS]" : "[INFORMATION_SCHEMA].[VIEWS]"
750
-
751
- view_info = select_one("SELECT * FROM #{information_query_table} WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA").to_h
752
-
753
- if view_info.present?
754
- if view_info['VIEW_DEFINITION'].blank? || view_info['VIEW_DEFINITION'].length == 4000
755
- view_info['VIEW_DEFINITION'] = begin
756
- select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join
757
- rescue
758
- warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
759
- nil
760
- end
761
- end
762
- end
763
-
764
- view_info
765
- end
756
+ identifier = SQLServer::Utils.extract_identifiers(table_name)
757
+ information_query_table = identifier.database.present? ? "[#{identifier.database}].[INFORMATION_SCHEMA].[VIEWS]" : "[INFORMATION_SCHEMA].[VIEWS]"
758
+
759
+ view_info = select_one("SELECT * FROM #{information_query_table} WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA").to_h
760
+
761
+ if view_info.present?
762
+ if view_info['VIEW_DEFINITION'].blank? || view_info['VIEW_DEFINITION'].length == 4000
763
+ view_info['VIEW_DEFINITION'] = begin
764
+ select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join
765
+ rescue
766
+ warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
767
+ nil
768
+ end
769
+ end
770
+ end
771
+
772
+ view_info
773
+ end
766
774
  end
767
775
 
768
776
  def views_real_column_name(table_name, column_name)
@@ -224,6 +224,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
224
224
  @identity_merge_sql = "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [id], [name], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([id], [name]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name] WHEN NOT MATCHED BY TARGET THEN INSERT ([id], [name]) VALUES (source.[id], source.[name]) OUTPUT INSERTED.[id]"
225
225
  @identity_merge_sql_unquoted = "MERGE INTO ships WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT id, name, ROW_NUMBER() OVER ( PARTITION BY id ORDER BY id DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 (id, name) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.id = source.id) WHEN MATCHED THEN UPDATE SET target.name = source.name WHEN NOT MATCHED BY TARGET THEN INSERT (id, name) VALUES (source.id, source.name) OUTPUT INSERTED.id"
226
226
  @identity_merge_sql_unordered = "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [name], [id], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([name], [id]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name] WHEN NOT MATCHED BY TARGET THEN INSERT ([name], [id]) VALUES (source.[name], source.[id]) OUTPUT INSERTED.[id]"
227
+ @identity_merge_sql_aliased_target = @identity_merge_sql.sub(" WITH (UPDLOCK, HOLDLOCK)", "")
227
228
 
228
229
  @identity_insert_sql_non_dbo = "INSERT INTO [test].[aliens] ([id],[name]) VALUES(420,'Mork')"
229
230
  @identity_insert_sql_non_dbo_unquoted = "INSERT INTO test.aliens ([id],[name]) VALUES(420,'Mork')"
@@ -247,6 +248,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
247
248
  assert_equal "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql)
248
249
  assert_equal "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql_unquoted)
249
250
  assert_equal "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql_unordered)
251
+ assert_equal "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql_aliased_target)
250
252
 
251
253
  assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo)
252
254
  assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted)
@@ -2,29 +2,31 @@
2
2
 
3
3
  require "cases/helper_sqlserver"
4
4
  require "migrations/create_clients_and_change_column_null"
5
+ require "cases/migration/helper"
5
6
 
6
7
  class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase
7
- before do
8
- @old_verbose = ActiveRecord::Migration.verbose
9
- ActiveRecord::Migration.verbose = false
10
- CreateClientsAndChangeColumnNull.new.up
11
- end
8
+ include ::ActiveRecord::Migration::TestHelper
12
9
 
13
- after do
14
- CreateClientsAndChangeColumnNull.new.down
15
- ActiveRecord::Migration.verbose = @old_verbose
10
+ def find_column(table, column_name)
11
+ connection.columns(table).find { |column| column.name == column_name.to_s }
16
12
  end
17
13
 
18
- def find_column(table, name)
19
- table.find { |column| column.name == name }
20
- end
14
+ describe "using migration" do
15
+ before do
16
+ @old_verbose = ActiveRecord::Migration.verbose
17
+ ActiveRecord::Migration.verbose = false
18
+ CreateClientsAndChangeColumnNull.new.up
19
+ end
20
+
21
+ after do
22
+ CreateClientsAndChangeColumnNull.new.down
23
+ ActiveRecord::Migration.verbose = @old_verbose
24
+ end
21
25
 
22
- let(:clients_table) { connection.columns("clients") }
23
- let(:name_column) { find_column(clients_table, "name") }
24
- let(:code_column) { find_column(clients_table, "code") }
25
- let(:value_column) { find_column(clients_table, "value") }
26
+ let(:name_column) { find_column("clients", "name") }
27
+ let(:code_column) { find_column("clients", "code") }
28
+ let(:value_column) { find_column("clients", "value") }
26
29
 
27
- describe "#change_column_null" do
28
30
  it "does not change the column limit" do
29
31
  _(name_column.limit).must_equal 15
30
32
  end
@@ -41,4 +43,32 @@ class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase
41
43
  _(value_column.scale).must_equal 8
42
44
  end
43
45
  end
46
+
47
+ it "datetime2 column" do
48
+ add_column :test_models, :expiry_date, :datetime, precision: 6, null: true
49
+
50
+ _(find_column(:test_models, :expiry_date).sql_type).must_equal "datetime2(6)"
51
+ _(find_column(:test_models, :expiry_date).null).must_equal true
52
+
53
+ connection.change_column_null(:test_models, :expiry_date, false)
54
+
55
+ _(find_column(:test_models, :expiry_date).sql_type).must_equal "datetime2(6)"
56
+ _(find_column(:test_models, :expiry_date).null).must_equal false
57
+ ensure
58
+ remove_column("test_models", "expiry_date")
59
+ end
60
+
61
+ it "datetime column" do
62
+ add_column :test_models, :expiry_date, :datetime, precision: nil, null: true
63
+
64
+ _(find_column(:test_models, :expiry_date).sql_type).must_equal "datetime"
65
+ _(find_column(:test_models, :expiry_date).null).must_equal true
66
+
67
+ connection.change_column_null(:test_models, :expiry_date, false)
68
+
69
+ _(find_column(:test_models, :expiry_date).sql_type).must_equal "datetime"
70
+ _(find_column(:test_models, :expiry_date).null).must_equal false
71
+ ensure
72
+ remove_column("test_models", "expiry_date")
73
+ end
44
74
  end
@@ -122,6 +122,38 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
122
122
  it do
123
123
  assert_equal "[with_numbers_1234]", connection.send(:get_raw_table_name, "MERGE INTO [with_numbers_1234] AS target")
124
124
  end
125
+
126
+ it do
127
+ assert_equal "[dashboards]", connection.send(:get_raw_table_name, "MERGE INTO [dashboards] AS target USING (VALUES (1)) AS source ([id]) ON (target.[id] = source.[id]) WHEN NOT MATCHED THEN INSERT ([id]) VALUES (source.[id]);")
128
+ end
129
+
130
+ it do
131
+ assert_equal "lock_without_defaults", connection.send(:get_raw_table_name, "MERGE INTO lock_without_defaults AS target USING (VALUES (1)) AS source ([id]) ON (target.[id] = source.[id]) WHEN NOT MATCHED THEN INSERT ([id]) VALUES (source.[id]);")
132
+ end
133
+
134
+ it do
135
+ assert_equal "[WITH - SPACES]", connection.send(:get_raw_table_name, "MERGE INTO [WITH - SPACES] AS target USING (VALUES (1)) AS source ([id]) ON (target.[id] = source.[id]) WHEN NOT MATCHED THEN INSERT ([id]) VALUES (source.[id]);")
136
+ end
137
+
138
+ it do
139
+ assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "MERGE INTO [with].[select notation] AS target USING (VALUES (1)) AS source ([id]) ON (target.[id] = source.[id]) WHEN NOT MATCHED THEN INSERT ([id]) VALUES (source.[id]);")
140
+ end
141
+
142
+ it do
143
+ assert_equal "[x]", connection.send(:get_raw_table_name, "MERGE INTO [x] WITH (UPDLOCK, HOLDLOCK) AS target USING (VALUES (1)) AS source ([id]) ON (target.[id] = source.[id]) WHEN NOT MATCHED THEN INSERT ([id]) VALUES (source.[id]);")
144
+ end
145
+
146
+ it do
147
+ assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "MERGE INTO [with].[select notation] WITH (UPDLOCK, HOLDLOCK) AS target USING (VALUES (1)) AS source ([id]) ON (target.[id] = source.[id]) WHEN NOT MATCHED THEN INSERT ([id]) VALUES (source.[id]);")
148
+ end
149
+
150
+ it do
151
+ assert_equal "[a].[dbo].[dashboards]", connection.send(:get_raw_table_name, "MERGE INTO [a].[dbo].[dashboards] WITH (UPDLOCK, HOLDLOCK) AS target USING (VALUES (1)) AS source ([id]) ON (target.[id] = source.[id]) WHEN NOT MATCHED THEN INSERT ([id]) VALUES (source.[id]);")
152
+ end
153
+
154
+ it do
155
+ assert_equal "with .dashboards", connection.send(:get_raw_table_name, "MERGE INTO with .dashboards WITH (UPDLOCK, HOLDLOCK) AS target USING (VALUES (1)) AS source ([id]) ON (target.[id] = source.[id]) WHEN NOT MATCHED THEN INSERT ([id]) VALUES (source.[id]);")
156
+ end
125
157
  end
126
158
 
127
159
  describe 'CREATE VIEW statements' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-sqlserver-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.11
4
+ version: 8.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Collins
@@ -243,8 +243,8 @@ licenses:
243
243
  - MIT
244
244
  metadata:
245
245
  bug_tracker_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues
246
- changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v8.0.11/CHANGELOG.md
247
- source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v8.0.11
246
+ changelog_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/v8.0.12/CHANGELOG.md
247
+ source_code_uri: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/v8.0.12
248
248
  rdoc_options: []
249
249
  require_paths:
250
250
  - lib
@@ -259,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
259
259
  - !ruby/object:Gem::Version
260
260
  version: '0'
261
261
  requirements: []
262
- rubygems_version: 3.6.7
262
+ rubygems_version: 4.0.16
263
263
  specification_version: 4
264
264
  summary: ActiveRecord SQL Server Adapter.
265
265
  test_files: