activerecord-jdbcsqlserver-adapter 50.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +27 -0
  4. data/CHANGELOG.md +124 -0
  5. data/CODE_OF_CONDUCT.md +31 -0
  6. data/Dockerfile +20 -0
  7. data/Gemfile +77 -0
  8. data/Guardfile +29 -0
  9. data/MIT-LICENSE +20 -0
  10. data/RAILS5-TODO.md +5 -0
  11. data/README.md +93 -0
  12. data/RUNNING_UNIT_TESTS.md +96 -0
  13. data/Rakefile +46 -0
  14. data/VERSION +1 -0
  15. data/activerecord-jdbcsqlserver-adapter.gemspec +21 -0
  16. data/appveyor.yml +39 -0
  17. data/docker-compose.ci.yml +11 -0
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +27 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/date_time.rb +58 -0
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +47 -0
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
  23. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
  24. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +362 -0
  25. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +67 -0
  26. data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
  27. data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +192 -0
  28. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +99 -0
  29. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -0
  31. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +517 -0
  32. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +66 -0
  33. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +66 -0
  34. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +22 -0
  35. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +20 -0
  36. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +112 -0
  37. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +64 -0
  38. data/lib/active_record/connection_adapters/sqlserver/type.rb +49 -0
  39. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +19 -0
  40. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +21 -0
  41. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +15 -0
  42. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +32 -0
  43. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +30 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +61 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +71 -0
  46. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +23 -0
  48. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +21 -0
  49. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +19 -0
  50. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +15 -0
  51. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +11 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +25 -0
  53. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +19 -0
  54. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +15 -0
  55. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +25 -0
  56. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +29 -0
  57. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
  58. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +19 -0
  59. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +68 -0
  60. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +93 -0
  61. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +19 -0
  62. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +25 -0
  63. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +21 -0
  64. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
  65. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +19 -0
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +26 -0
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +24 -0
  68. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +36 -0
  69. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +26 -0
  70. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +24 -0
  71. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +26 -0
  72. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +24 -0
  73. data/lib/active_record/connection_adapters/sqlserver/utils.rb +146 -0
  74. data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
  75. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +445 -0
  76. data/lib/active_record/connection_adapters/sqlserver_column.rb +28 -0
  77. data/lib/active_record/jdbc_sqlserver_connection_methods.rb +31 -0
  78. data/lib/active_record/sqlserver_base.rb +16 -0
  79. data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
  80. data/lib/activerecord-jdbcsqlserver-adapter.rb +24 -0
  81. data/lib/activerecord-sqlserver-adapter.rb +1 -0
  82. data/lib/arel/visitors/sqlserver.rb +205 -0
  83. data/lib/arel_sqlserver.rb +3 -0
  84. data/test/appveyor/dbsetup.ps1 +27 -0
  85. data/test/appveyor/dbsetup.sql +11 -0
  86. data/test/bin/wait-for.sh +79 -0
  87. data/test/cases/adapter_test_sqlserver.rb +430 -0
  88. data/test/cases/coerced_tests.rb +845 -0
  89. data/test/cases/column_test_sqlserver.rb +812 -0
  90. data/test/cases/connection_test_sqlserver.rb +71 -0
  91. data/test/cases/execute_procedure_test_sqlserver.rb +45 -0
  92. data/test/cases/fetch_test_sqlserver.rb +57 -0
  93. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
  94. data/test/cases/helper_sqlserver.rb +44 -0
  95. data/test/cases/index_test_sqlserver.rb +47 -0
  96. data/test/cases/json_test_sqlserver.rb +32 -0
  97. data/test/cases/migration_test_sqlserver.rb +61 -0
  98. data/test/cases/order_test_sqlserver.rb +147 -0
  99. data/test/cases/pessimistic_locking_test_sqlserver.rb +94 -0
  100. data/test/cases/rake_test_sqlserver.rb +169 -0
  101. data/test/cases/schema_dumper_test_sqlserver.rb +234 -0
  102. data/test/cases/schema_test_sqlserver.rb +54 -0
  103. data/test/cases/scratchpad_test_sqlserver.rb +8 -0
  104. data/test/cases/showplan_test_sqlserver.rb +65 -0
  105. data/test/cases/specific_schema_test_sqlserver.rb +180 -0
  106. data/test/cases/transaction_test_sqlserver.rb +91 -0
  107. data/test/cases/utils_test_sqlserver.rb +129 -0
  108. data/test/cases/uuid_test_sqlserver.rb +49 -0
  109. data/test/config.yml +38 -0
  110. data/test/debug.rb +14 -0
  111. data/test/fixtures/1px.gif +0 -0
  112. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  113. data/test/models/sqlserver/booking.rb +3 -0
  114. data/test/models/sqlserver/customers_view.rb +3 -0
  115. data/test/models/sqlserver/datatype.rb +3 -0
  116. data/test/models/sqlserver/datatype_migration.rb +8 -0
  117. data/test/models/sqlserver/dollar_table_name.rb +3 -0
  118. data/test/models/sqlserver/dot_table_name.rb +3 -0
  119. data/test/models/sqlserver/edge_schema.rb +13 -0
  120. data/test/models/sqlserver/fk_has_fk.rb +3 -0
  121. data/test/models/sqlserver/fk_has_pk.rb +3 -0
  122. data/test/models/sqlserver/natural_pk_data.rb +4 -0
  123. data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
  124. data/test/models/sqlserver/no_pk_data.rb +3 -0
  125. data/test/models/sqlserver/object_default.rb +3 -0
  126. data/test/models/sqlserver/quoted_table.rb +7 -0
  127. data/test/models/sqlserver/quoted_view_1.rb +3 -0
  128. data/test/models/sqlserver/quoted_view_2.rb +3 -0
  129. data/test/models/sqlserver/sst_memory.rb +3 -0
  130. data/test/models/sqlserver/string_default.rb +3 -0
  131. data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
  132. data/test/models/sqlserver/string_defaults_view.rb +3 -0
  133. data/test/models/sqlserver/tinyint_pk.rb +3 -0
  134. data/test/models/sqlserver/upper.rb +3 -0
  135. data/test/models/sqlserver/uppered.rb +3 -0
  136. data/test/models/sqlserver/uuid.rb +3 -0
  137. data/test/schema/datatypes/2012.sql +55 -0
  138. data/test/schema/enable-in-memory-oltp.sql +81 -0
  139. data/test/schema/sqlserver_specific_schema.rb +238 -0
  140. data/test/support/coerceable_test_sqlserver.rb +49 -0
  141. data/test/support/connection_reflection.rb +34 -0
  142. data/test/support/load_schema_sqlserver.rb +29 -0
  143. data/test/support/minitest_sqlserver.rb +1 -0
  144. data/test/support/paths_sqlserver.rb +50 -0
  145. data/test/support/rake_helpers.rb +41 -0
  146. data/test/support/sql_counter_sqlserver.rb +28 -0
  147. data/test/support/test_in_memory_oltp.rb +15 -0
  148. metadata +310 -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.data_source_exists?('test.sst_schema_natural_id')
17
+ assert connection.data_source_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,8 @@
1
+ require 'cases/helper_sqlserver'
2
+
3
+ class ScratchpadTestSQLServer < ActiveRecord::TestCase
4
+
5
+ it 'helps debug things' do
6
+ end
7
+
8
+ 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,180 @@
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
+ ['Fixnum', 'Integer'].must_include obj.id.class.name
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_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_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
+ if defined? JRUBY_VERSION
98
+ sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET \? ROWS FETCH NEXT \? ROWS ONLY/
99
+ else
100
+ sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/
101
+ end
102
+ assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load }
103
+ end
104
+
105
+ # Special quoted column
106
+
107
+ it 'work as normal' do
108
+ SSTestEdgeSchema.delete_all
109
+ r = SSTestEdgeSchema.create! 'crazy]]quote' => 'crazyqoute'
110
+ assert SSTestEdgeSchema.columns_hash['crazy]]quote']
111
+ assert_equal r, SSTestEdgeSchema.where('crazy]]quote' => 'crazyqoute').first
112
+ end
113
+
114
+ it 'various methods to bypass national quoted columns for any column, but primarily useful for char/varchar' do
115
+ value = Class.new do
116
+ def quoted_id
117
+ "'T'"
118
+ end
119
+ end
120
+ # Using ActiveRecord's quoted_id feature for objects.
121
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first }
122
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first }
123
+ # Using our custom char type data.
124
+ type = ActiveRecord::Type::SQLServer::Char
125
+ data = ActiveRecord::Type::SQLServer::Data
126
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new('T', type.new)).first }
127
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new('T', type.new)).first }
128
+ # Taking care of everything.
129
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: 'T').first }
130
+ assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: 'T').first }
131
+ end unless defined? JRUBY_VERSION
132
+
133
+ it 'can update and hence properly quoted non-national char/varchar columns' do
134
+ o = SSTestDatatypeMigration.create!
135
+ o.varchar_col = "O'Reilly"
136
+ o.save!
137
+ o.reload.varchar_col.must_equal "O'Reilly"
138
+ o.varchar_col = nil
139
+ o.save!
140
+ o.reload.varchar_col.must_be_nil
141
+ end
142
+
143
+ # With column names that have spaces
144
+
145
+ it 'create record using a custom attribute reader and be able to load it back in' do
146
+ value = 'Saved value into a column that has a space in the name.'
147
+ record = SSTestEdgeSchema.create! with_spaces: value
148
+ assert_equal value, SSTestEdgeSchema.find(record.id).with_spaces
149
+ end
150
+
151
+ # With description column
152
+
153
+ it 'allow all sorts of ordering without adapter munging it up with special description column' do
154
+ SSTestEdgeSchema.create! description: 'A'
155
+ SSTestEdgeSchema.create! description: 'B'
156
+ SSTestEdgeSchema.create! description: 'C'
157
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description').map(&:description)
158
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description asc').map(&:description)
159
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description ASC').map(&:description)
160
+ assert_equal ['C','B','A'], SSTestEdgeSchema.order('description desc').map(&:description)
161
+ assert_equal ['C','B','A'], SSTestEdgeSchema.order('description DESC').map(&:description)
162
+ end
163
+
164
+ # For uniqueidentifier model helpers
165
+
166
+ it 'returns a new id via connection newid_function' do
167
+ acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID
168
+ db_uuid = ActiveRecord::Base.connection.newid_function
169
+ db_uuid.must_match(acceptable_uuid)
170
+ end
171
+
172
+ # with similar table definition in two schemas
173
+
174
+ it 'returns the correct primary columns' do
175
+ connection = ActiveRecord::Base.connection
176
+ assert_equal 'field_1', connection.columns('test.sst_schema_test_mulitple_schema').detect(&:is_primary?).name
177
+ assert_equal 'field_2', connection.columns('test2.sst_schema_test_mulitple_schema').detect(&:is_primary?).name
178
+ end
179
+
180
+ end
@@ -0,0 +1,91 @@
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_tests = 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
+ describe 'when READ_COMMITTED_SNAPSHOT is set' do
56
+ before do
57
+ connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION ON"
58
+ connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE"
59
+ end
60
+
61
+ after do
62
+ connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION OFF"
63
+ connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT OFF WITH ROLLBACK IMMEDIATE"
64
+ end
65
+
66
+ it 'should use READ COMMITTED as an isolation level' do
67
+ connection.user_options_isolation_level.must_match "read committed snapshot"
68
+
69
+ Ship.transaction(isolation: :serializable) do
70
+ Ship.create! name: 'Black Pearl'
71
+ end
72
+
73
+ # We're actually testing that the isolation level was correctly reset to
74
+ # "READ COMMITTED", and that no exception was raised (it's reported back
75
+ # by SQL Server as "read committed snapshot").
76
+ connection.user_options_isolation_level.must_match "read committed snapshot"
77
+ end
78
+ end
79
+
80
+
81
+ protected
82
+
83
+ def delete_ships
84
+ Ship.delete_all
85
+ end
86
+
87
+ def assert_no_ships
88
+ assert Ship.count.zero?, "Expected Ship to have no models but it did have:\n#{Ship.all.inspect}"
89
+ end
90
+
91
+ 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