activerecord-sqlserver-adapter 4.1.8 → 4.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -0
  3. data/CHANGELOG.md +60 -0
  4. data/Gemfile +45 -0
  5. data/Guardfile +29 -0
  6. data/MIT-LICENSE +5 -5
  7. data/README.md +193 -0
  8. data/RUNNING_UNIT_TESTS.md +95 -0
  9. data/Rakefile +48 -0
  10. data/activerecord-sqlserver-adapter.gemspec +28 -0
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +5 -15
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +6 -4
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +9 -3
  15. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +3 -1
  16. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +130 -151
  17. data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -25
  18. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +39 -78
  19. data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +71 -47
  20. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +14 -30
  21. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +112 -108
  22. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +4 -2
  23. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +1 -1
  24. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
  25. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +52 -7
  26. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +52 -0
  27. data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
  28. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
  29. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
  30. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +13 -0
  31. data/lib/active_record/connection_adapters/sqlserver/type/castable.rb +15 -0
  32. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +15 -0
  33. data/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb +39 -0
  34. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +14 -0
  35. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +37 -0
  36. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +13 -0
  37. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +17 -0
  38. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +13 -0
  39. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
  40. data/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +32 -0
  41. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +17 -0
  42. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
  43. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +24 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
  46. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +59 -0
  48. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
  49. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
  50. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -0
  51. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +15 -0
  53. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
  54. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
  55. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
  56. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
  57. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
  58. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
  59. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
  60. data/lib/active_record/connection_adapters/sqlserver/utils.rb +118 -12
  61. data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
  62. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +133 -198
  63. data/lib/active_record/connection_adapters/sqlserver_column.rb +15 -86
  64. data/lib/active_record/sqlserver_base.rb +2 -0
  65. data/lib/arel/visitors/sqlserver.rb +120 -393
  66. data/lib/{arel/arel_sqlserver.rb → arel_sqlserver.rb} +1 -3
  67. data/test/cases/adapter_test_sqlserver.rb +420 -0
  68. data/test/cases/coerced_tests.rb +642 -0
  69. data/test/cases/column_test_sqlserver.rb +703 -0
  70. data/test/cases/connection_test_sqlserver.rb +216 -0
  71. data/test/cases/database_statements_test_sqlserver.rb +57 -0
  72. data/test/cases/execute_procedure_test_sqlserver.rb +38 -0
  73. data/test/cases/helper_sqlserver.rb +36 -0
  74. data/test/cases/migration_test_sqlserver.rb +66 -0
  75. data/test/cases/order_test_sqlserver.rb +147 -0
  76. data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
  77. data/test/cases/schema_dumper_test_sqlserver.rb +175 -0
  78. data/test/cases/schema_test_sqlserver.rb +54 -0
  79. data/test/cases/scratchpad_test_sqlserver.rb +9 -0
  80. data/test/cases/showplan_test_sqlserver.rb +65 -0
  81. data/test/cases/specific_schema_test_sqlserver.rb +118 -0
  82. data/test/cases/transaction_test_sqlserver.rb +61 -0
  83. data/test/cases/utils_test_sqlserver.rb +91 -0
  84. data/test/cases/uuid_test_sqlserver.rb +41 -0
  85. data/test/config.yml +35 -0
  86. data/test/fixtures/1px.gif +0 -0
  87. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  88. data/test/models/sqlserver/customers_view.rb +3 -0
  89. data/test/models/sqlserver/datatype.rb +3 -0
  90. data/test/models/sqlserver/datatype_migration.rb +3 -0
  91. data/test/models/sqlserver/dollar_table_name.rb +3 -0
  92. data/test/models/sqlserver/edge_schema.rb +13 -0
  93. data/test/models/sqlserver/fk_has_fk.rb +3 -0
  94. data/test/models/sqlserver/fk_has_pk.rb +3 -0
  95. data/test/models/sqlserver/natural_pk_data.rb +4 -0
  96. data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
  97. data/test/models/sqlserver/no_pk_data.rb +3 -0
  98. data/test/models/sqlserver/quoted_table.rb +7 -0
  99. data/test/models/sqlserver/quoted_view_1.rb +3 -0
  100. data/test/models/sqlserver/quoted_view_2.rb +3 -0
  101. data/test/models/sqlserver/string_default.rb +3 -0
  102. data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
  103. data/test/models/sqlserver/string_defaults_view.rb +3 -0
  104. data/test/models/sqlserver/tinyint_pk.rb +3 -0
  105. data/test/models/sqlserver/upper.rb +3 -0
  106. data/test/models/sqlserver/uppered.rb +3 -0
  107. data/test/models/sqlserver/uuid.rb +3 -0
  108. data/test/schema/datatypes/2012.sql +64 -0
  109. data/test/schema/sqlserver_specific_schema.rb +181 -0
  110. data/test/support/coerceable_test_sqlserver.rb +45 -0
  111. data/test/support/load_schema_sqlserver.rb +29 -0
  112. data/test/support/minitest_sqlserver.rb +1 -0
  113. data/test/support/paths_sqlserver.rb +48 -0
  114. data/test/support/rake_helpers.rb +41 -0
  115. data/test/support/sql_counter_sqlserver.rb +32 -0
  116. metadata +271 -21
  117. data/CHANGELOG +0 -39
  118. data/VERSION +0 -1
  119. data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +0 -17
  120. data/lib/active_record/sqlserver_test_case.rb +0 -17
  121. data/lib/arel/nodes_sqlserver.rb +0 -14
  122. data/lib/arel/select_manager_sqlserver.rb +0 -62
@@ -0,0 +1,90 @@
1
+ require 'cases/helper_sqlserver'
2
+ require 'models/person'
3
+ require 'models/reader'
4
+
5
+ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase
6
+
7
+ fixtures :people, :readers
8
+
9
+ before do
10
+ Person.columns
11
+ Reader.columns
12
+ end
13
+
14
+ it 'uses with updlock by default' do
15
+ assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)| do
16
+ Person.lock(true).to_a.must_equal Person.all.to_a
17
+ end
18
+ end
19
+
20
+ describe 'For simple finds with default lock option' do
21
+
22
+ it 'lock with simple find' do
23
+ assert_nothing_raised do
24
+ Person.transaction do
25
+ Person.lock(true).find(1).must_equal Person.find(1)
26
+ end
27
+ end
28
+ end
29
+
30
+ it 'lock with scoped find' do
31
+ assert_nothing_raised do
32
+ Person.transaction do
33
+ Person.lock(true).scoping do
34
+ Person.find(1).must_equal Person.find(1)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ it 'lock with eager find' do
41
+ assert_nothing_raised do
42
+ Person.transaction do
43
+ person = Person.lock(true).includes(:readers).find(1)
44
+ person.must_equal Person.find(1)
45
+ end
46
+ end
47
+ end
48
+
49
+ it 'reload with lock when #lock! called' do
50
+ assert_nothing_raised do
51
+ Person.transaction do
52
+ person = Person.find 1
53
+ old, person.first_name = person.first_name, 'fooman'
54
+ person.lock!
55
+ assert_equal old, person.first_name
56
+ end
57
+ end
58
+ end
59
+
60
+ it 'can add a custom lock directive' do
61
+ assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do
62
+ Person.lock('WITH(HOLDLOCK, ROWLOCK)').load
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ describe 'For paginated finds' do
69
+
70
+ before do
71
+ Person.delete_all
72
+ 20.times { |n| Person.create!(first_name: "Thing_#{n}") }
73
+ end
74
+
75
+ it 'copes with eager loading un-locked paginated' do
76
+ eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY/
77
+ loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/
78
+ assert_sql(eager_ids_sql, loader_sql) do
79
+ people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a
80
+ people[0].first_name.must_equal 'Thing_10'
81
+ people[1].first_name.must_equal 'Thing_11'
82
+ people[2].first_name.must_equal 'Thing_12'
83
+ people[3].first_name.must_equal 'Thing_13'
84
+ people[4].first_name.must_equal 'Thing_14'
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,175 @@
1
+ require 'cases/helper_sqlserver'
2
+
3
+ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
4
+
5
+ before { all_tables }
6
+
7
+ let(:all_tables) { ActiveRecord::Base.connection.tables }
8
+ let(:schema) { @generated_schema }
9
+
10
+ it 'sst_datatypes' do
11
+ generate_schema_for_table 'sst_datatypes'
12
+ # Exact Numerics
13
+ assert_line :bigint, type: 'bigint', limit: '8', precision: nil, scale: nil, default: '42'
14
+ assert_line :int, type: 'integer', limit: '4', precision: nil, scale: nil, default: '42'
15
+ assert_line :smallint, type: 'integer', limit: '2', precision: nil, scale: nil, default: '42'
16
+ assert_line :tinyint, type: 'integer', limit: '1', precision: nil, scale: nil, default: '42'
17
+ assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: 'true'
18
+ assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: '9', scale: '2', default: '12345.01'
19
+ assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: '18', scale: '0', default: '191.0'
20
+ assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: '36', scale: '2', default: '12345678901234567890.01'
21
+ assert_line :money, type: 'money', limit: nil, precision: '19', scale: '4', default: '4.2'
22
+ assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: '4.2'
23
+ # Approximate Numerics
24
+ assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001'
25
+ assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]}
26
+ # Date and Time
27
+ assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "'0001-01-01'"
28
+ assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'"
29
+ assert_line :smalldatetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1901-01-01 15:45:00'"
30
+ assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil
31
+ assert_line :time_7, type: 'time', limit: nil, precision: nil, scale: nil, default: nil
32
+ # Character Strings
33
+ assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\""
34
+ assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\""
35
+ assert_line :varchar_max, type: 'varchar_max', limit: '2147483647', precision: nil, scale: nil, default: "\"test varchar_max\""
36
+ assert_line :text, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: "\"test text\""
37
+ # Unicode Character Strings
38
+ assert_line :nchar_10, type: 'nchar', limit: '10', precision: nil, scale: nil, default: "\"12345678åå\""
39
+ assert_line :nvarchar_50, type: 'string', limit: '50', precision: nil, scale: nil, default: "\"test nvarchar_50 åå\""
40
+ assert_line :nvarchar_max, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: "\"test nvarchar_max åå\""
41
+ assert_line :ntext, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: "\"test ntext åå\""
42
+ # Binary Strings
43
+ assert_line :binary_49, type: 'binary_basic', limit: '49', precision: nil, scale: nil, default: nil
44
+ assert_line :varbinary_49, type: 'varbinary', limit: '49', precision: nil, scale: nil, default: nil
45
+ assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil
46
+ # Other Data Types
47
+ assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
48
+ assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
49
+ end
50
+
51
+ it 'sst_datatypes_migration' do
52
+ columns = SSTestDatatypeMigration.columns_hash
53
+ generate_schema_for_table 'sst_datatypes_migration'
54
+ # Simple Rails conventions
55
+ columns['integer_col'].sql_type.must_equal 'int(4)'
56
+ columns['bigint_col'].sql_type.must_equal 'bigint(8)'
57
+ columns['boolean_col'].sql_type.must_equal 'bit'
58
+ columns['decimal_col'].sql_type.must_equal 'decimal(18,0)'
59
+ columns['float_col'].sql_type.must_equal 'float'
60
+ columns['string_col'].sql_type.must_equal 'nvarchar(4000)'
61
+ columns['text_col'].sql_type.must_equal 'nvarchar(max)'
62
+ columns['datetime_col'].sql_type.must_equal 'datetime'
63
+ columns['timestamp_col'].sql_type.must_equal 'datetime'
64
+ columns['time_col'].sql_type.must_equal 'time(7)'
65
+ columns['date_col'].sql_type.must_equal 'date'
66
+ columns['binary_col'].sql_type.must_equal 'varbinary(max)'
67
+ assert_line :integer_col, type: 'integer', limit: '4', precision: nil, scale: nil, default: nil
68
+ assert_line :bigint_col, type: 'bigint', limit: '8', precision: nil, scale: nil, default: nil
69
+ assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil
70
+ assert_line :decimal_col, type: 'decimal', limit: nil, precision: '18', scale: '0', default: nil
71
+ assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil
72
+ assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil
73
+ assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil
74
+ assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
75
+ assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil
76
+ assert_line :time_col, type: 'time', limit: nil, precision: nil, scale: nil, default: nil
77
+ assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil
78
+ assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil
79
+ # Our type methods.
80
+ columns['real_col'].sql_type.must_equal 'real'
81
+ columns['money_col'].sql_type.must_equal 'money'
82
+ columns['smallmoney_col'].sql_type.must_equal 'smallmoney'
83
+ columns['char_col'].sql_type.must_equal 'char(1)'
84
+ columns['varchar_col'].sql_type.must_equal 'varchar(8000)'
85
+ columns['text_basic_col'].sql_type.must_equal 'text'
86
+ columns['nchar_col'].sql_type.must_equal 'nchar(1)'
87
+ columns['ntext_col'].sql_type.must_equal 'ntext'
88
+ columns['binary_basic_col'].sql_type.must_equal 'binary(1)'
89
+ columns['varbinary_col'].sql_type.must_equal 'varbinary(8000)'
90
+ columns['uuid_col'].sql_type.must_equal 'uniqueidentifier'
91
+ columns['sstimestamp_col'].sql_type.must_equal 'timestamp'
92
+ assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil
93
+ assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil
94
+ assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil
95
+ assert_line :char_col, type: 'char', limit: '1', precision: nil, scale: nil, default: nil
96
+ assert_line :varchar_col, type: 'varchar', limit: '8000', precision: nil, scale: nil, default: nil
97
+ assert_line :text_basic_col, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: nil
98
+ assert_line :nchar_col, type: 'nchar', limit: '1', precision: nil, scale: nil, default: nil
99
+ assert_line :ntext_col, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: nil
100
+ assert_line :binary_basic_col, type: 'binary_basic', limit: '1', precision: nil, scale: nil, default: nil
101
+ assert_line :varbinary_col, type: 'varbinary', limit: '8000', precision: nil, scale: nil, default: nil
102
+ assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil
103
+ assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil
104
+ end
105
+
106
+ # Special Cases
107
+
108
+ it 'primary_key' do
109
+ generate_schema_for_table('movies') do |output|
110
+ match = output.match(%r{create_table "movies"(.*)do})
111
+ assert_not_nil(match, "nonstandardpk table not found")
112
+ assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved"
113
+ end
114
+ end
115
+
116
+
117
+ private
118
+
119
+ def generate_schema_for_table(*table_names)
120
+ require 'stringio'
121
+ stream = StringIO.new
122
+ ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names
123
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
124
+ @generated_schema = stream.string
125
+ yield @generated_schema if block_given?
126
+ @schema_lines = Hash.new
127
+ type_matcher = /\A\s+t\.\w+\s+"(.*?)"[,\n]/
128
+ @generated_schema.each_line do |line|
129
+ next unless line =~ type_matcher
130
+ @schema_lines[Regexp.last_match[1]] = SchemaLine.new(line)
131
+ end
132
+ @generated_schema
133
+ end
134
+
135
+ def line(column_name)
136
+ @schema_lines[column_name.to_s]
137
+ end
138
+
139
+ def assert_line(column_name, options={})
140
+ line = line(column_name)
141
+ assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}"
142
+ line.type_method.must_equal options[:type], "Type of #{options[:type].inspect} not found in:\n #{line}" if options.key?(:type)
143
+ line.limit.must_equal options[:limit], "Limit of #{options[:limit].inspect} not found in:\n #{line}" if options.key?(:limit)
144
+ line.precision.must_equal options[:precision], "Precision of #{options[:precision].inspect} not found in:\n #{line}" if options.key?(:precision)
145
+ line.scale.must_equal options[:scale], "Scale of #{options[:scale].inspect} not found in:\n #{line}" if options.key?(:scale)
146
+ line.default.must_equal options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(String)
147
+ line.default.must_match options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(Regexp)
148
+ end
149
+
150
+ class SchemaLine
151
+
152
+ attr_reader :line
153
+
154
+ def self.match(method_name, pattern)
155
+ define_method(method_name) { line.match(pattern).try :[], 1 }
156
+ end
157
+
158
+ def initialize(line)
159
+ @line = line
160
+ end
161
+
162
+ match :type_method, %r{\A\s+t\.(.*?)\s}
163
+ match :limit, %r{\slimit:\s(.*?)[,\s]}
164
+ match :default, %r{\sdefault:\s(.*)\n}
165
+ match :precision, %r{\sprecision:\s(.*?)[,\s]}
166
+ match :scale, %r{\sscale:\s(.*?)[,\s]}
167
+
168
+ def to_s
169
+ line
170
+ end
171
+
172
+ end
173
+
174
+ end
175
+
@@ -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,118 @@
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.new.save
9
+ SSTestDollarTableName.limit(20).offset(1)
10
+ end
11
+
12
+ it 'models can use tinyint pk tables' do
13
+ obj = SSTestTinyintPk.create! name: '1'
14
+ obj.id.is_a? Fixnum
15
+ SSTestTinyintPk.find(obj.id).must_equal obj
16
+ end
17
+
18
+ it 'be able to complex count tables with no primary key' do
19
+ SSTestNoPkData.delete_all
20
+ 10.times { |n| SSTestNoPkData.create! name: "Test#{n}" }
21
+ assert_equal 1, SSTestNoPkData.where(name: 'Test5').count
22
+ end
23
+
24
+ it 'quote table names properly even when they are views' do
25
+ obj = SSTestQuotedTable.create!
26
+ assert_nothing_raised { assert SSTestQuotedTable.first }
27
+ obj = SSTestQuotedTableUser.create!
28
+ assert_nothing_raised { assert SSTestQuotedTableUser.first }
29
+ obj = SSTestQuotedView1.create!
30
+ assert_nothing_raised { assert SSTestQuotedView1.first }
31
+ obj = SSTestQuotedView2.create!
32
+ assert_nothing_raised { assert SSTestQuotedView2.first }
33
+ end
34
+
35
+ it 'cope with multi line defaults' do
36
+ default = SSTestStringDefault.new
37
+ assert_equal "Some long default with a\nnew line.", default.string_with_multiline_default
38
+ end
39
+
40
+ it 'default strings before save' do
41
+ default = SSTestStringDefault.new
42
+ assert_equal nil, default.string_with_null_default
43
+ assert_equal 'null', default.string_with_pretend_null_one
44
+ assert_equal '(null)', default.string_with_pretend_null_two
45
+ assert_equal 'NULL', default.string_with_pretend_null_three
46
+ assert_equal '(NULL)', default.string_with_pretend_null_four
47
+ assert_equal '(3)', default.string_with_pretend_paren_three
48
+ end
49
+
50
+ it 'default strings after save' do
51
+ default = SSTestStringDefault.create
52
+ assert_equal nil, default.string_with_null_default
53
+ assert_equal 'null', default.string_with_pretend_null_one
54
+ assert_equal '(null)', default.string_with_pretend_null_two
55
+ assert_equal 'NULL', default.string_with_pretend_null_three
56
+ assert_equal '(NULL)', default.string_with_pretend_null_four
57
+ end
58
+
59
+ # Natural primary keys.
60
+
61
+ it 'work with identity inserts' do
62
+ record = SSTestNaturalPkData.new name: 'Test', description: 'Natural identity inserts.'
63
+ record.id = '12345ABCDE'
64
+ assert record.save
65
+ assert_equal '12345ABCDE', record.reload.id
66
+ end
67
+
68
+ it 'work with identity inserts when the key is an int' do
69
+ record = SSTestNaturalPkIntData.new name: 'Test', description: 'Natural identity inserts.'
70
+ record.id = 12
71
+ assert record.save
72
+ assert_equal 12, record.reload.id
73
+ end
74
+
75
+ it 'use primary key for row table order in pagination sql' do
76
+ sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY/
77
+ assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load }
78
+ end
79
+
80
+ # Special quoted column
81
+
82
+ it 'work as normal' do
83
+ SSTestEdgeSchema.delete_all
84
+ r = SSTestEdgeSchema.create! 'crazy]]quote' => 'crazyqoute'
85
+ assert SSTestEdgeSchema.columns_hash['crazy]]quote']
86
+ assert_equal r, SSTestEdgeSchema.where('crazy]]quote' => 'crazyqoute').first
87
+ end
88
+
89
+ # With column names that have spaces
90
+
91
+ it 'create record using a custom attribute reader and be able to load it back in' do
92
+ value = 'Saved value into a column that has a space in the name.'
93
+ record = SSTestEdgeSchema.create! with_spaces: value
94
+ assert_equal value, SSTestEdgeSchema.find(record.id).with_spaces
95
+ end
96
+
97
+ # With description column
98
+
99
+ it 'allow all sorts of ordering without adapter munging it up with special description column' do
100
+ SSTestEdgeSchema.create! description: 'A'
101
+ SSTestEdgeSchema.create! description: 'B'
102
+ SSTestEdgeSchema.create! description: 'C'
103
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description').map(&:description)
104
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description asc').map(&:description)
105
+ assert_equal ['A','B','C'], SSTestEdgeSchema.order('description ASC').map(&:description)
106
+ assert_equal ['C','B','A'], SSTestEdgeSchema.order('description desc').map(&:description)
107
+ assert_equal ['C','B','A'], SSTestEdgeSchema.order('description DESC').map(&:description)
108
+ end
109
+
110
+ # For uniqueidentifier model helpers
111
+
112
+ it 'returns a new id via connection newid_function' do
113
+ acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID
114
+ db_uuid = ActiveRecord::Base.connection.newid_function
115
+ db_uuid.must_match(acceptable_uuid)
116
+ end
117
+
118
+ end