activerecord-jdbcsqlserver-adapter 51.1.0 → 52.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +22 -39
  4. data/{Dockerfile → Dockerfile.ci} +0 -0
  5. data/Gemfile +1 -3
  6. data/README.md +5 -8
  7. data/VERSION +1 -1
  8. data/activerecord-jdbcsqlserver-adapter.gemspec +2 -3
  9. data/docker-compose.ci.yml +7 -5
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +25 -29
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +14 -18
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +43 -0
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +26 -0
  14. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +13 -2
  15. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +53 -10
  16. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +1 -0
  17. data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +5 -13
  18. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +2 -1
  19. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +2 -2
  20. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +43 -27
  21. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +3 -4
  22. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +1 -1
  23. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +7 -0
  24. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +1 -0
  25. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +20 -14
  26. data/lib/active_record/tasks/sqlserver_database_tasks.rb +3 -1
  27. data/lib/activerecord-jdbcsqlserver-adapter.rb +3 -0
  28. data/lib/arel/visitors/sqlserver.rb +1 -1
  29. data/lib/arel_sqlserver.rb +0 -1
  30. data/test/bin/install-freetds.sh +18 -0
  31. data/test/cases/adapter_test_sqlserver.rb +29 -21
  32. data/test/cases/change_column_null_test_sqlserver.rb +42 -0
  33. data/test/cases/coerced_tests.rb +304 -30
  34. data/test/cases/column_test_sqlserver.rb +496 -462
  35. data/test/cases/connection_test_sqlserver.rb +2 -2
  36. data/test/cases/fetch_test_sqlserver.rb +5 -5
  37. data/test/cases/helper_sqlserver.rb +6 -0
  38. data/test/cases/json_test_sqlserver.rb +6 -6
  39. data/test/cases/migration_test_sqlserver.rb +13 -3
  40. data/test/cases/order_test_sqlserver.rb +19 -19
  41. data/test/cases/pessimistic_locking_test_sqlserver.rb +9 -9
  42. data/test/cases/rake_test_sqlserver.rb +20 -20
  43. data/test/cases/schema_dumper_test_sqlserver.rb +34 -33
  44. data/test/cases/schema_test_sqlserver.rb +2 -2
  45. data/test/cases/showplan_test_sqlserver.rb +25 -10
  46. data/test/cases/specific_schema_test_sqlserver.rb +11 -11
  47. data/test/cases/transaction_test_sqlserver.rb +9 -9
  48. data/test/cases/trigger_test_sqlserver.rb +8 -8
  49. data/test/cases/utils_test_sqlserver.rb +36 -36
  50. data/test/cases/uuid_test_sqlserver.rb +8 -8
  51. data/test/migrations/create_clients_and_change_column_null.rb +23 -0
  52. data/test/schema/datatypes/2012.sql +1 -0
  53. data/test/schema/sqlserver_specific_schema.rb +9 -1
  54. data/test/support/core_ext/query_cache.rb +29 -0
  55. metadata +19 -10
  56. data/BACKERS.md +0 -32
@@ -12,6 +12,7 @@ module ActiveRecord
12
12
 
13
13
  def drop_database(database)
14
14
  name = SQLServer::Utils.extract_identifiers(database)
15
+ do_execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
15
16
  do_execute "DROP DATABASE #{name}"
16
17
  end
17
18
 
@@ -140,25 +140,17 @@ module ActiveRecord
140
140
  [sql, binds, pk, sequence_name]
141
141
  end
142
142
 
143
+ # @Override original version uses driver specific query command
144
+ def sqlserver_version
145
+ @sqlserver_version ||= select_value('SELECT @@version').to_s
146
+ end
147
+
143
148
  # @Override
144
149
  def translate_exception(exception, message)
145
150
  return ActiveRecord::ValueTooLong.new(message) if exception.message.include?('java.sql.DataTruncation')
146
151
  super
147
152
  end
148
153
 
149
- # @Overwrite
150
- # Made it so we don't use the internal calls from the gem
151
- def version_year
152
- return @version_year if defined?(@version_year)
153
- @version_year = begin
154
- vstring = select_value('SELECT @@version').to_s
155
- return 2016 if vstring =~ /vNext/
156
- /SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i
157
- rescue Exception => e
158
- 2016
159
- end
160
- end
161
-
162
154
  private
163
155
 
164
156
  def _quote(value)
@@ -8,7 +8,8 @@ module ActiveRecord
8
8
  def visit_TableDefinition(o)
9
9
  if o.as
10
10
  table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name)
11
- projections, source = @conn.to_sql(o.as).match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
11
+ query = o.as.respond_to?(:to_sql) ? o.as.to_sql : o.as
12
+ projections, source = query.match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
12
13
  select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}"
13
14
  else
14
15
  o.instance_variable_set :@as, nil
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module SQLServer
4
- module SchemaDumper
4
+ class SchemaDumper < ConnectionAdapters::SchemaDumper
5
5
 
6
6
  SQLSEVER_NO_LIMIT_TYPES = [
7
7
  'text',
@@ -24,7 +24,7 @@ module ActiveRecord
24
24
 
25
25
  def schema_collation(column)
26
26
  return unless column.collation
27
- column.collation if column.collation != collation
27
+ column.collation if column.collation != @connection.collation
28
28
  end
29
29
 
30
30
  def default_primary_key?(column)
@@ -32,22 +32,32 @@ module ActiveRecord
32
32
  end
33
33
  end
34
34
 
35
- def indexes(table_name, name = nil)
36
- data = (select("EXEC sp_helpindex #{quote(table_name)}", name) || []) rescue [] # JDBC returns nil instead of an array or erring out for no results
35
+ def indexes(table_name)
36
+ data = (select("EXEC sp_helpindex #{quote(table_name)}", "SCHEMA") || []) rescue [] # JDBC returns nil instead of an array or erring out for no results
37
37
  data.reduce([]) do |indexes, index|
38
38
  index = index.with_indifferent_access
39
+
39
40
  if index[:index_description] =~ /primary key/
40
41
  indexes
41
42
  else
42
43
  name = index[:index_name]
43
44
  unique = index[:index_description] =~ /unique/
44
45
  where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}")
45
- columns = index[:index_keys].split(',').map do |column|
46
+ orders = {}
47
+ columns = []
48
+
49
+ index[:index_keys].split(',').each do |column|
46
50
  column.strip!
47
- column.gsub! '(-)', '' if column.ends_with?('(-)')
48
- column
51
+
52
+ if column.ends_with?('(-)')
53
+ column.gsub! '(-)', ''
54
+ orders[column] = :desc
55
+ end
56
+
57
+ columns << column
49
58
  end
50
- indexes << IndexDefinition.new(table_name, name, unique, columns, nil, nil, where)
59
+
60
+ indexes << IndexDefinition.new(table_name, name, unique, columns, where: where, orders: orders)
51
61
  end
52
62
  end
53
63
  end
@@ -132,7 +142,13 @@ module ActiveRecord
132
142
  sql_commands = []
133
143
  indexes = []
134
144
  column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s }
135
- if options_include_default?(options) || (column_object && column_object.type != type.to_sym)
145
+ without_constraints = options.key?(:default) || options.key?(:limit)
146
+ default = if !options.key?(:default) && column_object
147
+ column_object.default
148
+ else
149
+ options[:default]
150
+ end
151
+ if without_constraints || (column_object && column_object.type != type.to_sym)
136
152
  remove_default_constraint(table_name, column_name)
137
153
  indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }
138
154
  remove_indexes(table_name, column_name)
@@ -140,10 +156,9 @@ module ActiveRecord
140
156
  sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
141
157
  sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
142
158
  sql_commands.last << ' NOT NULL' if !options[:null].nil? && options[:null] == false
143
- if options.key?(:default) && default_constraint_name(table_name, column_name).present?
144
- change_column_default(table_name, column_name, options[:default])
145
- elsif options_include_default?(options)
146
- sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(options[:default], column_object)} FOR #{quote_column_name(column_name)}"
159
+ if without_constraints
160
+ default = quote_default_expression(default, column_object || column_for(table_name, column_name))
161
+ sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{default} FOR #{quote_column_name(column_name)}"
147
162
  end
148
163
  # Add any removed indexes back
149
164
  indexes.each do |index|
@@ -211,7 +226,8 @@ module ActiveRecord
211
226
  case type.to_s
212
227
  when 'integer'
213
228
  case limit
214
- when 1..2 then 'smallint'
229
+ when 1 then 'tinyint'
230
+ when 2 then 'smallint'
215
231
  when 3..4, nil then 'integer'
216
232
  when 5..8 then 'bigint'
217
233
  else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
@@ -237,7 +253,8 @@ module ActiveRecord
237
253
  s.gsub(/\s+(?:ASC|DESC)\b/i, '')
238
254
  .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
239
255
  }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
240
- [super, *order_columns].join(', ')
256
+
257
+ (order_columns << super).join(", ")
241
258
  end
242
259
 
243
260
  def update_table_definition(table_name, base)
@@ -247,7 +264,7 @@ module ActiveRecord
247
264
  def change_column_null(table_name, column_name, allow_null, default = nil)
248
265
  table_id = SQLServer::Utils.extract_identifiers(table_name)
249
266
  column_id = SQLServer::Utils.extract_identifiers(column_name)
250
- column = detect_column_for! table_name, column_name
267
+ column = column_for(table_name, column_name)
251
268
  if !allow_null.nil? && allow_null == false && !default.nil?
252
269
  do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL")
253
270
  end
@@ -256,13 +273,17 @@ module ActiveRecord
256
273
  do_execute sql
257
274
  end
258
275
 
276
+ def create_schema_dumper(options)
277
+ SQLServer::SchemaDumper.create(self, options)
278
+ end
279
+
259
280
  private
260
281
 
261
282
  def data_source_sql(name = nil, type: nil)
262
283
  scope = quoted_scope name, type: type
263
284
  table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
264
285
  sql = "SELECT #{table_name}"
265
- sql << ' FROM INFORMATION_SCHEMA.TABLES'
286
+ sql << ' FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)'
266
287
  sql << ' WHERE TABLE_CATALOG = DB_NAME()'
267
288
  sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
268
289
  sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
@@ -408,11 +429,13 @@ module ActiveRecord
408
429
  ci[:default_function] = begin
409
430
  default = ci[:default_value]
410
431
  if default.nil? && view_exists
411
- default = select_value "
432
+ default = select_value %{
412
433
  SELECT c.COLUMN_DEFAULT
413
434
  FROM #{database}.INFORMATION_SCHEMA.COLUMNS c
414
- WHERE c.TABLE_NAME = '#{view_tblnm}'
415
- AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'".squish, 'SCHEMA'
435
+ WHERE
436
+ c.TABLE_NAME = '#{view_tblnm}'
437
+ AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'
438
+ }.squish, 'SCHEMA'
416
439
  end
417
440
  case default
418
441
  when nil
@@ -431,7 +454,7 @@ module ActiveRecord
431
454
  else ci[:type]
432
455
  end
433
456
  value = default.match(/\A\((.*)\)\Z/m)[1]
434
- value = select_value "SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA'
457
+ value = select_value("SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA')
435
458
  [value, nil]
436
459
  end
437
460
  end
@@ -482,13 +505,6 @@ module ActiveRecord
482
505
  "DF_#{table_name}_#{column_name}"
483
506
  end
484
507
 
485
- def detect_column_for!(table_name, column_name)
486
- unless column = schema_cache.columns(table_name).find { |c| c.name == column_name.to_s }
487
- raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
488
- end
489
- column
490
- end
491
-
492
508
  def lowercase_schema_reflection_sql(node)
493
509
  lowercase_schema_reflection ? "LOWER(#{node})" : node
494
510
  end
@@ -504,7 +520,7 @@ module ActiveRecord
504
520
  @view_information ||= {}
505
521
  @view_information[table_name] ||= begin
506
522
  identifier = SQLServer::Utils.extract_identifiers(table_name)
507
- view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA'
523
+ view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA'
508
524
  if view_info
509
525
  view_info = view_info.with_indifferent_access
510
526
  if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
@@ -26,13 +26,13 @@ module ActiveRecord
26
26
 
27
27
  end
28
28
 
29
- Transaction.send :include, SQLServerTransaction
29
+ Transaction.send :prepend, SQLServerTransaction
30
30
 
31
31
  module SQLServerRealTransaction
32
32
 
33
33
  attr_reader :starting_isolation_level
34
34
 
35
- def initialize(connection, options, run_commit_callbacks: false)
35
+ def initialize(connection, options, *args)
36
36
  @connection = connection
37
37
  @starting_isolation_level = current_isolation_level if options[:isolation]
38
38
  super
@@ -58,7 +58,6 @@ module ActiveRecord
58
58
 
59
59
  end
60
60
 
61
- RealTransaction.send :include, SQLServerRealTransaction
62
-
61
+ RealTransaction.send :prepend, SQLServerRealTransaction
63
62
  end
64
63
  end
@@ -2,7 +2,7 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module SQLServer
4
4
  module Type
5
- class Json < ActiveRecord::Type::Internal::AbstractJson
5
+ class Json < ActiveRecord::Type::Json
6
6
 
7
7
  end
8
8
  end
@@ -4,6 +4,13 @@ module ActiveRecord
4
4
  module Type
5
5
  class String < ActiveRecord::Type::String
6
6
 
7
+ def changed_in_place?(raw_old_value, new_value)
8
+ if raw_old_value.is_a?(Data)
9
+ raw_old_value.value != new_value
10
+ else
11
+ super
12
+ end
13
+ end
7
14
 
8
15
  end
9
16
  end
@@ -20,6 +20,7 @@ module ActiveRecord
20
20
 
21
21
  # Currently only called from our custom Time type for formatting
22
22
  def _formatted(value)
23
+ return "#{value.to_s(:_sqlserver_time)}" unless precision
23
24
  "#{value.to_s(:_sqlserver_time)}.#{quote_fractional(value)}"
24
25
  end
25
26
 
@@ -7,6 +7,8 @@ require 'active_record/connection_adapters/sqlserver/core_ext/calculations'
7
7
  require 'active_record/connection_adapters/sqlserver/core_ext/explain' unless defined? JRUBY_VERSION
8
8
  require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber'
9
9
  require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods'
10
+ require 'active_record/connection_adapters/sqlserver/core_ext/finder_methods'
11
+ require 'active_record/connection_adapters/sqlserver/core_ext/query_methods'
10
12
  require 'active_record/connection_adapters/sqlserver/version'
11
13
  require 'active_record/connection_adapters/sqlserver/type'
12
14
  require 'active_record/connection_adapters/sqlserver/database_limits'
@@ -34,7 +36,6 @@ module ActiveRecord
34
36
  SQLServer::Quoting,
35
37
  SQLServer::DatabaseStatements,
36
38
  SQLServer::Showplan,
37
- SQLServer::SchemaDumper,
38
39
  SQLServer::SchemaStatements,
39
40
  SQLServer::DatabaseLimits,
40
41
  SQLServer::DatabaseTasks
@@ -53,6 +54,9 @@ module ActiveRecord
53
54
 
54
55
  ADAPTER_NAME = 'SQLServer'.freeze
55
56
 
57
+ # Default precision for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql)
58
+ DEFAULT_TIME_PRECISION = 7
59
+
56
60
  attr_reader :spid
57
61
 
58
62
  cattr_accessor :cs_equality_operator, instance_accessor: false
@@ -148,6 +152,10 @@ module ActiveRecord
148
152
  false
149
153
  end
150
154
 
155
+ def supports_savepoints?
156
+ true
157
+ end
158
+
151
159
  def supports_in_memory_oltp?
152
160
  @version_year >= 2014
153
161
  end
@@ -201,7 +209,7 @@ module ActiveRecord
201
209
 
202
210
  def tables_with_referential_integrity
203
211
  schemas_and_tables = select_rows <<-SQL.strip_heredoc
204
- SELECT s.name AS schema_name, o.name AS table_name
212
+ SELECT DISTINCT s.name AS schema_name, o.name AS table_name
205
213
  FROM sys.foreign_keys i
206
214
  INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
207
215
  INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
@@ -224,7 +232,7 @@ module ActiveRecord
224
232
  end
225
233
 
226
234
  def sqlserver_azure?
227
- @sqlserver_azure ||= !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i)
235
+ !!(sqlserver_version =~ /Azure/i)
228
236
  end
229
237
 
230
238
  def database_prefix_remote_server?
@@ -265,7 +273,7 @@ module ActiveRecord
265
273
 
266
274
  # === Abstract Adapter (Misc Support) =========================== #
267
275
 
268
- def initialize_type_map(m)
276
+ def initialize_type_map(m = type_map)
269
277
  m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
270
278
  # Exact Numerics
271
279
  register_class_with_limit m, 'bigint(8)', SQLServer::Type::BigInteger
@@ -305,8 +313,7 @@ module ActiveRecord
305
313
  end
306
314
  m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new
307
315
  m.register_type %r{\Atime}i do |sql_type|
308
- scale = extract_scale(sql_type)
309
- precision = extract_precision(sql_type)
316
+ precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION
310
317
  SQLServer::Type::Time.new precision: precision
311
318
  end
312
319
  # Character Strings
@@ -447,16 +454,15 @@ module ActiveRecord
447
454
  end
448
455
 
449
456
  def version_year
450
- return @version_year if defined?(@version_year)
451
- @version_year = begin
452
- vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
453
- return 2016 if vstring =~ /vNext/
454
- /SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i
455
- rescue Exception => e
456
- 2016
457
- end
457
+ return 2016 if sqlserver_version =~ /vNext/
458
+ /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i
459
+ rescue StandardError => e
460
+ 2016
458
461
  end
459
462
 
463
+ def sqlserver_version
464
+ @sqlserver_version ||= _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
465
+ end
460
466
  end
461
467
  end
462
468
  end
@@ -49,9 +49,11 @@ module ActiveRecord
49
49
  end
50
50
 
51
51
  def structure_dump(filename, extra_flags)
52
+ server_arg = "-S #{Shellwords.escape(configuration['host'])}"
53
+ server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration['port']
52
54
  command = [
53
55
  "defncopy",
54
- "-S #{Shellwords.escape(configuration['host'])}",
56
+ server_arg,
55
57
  "-D #{Shellwords.escape(configuration['database'])}",
56
58
  "-U #{Shellwords.escape(configuration['username'])}",
57
59
  "-P #{Shellwords.escape(configuration['password'])}",
@@ -1,6 +1,9 @@
1
1
  # Our core date/time overrides to support prepared statements
2
2
  require 'active_record/connection_adapters/sqlserver/core_ext/date_time'
3
3
 
4
+ require 'active_support' # Need this for the next line
5
+ require 'active_record/log_subscriber' # Need to make sure this is loaded before we load Core for monkey patching
6
+
4
7
  # Load the jar file for the jdbc driver
5
8
  require_relative './jdbc_mssql_driver_loader'
6
9
 
@@ -14,7 +14,7 @@ module Arel
14
14
  # SQLServer ToSql/Visitor (Overides)
15
15
 
16
16
  def visit_Arel_Nodes_BindParam o, collector
17
- collector.add_bind(o) { |i| "@#{i-1}" }
17
+ collector.add_bind(o.value) { |i| "@#{i-1}" }
18
18
  end unless defined? JRUBY_VERSION # converts bind argument markers "?" to "@n", but JDBC wants "?"
19
19
 
20
20
  def visit_Arel_Nodes_Bin o, collector
@@ -1,3 +1,2 @@
1
1
  require 'arel'
2
- require 'arel/visitors/bind_visitor'
3
2
  require 'arel/visitors/sqlserver'
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -x
4
+ set -e
5
+
6
+ FREETDS_VERSION=1.00.21
7
+
8
+ wget http://www.freetds.org/files/stable/freetds-$FREETDS_VERSION.tar.gz
9
+ tar -xzf freetds-$FREETDS_VERSION.tar.gz
10
+ cd freetds-$FREETDS_VERSION
11
+ ./configure --prefix=/opt/local \
12
+ --with-openssl=/opt/local \
13
+ --with-tdsver=7.3
14
+ make
15
+ make install
16
+ cd ..
17
+ rm -rf freetds-$FREETDS_VERSION
18
+ rm freetds-$FREETDS_VERSION.tar.gz
@@ -15,14 +15,15 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
15
15
 
16
16
  it 'has basic and non-sensitive information in the adapters inspect method' do
17
17
  string = connection.inspect
18
- string.must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter}
19
- string.must_match %r{version\: \d+\.\d}
20
- string.must_match %r{mode: (dblib|jdbc)}
21
- string.must_match %r{azure: (true|false)}
22
- string.wont_match %r{host}
23
- string.wont_match %r{password}
24
- string.wont_match %r{username}
25
- string.wont_match %r{port}
18
+
19
+ _(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter}
20
+ _(string).must_match %r{version\: \d+\.\d}
21
+ _(string).must_match %r{mode: (dblib|jdbc)}
22
+ _(string).must_match %r{azure: (true|false)}
23
+ _(string).wont_match %r{host}
24
+ _(string).wont_match %r{password}
25
+ _(string).wont_match %r{username}
26
+ _(string).wont_match %r{port}
26
27
  end
27
28
 
28
29
  it 'has a 128 max #table_alias_length' do
@@ -162,7 +163,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
162
163
  end
163
164
 
164
165
  it 'return an empty array when calling #identity_columns for a table_name with no identity' do
165
- connection.send(:identity_columns, Subscriber.table_name).must_equal []
166
+ _(connection.send(:identity_columns, Subscriber.table_name)).must_equal []
166
167
  end
167
168
 
168
169
  end
@@ -232,6 +233,11 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
232
233
  end
233
234
  end
234
235
 
236
+ it 'not disable referential integrity for the same table twice' do
237
+ tables = SSTestHasPk.connection.tables_with_referential_integrity
238
+ assert_equal tables.size, tables.uniq.size
239
+ end
240
+
235
241
  end
236
242
 
237
243
  describe 'database statements' do
@@ -269,9 +275,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
269
275
  assert_equal 'integer', connection.type_to_sql(:integer, limit: 3)
270
276
  end
271
277
 
272
- it 'create smallints when limit is less than 3' do
278
+ it 'create smallints when limit is 2' do
273
279
  assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2)
274
- assert_equal 'smallint', connection.type_to_sql(:integer, limit: 1)
280
+ end
281
+
282
+ it 'create tinyints when limit is 1' do
283
+ assert_equal 'tinyint', connection.type_to_sql(:integer, limit: 1)
275
284
  end
276
285
 
277
286
  it 'create bigints when limit is greateer than 4' do
@@ -296,7 +305,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
296
305
  end
297
306
 
298
307
  it 'find SSTestCustomersView table name' do
299
- connection.views.must_include 'sst_customers_view'
308
+ _(connection.views).must_include 'sst_customers_view'
300
309
  end
301
310
 
302
311
  it 'work with dynamic finders' do
@@ -337,9 +346,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
337
346
  end
338
347
 
339
348
  it 'find identity column' do
340
- SSTestCustomersView.primary_key.must_equal 'id'
341
- connection.primary_key(SSTestCustomersView.table_name).must_equal 'id'
342
- SSTestCustomersView.columns_hash['id'].must_be :is_identity?
349
+ _(SSTestCustomersView.primary_key).must_equal 'id'
350
+ _(connection.primary_key(SSTestCustomersView.table_name)).must_equal 'id'
351
+ _(SSTestCustomersView.columns_hash['id']).must_be :is_identity?
343
352
  end
344
353
 
345
354
  it 'find default values' do
@@ -364,9 +373,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
364
373
  end
365
374
 
366
375
  it 'find identity column' do
367
- SSTestStringDefaultsView.primary_key.must_equal 'id'
368
- connection.primary_key(SSTestStringDefaultsView.table_name).must_equal 'id'
369
- SSTestStringDefaultsView.columns_hash['id'].must_be :is_identity?
376
+ _(SSTestStringDefaultsView.primary_key).must_equal 'id'
377
+ _(connection.primary_key(SSTestStringDefaultsView.table_name)).must_equal 'id'
378
+ _(SSTestStringDefaultsView.columns_hash['id']).must_be :is_identity?
370
379
  end
371
380
 
372
381
  it 'find default values' do
@@ -415,12 +424,11 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
415
424
 
416
425
  it 'in_memory_oltp' do
417
426
  if ENV['IN_MEMORY_OLTP'] && connection.supports_in_memory_oltp?
418
- SSTMemory.primary_key.must_equal 'id'
419
- SSTMemory.columns_hash['id'].must_be :is_identity?
427
+ _(SSTMemory.primary_key).must_equal 'id'
428
+ _(SSTMemory.columns_hash['id']).must_be :is_identity?
420
429
  else
421
430
  skip 'supports_in_memory_oltp? => false'
422
431
  end
423
432
  end
424
433
 
425
434
  end
426
-