activerecord-sqlserver-adapter 5.2.1 → 7.0.0.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.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/issue_template.md +23 -0
  4. data/.github/workflows/ci.yml +29 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +29 -0
  7. data/CHANGELOG.md +17 -27
  8. data/{Dockerfile → Dockerfile.ci} +1 -1
  9. data/Gemfile +49 -41
  10. data/Guardfile +9 -8
  11. data/MIT-LICENSE +1 -1
  12. data/README.md +65 -42
  13. data/RUNNING_UNIT_TESTS.md +3 -0
  14. data/Rakefile +14 -16
  15. data/VERSION +1 -1
  16. data/activerecord-sqlserver-adapter.gemspec +25 -14
  17. data/appveyor.yml +22 -17
  18. data/docker-compose.ci.yml +7 -5
  19. data/guides/RELEASING.md +11 -0
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +5 -4
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +10 -14
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +12 -5
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +10 -7
  26. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +30 -0
  27. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -4
  28. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +117 -52
  29. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
  30. data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
  31. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +51 -14
  32. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +40 -6
  33. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +18 -10
  34. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +235 -167
  35. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
  36. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
  37. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
  38. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +36 -7
  39. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -45
  40. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +8 -10
  41. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
  42. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
  43. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
  44. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
  45. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -3
  46. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +7 -5
  47. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
  48. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
  51. data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
  53. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
  54. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
  55. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
  56. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
  57. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
  58. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
  59. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
  60. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
  61. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
  62. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
  63. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
  64. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
  65. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
  68. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
  69. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
  70. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
  71. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
  72. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
  73. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
  74. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
  75. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
  76. data/lib/active_record/connection_adapters/sqlserver/type.rb +38 -35
  77. data/lib/active_record/connection_adapters/sqlserver/utils.rb +26 -12
  78. data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
  79. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +271 -180
  80. data/lib/active_record/connection_adapters/sqlserver_column.rb +76 -16
  81. data/lib/active_record/sqlserver_base.rb +11 -9
  82. data/lib/active_record/tasks/sqlserver_database_tasks.rb +38 -39
  83. data/lib/activerecord-sqlserver-adapter.rb +3 -1
  84. data/lib/arel/visitors/sqlserver.rb +177 -56
  85. data/lib/arel_sqlserver.rb +4 -2
  86. data/test/appveyor/dbsetup.ps1 +4 -4
  87. data/test/cases/active_schema_test_sqlserver.rb +55 -0
  88. data/test/cases/adapter_test_sqlserver.rb +258 -173
  89. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  90. data/test/cases/change_column_null_test_sqlserver.rb +14 -12
  91. data/test/cases/coerced_tests.rb +1421 -397
  92. data/test/cases/column_test_sqlserver.rb +321 -315
  93. data/test/cases/connection_test_sqlserver.rb +17 -20
  94. data/test/cases/disconnected_test_sqlserver.rb +39 -0
  95. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
  96. data/test/cases/execute_procedure_test_sqlserver.rb +28 -19
  97. data/test/cases/fetch_test_sqlserver.rb +33 -21
  98. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
  99. data/test/cases/helper_sqlserver.rb +15 -15
  100. data/test/cases/in_clause_test_sqlserver.rb +63 -0
  101. data/test/cases/index_test_sqlserver.rb +15 -15
  102. data/test/cases/json_test_sqlserver.rb +25 -25
  103. data/test/cases/lateral_test_sqlserver.rb +35 -0
  104. data/test/cases/migration_test_sqlserver.rb +74 -27
  105. data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
  106. data/test/cases/order_test_sqlserver.rb +59 -53
  107. data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
  108. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  109. data/test/cases/rake_test_sqlserver.rb +70 -45
  110. data/test/cases/schema_dumper_test_sqlserver.rb +124 -109
  111. data/test/cases/schema_test_sqlserver.rb +20 -26
  112. data/test/cases/scratchpad_test_sqlserver.rb +4 -4
  113. data/test/cases/showplan_test_sqlserver.rb +28 -35
  114. data/test/cases/specific_schema_test_sqlserver.rb +68 -65
  115. data/test/cases/transaction_test_sqlserver.rb +18 -20
  116. data/test/cases/trigger_test_sqlserver.rb +14 -13
  117. data/test/cases/utils_test_sqlserver.rb +70 -70
  118. data/test/cases/uuid_test_sqlserver.rb +13 -14
  119. data/test/debug.rb +8 -6
  120. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  121. data/test/migrations/create_clients_and_change_column_null.rb +3 -1
  122. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
  123. data/test/models/sqlserver/booking.rb +3 -1
  124. data/test/models/sqlserver/composite_pk.rb +9 -0
  125. data/test/models/sqlserver/customers_view.rb +3 -1
  126. data/test/models/sqlserver/datatype.rb +2 -0
  127. data/test/models/sqlserver/datatype_migration.rb +2 -0
  128. data/test/models/sqlserver/dollar_table_name.rb +3 -1
  129. data/test/models/sqlserver/edge_schema.rb +3 -3
  130. data/test/models/sqlserver/fk_has_fk.rb +3 -1
  131. data/test/models/sqlserver/fk_has_pk.rb +3 -1
  132. data/test/models/sqlserver/natural_pk_data.rb +4 -2
  133. data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
  134. data/test/models/sqlserver/no_pk_data.rb +3 -1
  135. data/test/models/sqlserver/object_default.rb +3 -1
  136. data/test/models/sqlserver/quoted_table.rb +4 -2
  137. data/test/models/sqlserver/quoted_view_1.rb +3 -1
  138. data/test/models/sqlserver/quoted_view_2.rb +3 -1
  139. data/test/models/sqlserver/sst_memory.rb +3 -1
  140. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  141. data/test/models/sqlserver/string_default.rb +3 -1
  142. data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
  143. data/test/models/sqlserver/string_defaults_view.rb +3 -1
  144. data/test/models/sqlserver/tinyint_pk.rb +3 -1
  145. data/test/models/sqlserver/trigger.rb +4 -2
  146. data/test/models/sqlserver/trigger_history.rb +3 -1
  147. data/test/models/sqlserver/upper.rb +3 -1
  148. data/test/models/sqlserver/uppered.rb +3 -1
  149. data/test/models/sqlserver/uuid.rb +3 -1
  150. data/test/schema/sqlserver_specific_schema.rb +56 -21
  151. data/test/support/coerceable_test_sqlserver.rb +19 -13
  152. data/test/support/connection_reflection.rb +3 -2
  153. data/test/support/core_ext/query_cache.rb +4 -1
  154. data/test/support/load_schema_sqlserver.rb +5 -5
  155. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
  156. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
  157. data/test/support/minitest_sqlserver.rb +3 -1
  158. data/test/support/paths_sqlserver.rb +11 -11
  159. data/test/support/rake_helpers.rb +15 -10
  160. data/test/support/sql_counter_sqlserver.rb +16 -15
  161. data/test/support/test_in_memory_oltp.rb +9 -7
  162. metadata +47 -13
  163. data/.travis.yml +0 -25
  164. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +0 -26
@@ -1,19 +1,21 @@
1
- require 'cases/helper_sqlserver'
2
- require 'models/topic'
3
- require 'models/task'
4
- require 'models/post'
5
- require 'models/subscriber'
6
- require 'models/minimalistic'
1
+ # frozen_string_literal: true
7
2
 
8
- class AdapterTestSQLServer < ActiveRecord::TestCase
3
+ require "cases/helper_sqlserver"
4
+ require "models/topic"
5
+ require "models/task"
6
+ require "models/post"
7
+ require "models/subscriber"
8
+ require "models/minimalistic"
9
+ require "models/college"
9
10
 
11
+ class AdapterTestSQLServer < ActiveRecord::TestCase
10
12
  fixtures :tasks
11
13
 
12
14
  let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" }
13
15
  let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" }
14
16
  let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" }
15
17
 
16
- it 'has basic and non-senstive information in the adpaters inspect method' do
18
+ it "has basic and non-sensitive information in the adapters inspect method" do
17
19
  string = connection.inspect
18
20
  _(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter}
19
21
  _(string).must_match %r{version\: \d.\d}
@@ -25,48 +27,108 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
25
27
  _(string).wont_match %r{port}
26
28
  end
27
29
 
28
- it 'has a 128 max #table_alias_length' do
30
+ it "has a 128 max #table_alias_length" do
29
31
  assert connection.table_alias_length <= 128
30
32
  end
31
33
 
32
- it 'raises invalid statement error for bad SQL' do
34
+ it "raises invalid statement error for bad SQL" do
33
35
  assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") }
34
36
  end
35
37
 
36
- it 'is has our adapter_name' do
37
- assert_equal 'SQLServer', connection.adapter_name
38
+ it "is has our adapter_name" do
39
+ assert_equal "SQLServer", connection.adapter_name
38
40
  end
39
41
 
40
- it 'support DDL in transactions' do
42
+ it "support DDL in transactions" do
41
43
  assert connection.supports_ddl_transactions?
42
44
  end
43
45
 
44
- it 'allow owner table name prefixs like dbo to still allow table exists to return true' do
46
+ it "table exists works if table name prefixed by schema and owner" do
45
47
  begin
46
- assert_equal 'topics', Topic.table_name
48
+ assert_equal "topics", Topic.table_name
47
49
  assert Topic.table_exists?
48
- Topic.table_name = 'dbo.topics'
49
- assert Topic.table_exists?, 'Tasks table name of dbo.topics should return true for exists.'
50
+
51
+ # Test when owner included in table name.
52
+ Topic.table_name = "dbo.topics"
53
+ assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists."
54
+
55
+ # Test when database and owner included in table name.
56
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
57
+ Topic.table_name = "#{db_config.database}.dbo.topics"
58
+ assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists."
50
59
  ensure
51
- Topic.table_name = 'topics'
60
+ Topic.table_name = "topics"
52
61
  end
53
62
  end
54
63
 
55
- it 'return true to insert sql query for inserts only' do
56
- assert connection.send(:insert_sql?,'INSERT...')
64
+ it "test table existence across database schemas" do
65
+ arunit_connection = Topic.connection
66
+ arunit2_connection = College.connection
67
+
68
+ arunit_database = arunit_connection.pool.db_config.database
69
+ arunit2_database = arunit2_connection.pool.db_config.database
70
+
71
+ # Assert that connections use different default databases schemas.
72
+ assert_not_equal arunit_database, arunit2_database
73
+
74
+ # Assert that the Topics table exists when using the Topics connection.
75
+ assert arunit_connection.table_exists?('topics'), 'Topics table exists using table name'
76
+ assert arunit_connection.table_exists?('dbo.topics'), 'Topics table exists using owner and table name'
77
+ assert arunit_connection.table_exists?("#{arunit_database}.dbo.topics"), 'Topics table exists using database, owner and table name'
78
+
79
+ # Assert that the Colleges table exists when using the Colleges connection.
80
+ assert arunit2_connection.table_exists?('colleges'), 'College table exists using table name'
81
+ assert arunit2_connection.table_exists?('dbo.colleges'), 'College table exists using owner and table name'
82
+ assert arunit2_connection.table_exists?("#{arunit2_database}.dbo.colleges"), 'College table exists using database, owner and table name'
83
+
84
+ # Assert that the tables exist when using each others connection.
85
+ assert arunit_connection.table_exists?("#{arunit2_database}.dbo.colleges"), 'Colleges table exists using Topics connection'
86
+ assert arunit2_connection.table_exists?("#{arunit_database}.dbo.topics"), 'Topics table exists using Colleges connection'
87
+ end
88
+
89
+ it "return true to insert sql query for inserts only" do
90
+ assert connection.send(:insert_sql?, "INSERT...")
57
91
  assert connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0")
58
- assert !connection.send(:insert_sql?,'UPDATE...')
59
- assert !connection.send(:insert_sql?,'SELECT...')
92
+ assert !connection.send(:insert_sql?, "UPDATE...")
93
+ assert !connection.send(:insert_sql?, "SELECT...")
94
+ end
95
+
96
+ it "return unquoted table name object from basic INSERT UPDATE and SELECT statements" do
97
+ assert_equal "funny_jokes", connection.send(:get_table_name, basic_insert_sql)
98
+ assert_equal "customers", connection.send(:get_table_name, basic_update_sql)
99
+ assert_equal "customers", connection.send(:get_table_name, basic_select_sql)
100
+ end
101
+
102
+ it "test bad connection" do
103
+ assert_raise ActiveRecord::NoDatabaseError do
104
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
105
+ configuration = db_config.configuration_hash.merge(database: "inexistent_activerecord_unittest")
106
+ ActiveRecord::Base.sqlserver_connection configuration
107
+ end
108
+ end
109
+
110
+ it "test database exists returns false if database does not exist" do
111
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
112
+ configuration = db_config.configuration_hash.merge(database: "inexistent_activerecord_unittest")
113
+ assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(configuration),
114
+ "expected database to not exist"
60
115
  end
61
116
 
62
- it 'return unquoted table name object from basic INSERT UPDATE and SELECT statements' do
63
- assert_equal 'funny_jokes', connection.send(:get_table_name, basic_insert_sql)
64
- assert_equal 'customers', connection.send(:get_table_name, basic_update_sql)
65
- assert_equal 'customers', connection.send(:get_table_name, basic_select_sql)
117
+ it "test database exists returns true when the database exists" do
118
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
119
+ assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(db_config.configuration_hash),
120
+ "expected database #{db_config.database} to exist"
66
121
  end
67
122
 
68
- describe 'with different language' do
123
+ it "test primary key violation" do
124
+ Post.create!(id: 0, title: 'Setup', body: 'Create post with primary key of zero')
125
+
126
+ assert_raise ActiveRecord::RecordNotUnique do
127
+ Post.create!(id: 0, title: 'Test', body: 'Try to create another post with primary key of zero')
128
+ end
129
+ end
69
130
 
131
+ describe "with different language" do
70
132
  before do
71
133
  @default_language = connection.user_options_language
72
134
  end
@@ -76,18 +138,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
76
138
  connection.send :initialize_dateformatter
77
139
  end
78
140
 
79
- it 'memos users dateformat' do
141
+ it "memos users dateformat" do
80
142
  connection.execute("SET LANGUAGE us_english") rescue nil
81
143
  dateformat = connection.instance_variable_get(:@database_dateformat)
82
- assert_equal 'mdy', dateformat
144
+ assert_equal "mdy", dateformat
83
145
  end
84
146
 
85
- it 'has a dateformatter' do
147
+ it "has a dateformatter" do
86
148
  assert Date::DATE_FORMATS[:_sqlserver_dateformat]
87
149
  assert Time::DATE_FORMATS[:_sqlserver_dateformat]
88
150
  end
89
151
 
90
- it 'does a datetime insertion when language is german' do
152
+ it "does a datetime insertion when language is german" do
91
153
  connection.execute("SET LANGUAGE deutsch")
92
154
  connection.send :initialize_dateformatter
93
155
  assert_nothing_raised do
@@ -96,40 +158,36 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
96
158
  Task.create! starting: starting, ending: ending
97
159
  end
98
160
  end
99
-
100
161
  end
101
162
 
102
- describe 'testing #lowercase_schema_reflection' do
103
-
163
+ describe "testing #lowercase_schema_reflection" do
104
164
  before do
105
165
  SSTestUpper.delete_all
106
- SSTestUpper.create COLUMN1: 'Got a minute?', COLUMN2: 419
107
- SSTestUpper.create COLUMN1: 'Favorite number?', COLUMN2: 69
166
+ SSTestUpper.create COLUMN1: "Got a minute?", COLUMN2: 419
167
+ SSTestUpper.create COLUMN1: "Favorite number?", COLUMN2: 69
108
168
  end
109
169
 
110
170
  after do
111
171
  connection.lowercase_schema_reflection = false
112
172
  end
113
173
 
114
- it 'not lowercase schema reflection by default' do
115
- assert SSTestUpper.columns_hash['COLUMN1']
116
- assert_equal 'Got a minute?', SSTestUpper.first.COLUMN1
117
- assert_equal 'Favorite number?', SSTestUpper.last.COLUMN1
118
- assert SSTestUpper.columns_hash['COLUMN2']
174
+ it "not lowercase schema reflection by default" do
175
+ assert SSTestUpper.columns_hash["COLUMN1"]
176
+ assert_equal "Got a minute?", SSTestUpper.first.COLUMN1
177
+ assert_equal "Favorite number?", SSTestUpper.last.COLUMN1
178
+ assert SSTestUpper.columns_hash["COLUMN2"]
119
179
  end
120
180
 
121
- it 'lowercase schema reflection when set' do
181
+ it "lowercase schema reflection when set" do
122
182
  connection.lowercase_schema_reflection = true
123
- assert SSTestUppered.columns_hash['column1']
124
- assert_equal 'Got a minute?', SSTestUppered.first.column1
125
- assert_equal 'Favorite number?', SSTestUppered.last.column1
126
- assert SSTestUppered.columns_hash['column2']
183
+ assert SSTestUppered.columns_hash["column1"]
184
+ assert_equal "Got a minute?", SSTestUppered.first.column1
185
+ assert_equal "Favorite number?", SSTestUppered.last.column1
186
+ assert SSTestUppered.columns_hash["column2"]
127
187
  end
128
-
129
188
  end
130
189
 
131
- describe 'identity inserts' do
132
-
190
+ describe "identity inserts" do
133
191
  before do
134
192
  @identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')"
135
193
  @identity_insert_sql_unquoted = "INSERT INTO funny_jokes (id, name) VALUES(420, 'Knock knock')"
@@ -139,62 +197,60 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
139
197
  @identity_insert_sql_unordered_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Knock knock', @1 = 420"
140
198
  end
141
199
 
142
- it 'return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column' do
143
- assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql)
144
- assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unquoted)
145
- assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unordered)
146
- assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_sp)
147
- assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unquoted_sp)
148
- assert_equal 'funny_jokes', connection.send(:query_requires_identity_insert?,@identity_insert_sql_unordered_sp)
200
+ it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do
201
+ assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql)
202
+ assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted)
203
+ assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered)
204
+ assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_sp)
205
+ assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unquoted_sp)
206
+ assert_equal "funny_jokes", connection.send(:query_requires_identity_insert?, @identity_insert_sql_unordered_sp)
149
207
  end
150
208
 
151
- it 'return false to #query_requires_identity_insert? for normal SQL' do
209
+ it "return false to #query_requires_identity_insert? for normal SQL" do
152
210
  [basic_insert_sql, basic_update_sql, basic_select_sql].each do |sql|
153
- assert !connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}"
211
+ assert !connection.send(:query_requires_identity_insert?, sql), "SQL was #{sql}"
154
212
  end
155
213
  end
156
214
 
157
- it 'find identity column using #identity_columns' do
158
- task_id_column = Task.columns_hash['id']
215
+ it "find identity column using #identity_columns" do
216
+ task_id_column = Task.columns_hash["id"]
159
217
  assert_equal task_id_column.name, connection.send(:identity_columns, Task.table_name).first.name
160
218
  assert_equal task_id_column.sql_type, connection.send(:identity_columns, Task.table_name).first.sql_type
161
219
  end
162
220
 
163
- it 'return an empty array when calling #identity_columns for a table_name with no identity' do
221
+ it "return an empty array when calling #identity_columns for a table_name with no identity" do
164
222
  _(connection.send(:identity_columns, Subscriber.table_name)).must_equal []
165
223
  end
166
-
167
224
  end
168
225
 
169
- describe 'quoting' do
170
-
171
- it 'return 1 for #quoted_true' do
172
- assert_equal '1', connection.quoted_true
226
+ describe "quoting" do
227
+ it "return 1 for #quoted_true" do
228
+ assert_equal "1", connection.quoted_true
173
229
  end
174
230
 
175
- it 'return 0 for #quoted_false' do
176
- assert_equal '0', connection.quoted_false
231
+ it "return 0 for #quoted_false" do
232
+ assert_equal "0", connection.quoted_false
177
233
  end
178
234
 
179
- it 'not escape backslash characters like abstract adapter' do
235
+ it "not escape backslash characters like abstract adapter" do
180
236
  string_with_backslashs = "\\n"
181
237
  assert_equal string_with_backslashs, connection.quote_string(string_with_backslashs)
182
238
  end
183
239
 
184
- it 'quote column names with brackets' do
185
- assert_equal '[foo]', connection.quote_column_name(:foo)
186
- assert_equal '[foo]', connection.quote_column_name('foo')
187
- assert_equal '[foo].[bar]', connection.quote_column_name('foo.bar')
240
+ it "quote column names with brackets" do
241
+ assert_equal "[foo]", connection.quote_column_name(:foo)
242
+ assert_equal "[foo]", connection.quote_column_name("foo")
243
+ assert_equal "[foo].[bar]", connection.quote_column_name("foo.bar")
188
244
  end
189
245
 
190
- it 'not quote already quoted column names with brackets' do
191
- assert_equal '[foo]', connection.quote_column_name('[foo]')
192
- assert_equal '[foo].[bar]', connection.quote_column_name('[foo].[bar]')
246
+ it "not quote already quoted column names with brackets" do
247
+ assert_equal "[foo]", connection.quote_column_name("[foo]")
248
+ assert_equal "[foo].[bar]", connection.quote_column_name("[foo].[bar]")
193
249
  end
194
250
 
195
- it 'quote table names like columns' do
196
- assert_equal '[foo].[bar]', connection.quote_column_name('foo.bar')
197
- assert_equal '[foo].[bar].[baz]', connection.quote_column_name('foo.bar.baz')
251
+ it "quote table names like columns" do
252
+ assert_equal "[foo].[bar]", connection.quote_column_name("foo.bar")
253
+ assert_equal "[foo].[bar].[baz]", connection.quote_column_name("foo.bar.baz")
198
254
  end
199
255
 
200
256
  it "surround string with national prefix" do
@@ -204,44 +260,40 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
204
260
  it "escape all single quotes by repeating them" do
205
261
  assert_equal "N'''quotation''s'''", connection.quote("'quotation's'")
206
262
  end
207
-
208
263
  end
209
264
 
210
- describe 'disabling referential integrity' do
211
-
265
+ describe "disabling referential integrity" do
212
266
  before do
213
267
  connection.disable_referential_integrity { SSTestHasPk.delete_all; SSTestHasFk.delete_all }
214
268
  @parent = SSTestHasPk.create!
215
269
  @member = SSTestHasFk.create!(fk_id: @parent.id)
216
270
  end
217
271
 
218
- it 'NOT ALLOW by default the deletion of a referenced parent' do
219
- SSTestHasPk.connection.disable_referential_integrity { }
272
+ it "NOT ALLOW by default the deletion of a referenced parent" do
273
+ SSTestHasPk.connection.disable_referential_integrity {}
220
274
  assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy }
221
275
  end
222
276
 
223
- it 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do
277
+ it "ALLOW deletion of referenced parent using #disable_referential_integrity block" do
224
278
  SSTestHasPk.connection.disable_referential_integrity { @parent.destroy }
225
279
  end
226
280
 
227
- it 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do
281
+ it "again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block" do
228
282
  assert_raise(ActiveRecord::StatementInvalid) do
229
- SSTestHasPk.connection.disable_referential_integrity { }
283
+ SSTestHasPk.connection.disable_referential_integrity {}
230
284
  @parent.destroy
231
285
  end
232
286
  end
233
287
 
234
- it 'not disable referential integrity for the same table twice' do
288
+ it "not disable referential integrity for the same table twice" do
235
289
  tables = SSTestHasPk.connection.tables_with_referential_integrity
236
290
  assert_equal tables.size, tables.uniq.size
237
291
  end
238
-
239
292
  end
240
293
 
241
- describe 'database statements' do
242
-
294
+ describe "database statements" do
243
295
  it "run the database consistency checker useroptions command" do
244
- skip 'on azure' if connection_sqlserver_azure?
296
+ skip "on azure" if connection_sqlserver_azure?
245
297
  keys = [:textsize, :language, :isolation_level, :dateformat]
246
298
  user_options = connection.user_options
247
299
  keys.each do |key|
@@ -251,183 +303,216 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
251
303
  end
252
304
 
253
305
  it "return a underscored key hash with indifferent access of the results" do
254
- skip 'on azure' if connection_sqlserver_azure?
306
+ skip "on azure" if connection_sqlserver_azure?
255
307
  user_options = connection.user_options
256
- assert_equal 'read committed', user_options['isolation_level']
257
- assert_equal 'read committed', user_options[:isolation_level]
308
+ assert_equal "read committed", user_options["isolation_level"]
309
+ assert_equal "read committed", user_options[:isolation_level]
258
310
  end
259
-
260
311
  end
261
312
 
262
- describe 'schema statements' do
263
-
264
- it 'create integers when no limit supplied' do
265
- assert_equal 'integer', connection.type_to_sql(:integer)
313
+ describe "schema statements" do
314
+ it "create integers when no limit supplied" do
315
+ assert_equal "integer", connection.type_to_sql(:integer)
266
316
  end
267
317
 
268
- it 'create integers when limit is 4' do
269
- assert_equal 'integer', connection.type_to_sql(:integer, limit: 4)
318
+ it "create integers when limit is 4" do
319
+ assert_equal "integer", connection.type_to_sql(:integer, limit: 4)
270
320
  end
271
321
 
272
- it 'create integers when limit is 3' do
273
- assert_equal 'integer', connection.type_to_sql(:integer, limit: 3)
322
+ it "create integers when limit is 3" do
323
+ assert_equal "integer", connection.type_to_sql(:integer, limit: 3)
274
324
  end
275
325
 
276
- it 'create smallints when limit is 2' do
277
- assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2)
326
+ it "create smallints when limit is 2" do
327
+ assert_equal "smallint", connection.type_to_sql(:integer, limit: 2)
278
328
  end
279
329
 
280
- it 'create tinyints when limit is 1' do
281
- assert_equal 'tinyint', connection.type_to_sql(:integer, limit: 1)
330
+ it "create tinyints when limit is 1" do
331
+ assert_equal "tinyint", connection.type_to_sql(:integer, limit: 1)
282
332
  end
283
333
 
284
- it 'create bigints when limit is greateer than 4' do
285
- assert_equal 'bigint', connection.type_to_sql(:integer, limit: 5)
286
- assert_equal 'bigint', connection.type_to_sql(:integer, limit: 6)
287
- assert_equal 'bigint', connection.type_to_sql(:integer, limit: 7)
288
- assert_equal 'bigint', connection.type_to_sql(:integer, limit: 8)
334
+ it "create bigints when limit is greateer than 4" do
335
+ assert_equal "bigint", connection.type_to_sql(:integer, limit: 5)
336
+ assert_equal "bigint", connection.type_to_sql(:integer, limit: 6)
337
+ assert_equal "bigint", connection.type_to_sql(:integer, limit: 7)
338
+ assert_equal "bigint", connection.type_to_sql(:integer, limit: 8)
289
339
  end
290
340
 
291
- it 'create floats when no limit supplied' do
292
- assert_equal 'float', connection.type_to_sql(:float)
341
+ it "create floats when no limit supplied" do
342
+ assert_equal "float", connection.type_to_sql(:float)
293
343
  end
294
-
295
344
  end
296
345
 
297
- describe 'views' do
298
-
346
+ describe "views" do
299
347
  # Using connection.views
300
348
 
301
- it 'return an array' do
349
+ it "return an array" do
302
350
  assert_instance_of Array, connection.views
303
351
  end
304
352
 
305
- it 'find SSTestCustomersView table name' do
306
- _(connection.views).must_include 'sst_customers_view'
353
+ it "find SSTestCustomersView table name" do
354
+ _(connection.views).must_include "sst_customers_view"
307
355
  end
308
356
 
309
- it 'work with dynamic finders' do
310
- name = 'MetaSkills'
357
+ it "work with dynamic finders" do
358
+ name = "MetaSkills"
311
359
  customer = SSTestCustomersView.create! name: name
312
360
  assert_equal customer, SSTestCustomersView.find_by_name(name)
313
361
  end
314
362
 
315
- it 'not contain system views' do
316
- systables = ['sysconstraints','syssegments']
363
+ it "not contain system views" do
364
+ systables = ["sysconstraints", "syssegments"]
317
365
  systables.each do |systable|
318
366
  assert !connection.views.include?(systable), "This systable #{systable} should not be in the views array."
319
367
  end
320
368
  end
321
369
 
322
- it 'allow the connection#view_information method to return meta data on the view' do
323
- view_info = connection.send(:view_information,'sst_customers_view')
324
- assert_equal('sst_customers_view', view_info['TABLE_NAME'])
325
- assert_match(/CREATE VIEW sst_customers_view/, view_info['VIEW_DEFINITION'])
370
+ it "allow the connection#view_information method to return meta data on the view" do
371
+ view_info = connection.send(:view_information, "sst_customers_view")
372
+ assert_equal("sst_customers_view", view_info["TABLE_NAME"])
373
+ assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"])
326
374
  end
327
375
 
328
- it 'allow the connection#view_table_name method to return true table_name for the view' do
329
- assert_equal 'customers', connection.send(:view_table_name,'sst_customers_view')
330
- assert_equal 'topics', connection.send(:view_table_name,'topics'), 'No view here, the same table name should come back.'
376
+ it "allow the connection#view_table_name method to return true table_name for the view" do
377
+ assert_equal "customers", connection.send(:view_table_name, "sst_customers_view")
378
+ assert_equal "topics", connection.send(:view_table_name, "topics"), "No view here, the same table name should come back."
331
379
  end
332
380
 
333
381
  # With same column names
334
382
 
335
- it 'have matching column objects' do
336
- columns = ['id','name','balance']
383
+ it "have matching column objects" do
384
+ columns = ["id", "name", "balance"]
337
385
  assert !SSTestCustomersView.columns.blank?
338
386
  assert_equal columns.size, SSTestCustomersView.columns.size
339
387
  columns.each do |colname|
340
- assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
341
- SSTestCustomersView.columns_hash[colname],
342
- "Column name #{colname.inspect} was not found in these columns #{SSTestCustomersView.columns.map(&:name).inspect}"
388
+ assert_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Column,
389
+ SSTestCustomersView.columns_hash[colname],
390
+ "Column name #{colname.inspect} was not found in these columns #{SSTestCustomersView.columns.map(&:name).inspect}"
343
391
  end
344
392
  end
345
393
 
346
- it 'find identity column' do
347
- _(SSTestCustomersView.primary_key).must_equal 'id'
348
- _(connection.primary_key(SSTestCustomersView.table_name)).must_equal 'id'
349
- _(SSTestCustomersView.columns_hash['id']).must_be :is_identity?
394
+ it "find identity column" do
395
+ _(SSTestCustomersView.primary_key).must_equal "id"
396
+ _(connection.primary_key(SSTestCustomersView.table_name)).must_equal "id"
397
+ _(SSTestCustomersView.columns_hash["id"]).must_be :is_identity?
350
398
  end
351
399
 
352
- it 'find default values' do
400
+ it "find default values" do
353
401
  assert_equal 0, SSTestCustomersView.new.balance
354
402
  end
355
403
 
356
- it 'respond true to data_source_exists?' do
404
+ it "respond true to data_source_exists?" do
357
405
  assert SSTestCustomersView.connection.data_source_exists?(SSTestCustomersView.table_name)
358
406
  end
359
407
 
360
408
  # With aliased column names
361
409
 
362
- it 'have matching column objects' do
363
- columns = ['id','pretend_null']
410
+ it "have matching column objects" do
411
+ columns = ["id", "pretend_null"]
364
412
  assert !SSTestStringDefaultsView.columns.blank?
365
413
  assert_equal columns.size, SSTestStringDefaultsView.columns.size
366
414
  columns.each do |colname|
367
- assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn,
368
- SSTestStringDefaultsView.columns_hash[colname],
369
- "Column name #{colname.inspect} was not found in these columns #{SSTestStringDefaultsView.columns.map(&:name).inspect}"
415
+ assert_instance_of ActiveRecord::ConnectionAdapters::SQLServer::Column,
416
+ SSTestStringDefaultsView.columns_hash[colname],
417
+ "Column name #{colname.inspect} was not found in these columns #{SSTestStringDefaultsView.columns.map(&:name).inspect}"
370
418
  end
371
419
  end
372
420
 
373
- it 'find identity column' do
374
- _(SSTestStringDefaultsView.primary_key).must_equal 'id'
375
- _(connection.primary_key(SSTestStringDefaultsView.table_name)).must_equal 'id'
376
- _(SSTestStringDefaultsView.columns_hash['id']).must_be :is_identity?
421
+ it "find identity column" do
422
+ _(SSTestStringDefaultsView.primary_key).must_equal "id"
423
+ _(connection.primary_key(SSTestStringDefaultsView.table_name)).must_equal "id"
424
+ _(SSTestStringDefaultsView.columns_hash["id"]).must_be :is_identity?
377
425
  end
378
426
 
379
- it 'find default values' do
380
- assert_equal 'null', SSTestStringDefaultsView.new.pretend_null,
381
- SSTestStringDefaultsView.columns_hash['pretend_null'].inspect
427
+ it "find default values" do
428
+ assert_equal "null", SSTestStringDefaultsView.new.pretend_null,
429
+ SSTestStringDefaultsView.columns_hash["pretend_null"].inspect
382
430
  end
383
431
 
384
- it 'respond true to data_source_exists?' do
432
+ it "respond true to data_source_exists?" do
385
433
  assert SSTestStringDefaultsView.connection.data_source_exists?(SSTestStringDefaultsView.table_name)
386
434
  end
387
435
 
388
436
  # That have more than 4000 chars for their defintion
389
437
 
390
- it 'cope with null returned for the defintion' do
438
+ it "cope with null returned for the defintion" do
391
439
  assert_nothing_raised() { SSTestStringDefaultsBigView.columns }
392
440
  end
393
441
 
394
- it 'using alternate view defintion still be able to find real default' do
395
- assert_equal 'null', SSTestStringDefaultsBigView.new.pretend_null,
396
- SSTestStringDefaultsBigView.columns_hash['pretend_null'].inspect
442
+ it "using alternate view defintion still be able to find real default" do
443
+ assert_equal "null", SSTestStringDefaultsBigView.new.pretend_null,
444
+ SSTestStringDefaultsBigView.columns_hash["pretend_null"].inspect
397
445
  end
398
-
399
446
  end
400
447
 
401
- describe 'database_prefix_remote_server?' do
402
-
448
+ describe "database_prefix_remote_server?" do
403
449
  after do
404
450
  connection_options.delete(:database_prefix)
405
451
  end
406
452
 
407
- it 'returns false if database_prefix is not configured' do
453
+ it "returns false if database_prefix is not configured" do
408
454
  assert_equal false, connection.database_prefix_remote_server?
409
455
  end
410
456
 
411
- it 'returns true if database_prefix has been set' do
457
+ it "returns true if database_prefix has been set" do
412
458
  connection_options[:database_prefix] = "server.database.schema."
413
459
  assert_equal true, connection.database_prefix_remote_server?
414
460
  end
415
461
 
416
- it 'returns false if database_prefix has been set incorrectly' do
462
+ it "returns false if database_prefix has been set incorrectly" do
417
463
  connection_options[:database_prefix] = "server.database.schema"
418
464
  assert_equal false, connection.database_prefix_remote_server?
419
465
  end
420
-
421
466
  end
422
467
 
423
- it 'in_memory_oltp' do
424
- if ENV['IN_MEMORY_OLTP'] && connection.supports_in_memory_oltp?
425
- _(SSTMemory.primary_key).must_equal 'id'
426
- _(SSTMemory.columns_hash['id']).must_be :is_identity?
468
+ it "in_memory_oltp" do
469
+ if ENV["IN_MEMORY_OLTP"] && connection.supports_in_memory_oltp?
470
+ _(SSTMemory.primary_key).must_equal "id"
471
+ _(SSTMemory.columns_hash["id"]).must_be :is_identity?
427
472
  else
428
- skip 'supports_in_memory_oltp? => false'
473
+ skip "supports_in_memory_oltp? => false"
429
474
  end
430
475
  end
431
476
 
432
- end
477
+ describe "block writes to a database" do
478
+ def setup
479
+ @conn = ActiveRecord::Base.connection
480
+ end
481
+
482
+ def test_errors_when_an_insert_query_is_called_while_preventing_writes
483
+ assert_raises(ActiveRecord::ReadOnlyError) do
484
+ ActiveRecord::Base.while_preventing_writes do
485
+ @conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
486
+ end
487
+ end
488
+ end
489
+
490
+ def test_errors_when_an_update_query_is_called_while_preventing_writes
491
+ @conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
492
+
493
+ assert_raises(ActiveRecord::ReadOnlyError) do
494
+ ActiveRecord::Base.while_preventing_writes do
495
+ @conn.update("UPDATE [subscribers] SET [subscribers].[name] = 'Aidan' WHERE [subscribers].[nick] = 'aido'")
496
+ end
497
+ end
498
+ end
499
+
500
+ def test_errors_when_a_delete_query_is_called_while_preventing_writes
501
+ @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
502
+
503
+ assert_raises(ActiveRecord::ReadOnlyError) do
504
+ ActiveRecord::Base.while_preventing_writes do
505
+ @conn.execute("DELETE FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
506
+ end
507
+ end
508
+ end
433
509
 
510
+ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
511
+ @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
512
+
513
+ ActiveRecord::Base.while_preventing_writes do
514
+ assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
515
+ end
516
+ end
517
+ end
518
+ end