activerecord-sqlserver-adapter_new 4.2.15

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 (132) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/CHANGELOG.md +212 -0
  4. data/CODE_OF_CONDUCT.md +31 -0
  5. data/Gemfile +61 -0
  6. data/Guardfile +29 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +201 -0
  9. data/RUNNING_UNIT_TESTS.md +121 -0
  10. data/Rakefile +48 -0
  11. data/VERSION +1 -0
  12. data/activerecord-sqlserver-adapter_new.gemspec +20 -0
  13. data/appveyor.yml +39 -0
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +27 -0
  15. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
  16. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +40 -0
  17. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +34 -0
  19. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
  20. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +386 -0
  21. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +68 -0
  22. data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
  23. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +114 -0
  25. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +52 -0
  26. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +473 -0
  27. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +66 -0
  28. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +66 -0
  29. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +22 -0
  30. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +76 -0
  31. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +57 -0
  32. data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
  33. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
  34. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
  35. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +12 -0
  36. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +38 -0
  37. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +21 -0
  38. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +41 -0
  39. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
  40. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +31 -0
  41. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +12 -0
  42. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +15 -0
  43. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +12 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +15 -0
  46. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
  48. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +22 -0
  49. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
  50. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
  51. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +40 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +76 -0
  53. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
  54. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
  55. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -0
  56. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
  57. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +15 -0
  58. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
  59. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
  60. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
  61. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
  62. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
  63. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
  64. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
  65. data/lib/active_record/connection_adapters/sqlserver/utils.rb +136 -0
  66. data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
  67. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +405 -0
  68. data/lib/active_record/connection_adapters/sqlserver_column.rb +53 -0
  69. data/lib/active_record/sqlserver_base.rb +20 -0
  70. data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
  71. data/lib/activerecord-sqlserver-adapter.rb +1 -0
  72. data/lib/arel/visitors/sqlserver.rb +214 -0
  73. data/lib/arel_sqlserver.rb +3 -0
  74. data/test/appveyor/dbsetup.ps1 +27 -0
  75. data/test/appveyor/dbsetup.sql +11 -0
  76. data/test/cases/adapter_test_sqlserver.rb +444 -0
  77. data/test/cases/coerced_tests.rb +713 -0
  78. data/test/cases/column_test_sqlserver.rb +780 -0
  79. data/test/cases/connection_test_sqlserver.rb +142 -0
  80. data/test/cases/execute_procedure_test_sqlserver.rb +44 -0
  81. data/test/cases/fetch_test_sqlserver.rb +57 -0
  82. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
  83. data/test/cases/helper_sqlserver.rb +54 -0
  84. data/test/cases/migration_test_sqlserver.rb +61 -0
  85. data/test/cases/order_test_sqlserver.rb +147 -0
  86. data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
  87. data/test/cases/rake_test_sqlserver.rb +163 -0
  88. data/test/cases/schema_dumper_test_sqlserver.rb +198 -0
  89. data/test/cases/schema_test_sqlserver.rb +54 -0
  90. data/test/cases/scratchpad_test_sqlserver.rb +9 -0
  91. data/test/cases/showplan_test_sqlserver.rb +65 -0
  92. data/test/cases/specific_schema_test_sqlserver.rb +167 -0
  93. data/test/cases/transaction_test_sqlserver.rb +66 -0
  94. data/test/cases/utils_test_sqlserver.rb +129 -0
  95. data/test/cases/uuid_test_sqlserver.rb +48 -0
  96. data/test/config.yml +41 -0
  97. data/test/debug.rb +14 -0
  98. data/test/fixtures/1px.gif +0 -0
  99. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  100. data/test/models/sqlserver/booking.rb +3 -0
  101. data/test/models/sqlserver/customers_view.rb +3 -0
  102. data/test/models/sqlserver/datatype.rb +3 -0
  103. data/test/models/sqlserver/datatype_migration.rb +3 -0
  104. data/test/models/sqlserver/dollar_table_name.rb +3 -0
  105. data/test/models/sqlserver/dot_table_name.rb +3 -0
  106. data/test/models/sqlserver/edge_schema.rb +13 -0
  107. data/test/models/sqlserver/fk_has_fk.rb +3 -0
  108. data/test/models/sqlserver/fk_has_pk.rb +3 -0
  109. data/test/models/sqlserver/natural_pk_data.rb +4 -0
  110. data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
  111. data/test/models/sqlserver/no_pk_data.rb +3 -0
  112. data/test/models/sqlserver/object_default.rb +3 -0
  113. data/test/models/sqlserver/quoted_table.rb +7 -0
  114. data/test/models/sqlserver/quoted_view_1.rb +3 -0
  115. data/test/models/sqlserver/quoted_view_2.rb +3 -0
  116. data/test/models/sqlserver/string_default.rb +3 -0
  117. data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
  118. data/test/models/sqlserver/string_defaults_view.rb +3 -0
  119. data/test/models/sqlserver/tinyint_pk.rb +3 -0
  120. data/test/models/sqlserver/upper.rb +3 -0
  121. data/test/models/sqlserver/uppered.rb +3 -0
  122. data/test/models/sqlserver/uuid.rb +3 -0
  123. data/test/schema/datatypes/2012.sql +55 -0
  124. data/test/schema/sqlserver_specific_schema.rb +207 -0
  125. data/test/support/coerceable_test_sqlserver.rb +45 -0
  126. data/test/support/connection_reflection.rb +37 -0
  127. data/test/support/load_schema_sqlserver.rb +29 -0
  128. data/test/support/minitest_sqlserver.rb +1 -0
  129. data/test/support/paths_sqlserver.rb +50 -0
  130. data/test/support/rake_helpers.rb +41 -0
  131. data/test/support/sql_counter_sqlserver.rb +32 -0
  132. metadata +253 -0
@@ -0,0 +1,54 @@
1
+ require 'cases/helper_sqlserver'
2
+
3
+ class SchemaTestSQLServer < ActiveRecord::TestCase
4
+
5
+ describe 'When table is dbo schema' do
6
+
7
+ it 'find primary key for tables with odd schema' do
8
+ connection.primary_key('sst_natural_pk_data').must_equal 'legacy_id'
9
+ end
10
+
11
+ end
12
+
13
+ describe 'When table is in non-dbo schema' do
14
+
15
+ it 'work with table exists' do
16
+ assert connection.table_exists?('test.sst_schema_natural_id')
17
+ assert connection.table_exists?('[test].[sst_schema_natural_id]')
18
+ end
19
+
20
+ it 'find primary key for tables with odd schema' do
21
+ connection.primary_key('test.sst_schema_natural_id').must_equal 'legacy_id'
22
+ end
23
+
24
+ it "have only one identity column" do
25
+ columns = connection.columns("test.sst_schema_identity")
26
+ assert_equal 2, columns.size
27
+ assert_equal 1, columns.select{ |c| c.is_identity? }.size
28
+ end
29
+
30
+ it "read only column properties for table in specific schema" do
31
+ test_columns = connection.columns("test.sst_schema_columns")
32
+ dbo_columns = connection.columns("dbo.sst_schema_columns")
33
+ columns = connection.columns("sst_schema_columns") # This returns table from dbo schema
34
+ assert_equal 7, test_columns.size
35
+ assert_equal 2, dbo_columns.size
36
+ assert_equal 2, columns.size
37
+ assert_equal 1, test_columns.select{ |c| c.is_identity? }.size
38
+ assert_equal 1, dbo_columns.select{ |c| c.is_identity? }.size
39
+ assert_equal 1, columns.select{ |c| c.is_identity? }.size
40
+ end
41
+
42
+ it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do
43
+ columns = connection.columns("test.sst_schema_columns")
44
+ assert_equal 255, columns.find {|c| c.name == 'name'}.limit
45
+ assert_equal 1000, columns.find {|c| c.name == 'description'}.limit
46
+ assert_equal 255, columns.find {|c| c.name == 'n_name'}.limit
47
+ assert_equal 1000, columns.find {|c| c.name == 'n_description'}.limit
48
+ end
49
+
50
+ end
51
+
52
+
53
+ end
54
+
@@ -0,0 +1,9 @@
1
+ require 'cases/helper_sqlserver'
2
+
3
+ class ScratchpadTestSQLServer < ActiveRecord::TestCase
4
+
5
+ it 'helps debug things' do
6
+ #
7
+ end
8
+
9
+ end
@@ -0,0 +1,65 @@
1
+ require 'cases/helper_sqlserver'
2
+ require 'models/car'
3
+
4
+ class ShowplanTestSQLServer < ActiveRecord::TestCase
5
+
6
+ fixtures :cars
7
+
8
+ describe 'Unprepare previously prepared SQL' do
9
+
10
+ it 'from simple statement' do
11
+ plan = Car.where(id: 1).explain
12
+ plan.must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1"
13
+ plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql'
14
+ end
15
+
16
+ it 'from multiline statement' do
17
+ plan = Car.where("\n id = 1 \n").explain
18
+ plan.must_include "SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)"
19
+ plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql'
20
+ end
21
+
22
+ it 'from prepared statement ...' do
23
+ plan = Car.where(name: ',').limit(1).explain
24
+ plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]"
25
+ plan.must_include "TOP EXPRESSION", 'make sure we do not showplan the sp_executesql'
26
+ plan.must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql'
27
+ end
28
+
29
+ end
30
+
31
+ describe 'With SHOWPLAN_TEXT option' do
32
+
33
+ it 'use simple table printer' do
34
+ with_showplan_option('SHOWPLAN_TEXT') do
35
+ plan = Car.where(id: 1).explain
36
+ plan.must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]"
37
+ plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql'
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ describe 'With SHOWPLAN_XML option' do
44
+
45
+ it 'show formatted xml' do
46
+ with_showplan_option('SHOWPLAN_XML') do
47
+ plan = Car.where(id: 1).explain
48
+ plan.must_include 'ShowPlanXML'
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+
55
+ private
56
+
57
+ def with_showplan_option(option)
58
+ old_option = ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option
59
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = option
60
+ yield
61
+ ensure
62
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = old_option
63
+ end
64
+
65
+ end
@@ -0,0 +1,167 @@
1
+ require 'cases/helper_sqlserver'
2
+
3
+ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase
4
+
5
+ after { SSTestEdgeSchema.delete_all }
6
+
7
+ it 'handle dollar symbols' do
8
+ SSTestDollarTableName.create!
9
+ SSTestDollarTableName.limit(20).offset(1)
10
+ end
11
+
12
+ it 'handle dot table names' do
13
+ SSTestDotTableName.create! name: 'test'
14
+ SSTestDotTableName.limit(20).offset(1)
15
+ SSTestDotTableName.where(name: 'test').first.must_be :present?
16
+ end
17
+
18
+ it 'models can use tinyint pk tables' do
19
+ obj = SSTestTinyintPk.create! name: '1'
20
+ obj.id.is_a? Fixnum
21
+ SSTestTinyintPk.find(obj.id).must_equal obj
22
+ end
23
+
24
+ it 'be able to complex count tables with no primary key' do
25
+ SSTestNoPkData.delete_all
26
+ 10.times { |n| SSTestNoPkData.create! name: "Test#{n}" }
27
+ assert_equal 1, SSTestNoPkData.where(name: 'Test5').count
28
+ end
29
+
30
+ it 'quote table names properly even when they are views' do
31
+ obj = SSTestQuotedTable.create!
32
+ assert_nothing_raised { assert SSTestQuotedTable.first }
33
+ obj = SSTestQuotedTableUser.create!
34
+ assert_nothing_raised { assert SSTestQuotedTableUser.first }
35
+ obj = SSTestQuotedView1.create!
36
+ assert_nothing_raised { assert SSTestQuotedView1.first }
37
+ obj = SSTestQuotedView2.create!
38
+ assert_nothing_raised { assert SSTestQuotedView2.first }
39
+ end
40
+
41
+ it 'cope with multi line defaults' do
42
+ default = SSTestStringDefault.new
43
+ assert_equal "Some long default with a\nnew line.", default.string_with_multiline_default
44
+ end
45
+
46
+ it 'default strings before save' do
47
+ default = SSTestStringDefault.new
48
+ assert_equal nil, default.string_with_null_default
49
+ assert_equal 'null', default.string_with_pretend_null_one
50
+ assert_equal '(null)', default.string_with_pretend_null_two
51
+ assert_equal 'NULL', default.string_with_pretend_null_three
52
+ assert_equal '(NULL)', default.string_with_pretend_null_four
53
+ assert_equal '(3)', default.string_with_pretend_paren_three
54
+ end
55
+
56
+ it 'default strings after save' do
57
+ default = SSTestStringDefault.create
58
+ assert_equal nil, default.string_with_null_default
59
+ assert_equal 'null', default.string_with_pretend_null_one
60
+ assert_equal '(null)', default.string_with_pretend_null_two
61
+ assert_equal 'NULL', default.string_with_pretend_null_three
62
+ assert_equal '(NULL)', default.string_with_pretend_null_four
63
+ end
64
+
65
+ it 'default objects work' do
66
+ obj = SSTestObjectDefault.create! name: 'MetaSkills'
67
+ obj.date.must_be_nil 'since this is set on insert'
68
+ obj.reload.date.must_be_instance_of Date
69
+ end
70
+
71
+ it 'allows datetime2 as timestamps' do
72
+ SSTestBooking.columns_hash['created_at'].sql_type.must_equal 'datetime2(7)'
73
+ SSTestBooking.columns_hash['updated_at'].sql_type.must_equal 'datetime2(7)'
74
+ obj1 = SSTestBooking.new name: 'test1'
75
+ obj1.save!
76
+ obj1.created_at.must_be_instance_of Time
77
+ obj1.updated_at.must_be_instance_of Time
78
+ end
79
+
80
+ # Natural primary keys.
81
+
82
+ it 'work with identity inserts' do
83
+ record = SSTestNaturalPkData.new name: 'Test', description: 'Natural identity inserts.'
84
+ record.id = '12345ABCDE'
85
+ assert record.save
86
+ assert_equal '12345ABCDE', record.reload.id
87
+ end
88
+
89
+ it 'work with identity inserts when the key is an int' do
90
+ record = SSTestNaturalPkIntData.new name: 'Test', description: 'Natural identity inserts.'
91
+ record.id = 12
92
+ assert record.save
93
+ assert_equal 12, record.reload.id
94
+ end
95
+
96
+ it 'use primary key for row table order in pagination sql' do
97
+ sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY/
98
+ assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load }
99
+ end
100
+
101
+ # Special quoted column
102
+
103
+ it 'work as normal' do
104
+ SSTestEdgeSchema.delete_all
105
+ r = SSTestEdgeSchema.create! 'crazy]]quote' => 'crazyqoute'
106
+ assert SSTestEdgeSchema.columns_hash['crazy]]quote']
107
+ assert_equal r, SSTestEdgeSchema.where('crazy]]quote' => 'crazyqoute').first
108
+ end
109
+
110
+ it 'various methods to bypass national quoted columns for any column, but primarily useful for char/varchar' do
111
+ value = Class.new do
112
+ def quoted_id
113
+ "'T'"
114
+ end
115
+ end
116
+ # Using ActiveRecord's quoted_id feature for objects.
117
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first }
118
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first }
119
+ # Using our custom char type data.
120
+ data = ActiveRecord::Type::SQLServer::Char::Data
121
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new('T')).first }
122
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new('T')).first }
123
+ # Taking care of everything.
124
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: 'T').first }
125
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: 'T').first }
126
+ end
127
+
128
+ it 'can update and hence properly quoted non-national char/varchar columns' do
129
+ o = SSTestDatatypeMigration.create!
130
+ o.varchar_col = "O'Reilly"
131
+ o.save!
132
+ o.reload.varchar_col.must_equal "O'Reilly"
133
+ o.varchar_col = nil
134
+ o.save!
135
+ o.reload.varchar_col.must_be_nil
136
+ end
137
+
138
+ # With column names that have spaces
139
+
140
+ it 'create record using a custom attribute reader and be able to load it back in' do
141
+ value = 'Saved value into a column that has a space in the name.'
142
+ record = SSTestEdgeSchema.create! with_spaces: value
143
+ assert_equal value, SSTestEdgeSchema.find(record.id).with_spaces
144
+ end
145
+
146
+ # With description column
147
+
148
+ it 'allow all sorts of ordering without adapter munging it up with special description column' do
149
+ SSTestEdgeSchema.create! description: 'A'
150
+ SSTestEdgeSchema.create! description: 'B'
151
+ SSTestEdgeSchema.create! description: 'C'
152
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description').map(&:description)
153
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description asc').map(&:description)
154
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description ASC').map(&:description)
155
+ assert_equal ['C','B','A'], SSTestEdgeSchema.order('description desc').map(&:description)
156
+ assert_equal ['C','B','A'], SSTestEdgeSchema.order('description DESC').map(&:description)
157
+ end
158
+
159
+ # For uniqueidentifier model helpers
160
+
161
+ it 'returns a new id via connection newid_function' do
162
+ acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID
163
+ db_uuid = ActiveRecord::Base.connection.newid_function
164
+ db_uuid.must_match(acceptable_uuid)
165
+ end
166
+
167
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: UTF-8
2
+ require 'cases/helper_sqlserver'
3
+ require 'models/ship'
4
+ require 'models/developer'
5
+
6
+ class TransactionTestSQLServer < ActiveRecord::TestCase
7
+
8
+ self.use_transactional_fixtures = false
9
+
10
+ before { delete_ships }
11
+
12
+ it 'allow ActiveRecord::Rollback to work in 1 transaction block' do
13
+ Ship.transaction do
14
+ Ship.create! name: 'Black Pearl'
15
+ raise ActiveRecord::Rollback
16
+ end
17
+ assert_no_ships
18
+ end
19
+
20
+ it 'allow nested transactions to totally rollback' do
21
+ begin
22
+ Ship.transaction do
23
+ Ship.create! name: 'Black Pearl'
24
+ Ship.transaction do
25
+ Ship.create! name: 'Flying Dutchman'
26
+ raise 'HELL'
27
+ end
28
+ end
29
+ rescue Exception => e
30
+ assert_no_ships
31
+ end
32
+ end
33
+
34
+ it 'can use an isolation level and reverts back to starting isolation level' do
35
+ in_level = nil
36
+ begin_level = connection.user_options_isolation_level
37
+ begin_level.must_match %r{read committed}i
38
+ Ship.transaction(isolation: :serializable) do
39
+ Ship.create! name: 'Black Pearl'
40
+ in_level = connection.user_options_isolation_level
41
+ end
42
+ after_level = connection.user_options_isolation_level
43
+ in_level.must_match %r{serializable}i
44
+ after_level.must_match %r{read committed}i
45
+ end
46
+
47
+ it 'can use an isolation level and reverts back to starting isolation level under exceptions' do
48
+ connection.user_options_isolation_level.must_match %r{read committed}i
49
+ lambda {
50
+ Ship.transaction(isolation: :serializable) { Ship.create! }
51
+ }.must_raise(ActiveRecord::RecordInvalid)
52
+ connection.user_options_isolation_level.must_match %r{read committed}i
53
+ end
54
+
55
+
56
+ protected
57
+
58
+ def delete_ships
59
+ Ship.delete_all
60
+ end
61
+
62
+ def assert_no_ships
63
+ assert Ship.count.zero?, "Expected Ship to have no models but it did have:\n#{Ship.all.inspect}"
64
+ end
65
+
66
+ end
@@ -0,0 +1,129 @@
1
+ require 'cases/helper_sqlserver'
2
+
3
+ class UtilsTestSQLServer < ActiveRecord::TestCase
4
+
5
+ it '.quote_string' do
6
+ SQLServer::Utils.quote_string("I'll store this in C:\\Users").must_equal "I''ll store this in C:\\Users"
7
+ end
8
+
9
+ it '.unquote_string' do
10
+ SQLServer::Utils.unquote_string("I''ll store this in C:\\Users").must_equal "I'll store this in C:\\Users"
11
+ end
12
+
13
+ it '.quoted_raw' do
14
+ SQLServer::Utils.quoted_raw("some.Name").must_equal "[some.Name]"
15
+ end
16
+
17
+ describe '.extract_identifiers constructor and thus SQLServer::Utils::Name value object' do
18
+
19
+ let(:valid_names) { valid_names_unquoted + valid_names_quoted }
20
+
21
+ let(:valid_names_unquoted) {[
22
+ 'server.database.schema.object',
23
+ 'server.database..object',
24
+ 'server..schema.object',
25
+ 'server...object',
26
+ 'database.schema.object',
27
+ 'database..object',
28
+ 'schema.object',
29
+ 'object'
30
+ ]}
31
+
32
+ let(:valid_names_quoted) {[
33
+ '[server].[database].[schema].[object]',
34
+ '[server].[database]..[object]',
35
+ '[server]..[schema].[object]',
36
+ '[server]...[object]',
37
+ '[database].[schema].[object]',
38
+ '[database]..[object]',
39
+ '[schema].[object]',
40
+ '[object]'
41
+ ]}
42
+
43
+ let(:server_names) { valid_names.partition { |name| name =~ /server/ } }
44
+ let(:database_names) { valid_names.partition { |name| name =~ /database/ } }
45
+ let(:schema_names) { valid_names.partition { |name| name =~ /schema/ } }
46
+
47
+ it 'extracts and returns #object identifier unquoted by default or quoted as needed' do
48
+ valid_names.each do |n|
49
+ name = extract_identifiers(n)
50
+ name.object.must_equal 'object', "With #{n.inspect} for #object"
51
+ name.object_quoted.must_equal '[object]', "With #{n.inspect} for #object_quoted"
52
+ end
53
+ end
54
+
55
+ [:schema, :database, :server].each do |part|
56
+
57
+ it "extracts and returns #{part} identifier unquoted by default or quoted as needed" do
58
+ present, blank = send(:"#{part}_names")
59
+ present.each do |n|
60
+ name = extract_identifiers(n)
61
+ name.send(:"#{part}").must_equal "#{part}", "With #{n.inspect} for ##{part} method"
62
+ name.send(:"#{part}_quoted").must_equal "[#{part}]", "With #{n.inspect} for ##{part}_quoted method"
63
+ end
64
+ blank.each do |n|
65
+ name = extract_identifiers(n)
66
+ name.send(:"#{part}").must_be_nil "With #{n.inspect} for ##{part} method"
67
+ name.send(:"#{part}_quoted").must_be_nil "With #{n.inspect} for ##{part}_quoted method"
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ it 'does not blow up on nil or blank string name' do
74
+ extract_identifiers(nil).object.must_be_nil
75
+ extract_identifiers(' ').object.must_be_nil
76
+ end
77
+
78
+ it 'has a #quoted that returns a fully quoted name with all identifiers as orginially passed in' do
79
+ extract_identifiers('object').quoted.must_equal '[object]'
80
+ extract_identifiers('server.database..object').quoted.must_equal '[server].[database]..[object]'
81
+ extract_identifiers('[server]...[object]').quoted.must_equal '[server]...[object]'
82
+ end
83
+
84
+ it 'can take a symbol argument' do
85
+ extract_identifiers(:object).object.must_equal 'object'
86
+ end
87
+
88
+ it 'allows identifiers with periods to work' do
89
+ extract_identifiers('[obj.name]').quoted.must_equal '[obj.name]'
90
+ extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]'
91
+ end
92
+
93
+ it 'should indicate if a name is fully qualitified' do
94
+ extract_identifiers('object').fully_qualified?.must_equal false
95
+ extract_identifiers('schema.object').fully_qualified?.must_equal false
96
+ extract_identifiers('database.schema.object').fully_qualified?.must_equal false
97
+ extract_identifiers('database.object').fully_qualified?.must_equal false
98
+ extract_identifiers('server...object').fully_qualified?.must_equal false
99
+ extract_identifiers('server.database..object').fully_qualified?.must_equal false
100
+ extract_identifiers('server.database.schema.object').fully_qualified?.must_equal true
101
+ extract_identifiers('server.database.schema.').fully_qualified?.must_equal true
102
+ extract_identifiers('[obj.name]').fully_qualified?.must_equal false
103
+ extract_identifiers('[schema].[obj.name]').fully_qualified?.must_equal false
104
+ extract_identifiers('[database].[schema].[obj.name]').fully_qualified?.must_equal false
105
+ extract_identifiers('[database].[obj.name]').fully_qualified?.must_equal false
106
+ extract_identifiers('[server.name]...[obj.name]').fully_qualified?.must_equal false
107
+ extract_identifiers('[server.name].[database]..[obj.name]').fully_qualified?.must_equal false
108
+ extract_identifiers('[server.name].[database].[schema].[obj.name]').fully_qualified?.must_equal true
109
+ extract_identifiers('[server.name].[database].[schema].').fully_qualified?.must_equal true
110
+ end
111
+
112
+ it 'can return fully qualified quoted table name' do
113
+ name = extract_identifiers('[my.server].db.schema.')
114
+ name.fully_qualified_database_quoted.must_equal '[my.server].[db]'
115
+ name = extract_identifiers('[server.name].[database].[schema].[object]')
116
+ name.fully_qualified_database_quoted.must_equal '[server.name].[database]'
117
+ name = extract_identifiers('server.database.schema.object')
118
+ name.fully_qualified_database_quoted.must_equal '[server].[database]'
119
+ end
120
+
121
+ end
122
+
123
+ private
124
+
125
+ def extract_identifiers(name)
126
+ SQLServer::Utils.extract_identifiers(name)
127
+ end
128
+
129
+ end