activerecord-sqlserver-adapter 4.1.8 → 4.2.0.pre

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 (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