activerecord-sqlserver-adapter 6.1.2.0 → 7.0.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +4 -1
  3. data/CHANGELOG.md +16 -41
  4. data/Gemfile +1 -0
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +18 -9
  7. data/VERSION +1 -1
  8. data/activerecord-sqlserver-adapter.gemspec +2 -2
  9. data/appveyor.yml +4 -6
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +7 -15
  11. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +15 -6
  12. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +2 -4
  13. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +11 -9
  14. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +27 -8
  15. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +3 -1
  16. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +1 -1
  17. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +1 -1
  18. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +1 -1
  19. data/lib/active_record/connection_adapters/sqlserver/utils.rb +16 -1
  20. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +84 -74
  21. data/lib/arel/visitors/sqlserver.rb +2 -0
  22. data/test/cases/active_schema_test_sqlserver.rb +55 -0
  23. data/test/cases/coerced_tests.rb +301 -85
  24. data/test/cases/column_test_sqlserver.rb +58 -58
  25. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
  26. data/test/cases/rake_test_sqlserver.rb +2 -1
  27. data/test/cases/schema_dumper_test_sqlserver.rb +1 -1
  28. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +1 -1
  29. data/test/support/coerceable_test_sqlserver.rb +4 -4
  30. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
  31. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
  32. data/test/support/rake_helpers.rb +3 -1
  33. metadata +15 -11
  34. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  35. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
@@ -105,7 +105,7 @@ module ActiveRecord
105
105
  end
106
106
 
107
107
  def config_appname(config)
108
- if self.instance_methods.include?(:configure_application_name)
108
+ if instance_methods.include?(:configure_application_name)
109
109
  ActiveSupport::Deprecation.warn <<~MSG.squish
110
110
  Configuring the application name used by TinyTDS by overriding the
111
111
  `ActiveRecord::ConnectionAdapters::SQLServerAdapter#configure_application_name`
@@ -119,9 +119,9 @@ module ActiveRecord
119
119
  end
120
120
 
121
121
  def rails_application_name
122
- return nil if Rails.application.nil?
123
-
124
122
  Rails.application.class.name.split("::").first
123
+ rescue
124
+ nil # Might not be in a Rails context so we fallback to `nil`.
125
125
  end
126
126
 
127
127
  def config_login_timeout(config)
@@ -377,83 +377,93 @@ module ActiveRecord
377
377
  version_year
378
378
  end
379
379
 
380
- protected
381
-
382
- # === Abstract Adapter (Misc Support) =========================== #
383
-
384
- def initialize_type_map(m = type_map)
385
- m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
386
-
387
- # Exact Numerics
388
- register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger
389
- m.alias_type "bigint", "bigint(8)"
390
- register_class_with_limit m, "int(4)", SQLServer::Type::Integer
391
- m.alias_type "integer", "int(4)"
392
- m.alias_type "int", "int(4)"
393
- register_class_with_limit m, "smallint(2)", SQLServer::Type::SmallInteger
394
- m.alias_type "smallint", "smallint(2)"
395
- register_class_with_limit m, "tinyint(1)", SQLServer::Type::TinyInteger
396
- m.alias_type "tinyint", "tinyint(1)"
397
- m.register_type "bit", SQLServer::Type::Boolean.new
398
- m.register_type %r{\Adecimal}i do |sql_type|
399
- scale = extract_scale(sql_type)
400
- precision = extract_precision(sql_type)
401
- if scale == 0
402
- SQLServer::Type::DecimalWithoutScale.new(precision: precision)
403
- else
404
- SQLServer::Type::Decimal.new(precision: precision, scale: scale)
380
+ class << self
381
+ protected
382
+
383
+ def initialize_type_map(m)
384
+ m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
385
+
386
+ # Exact Numerics
387
+ register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger
388
+ m.alias_type "bigint", "bigint(8)"
389
+ register_class_with_limit m, "int(4)", SQLServer::Type::Integer
390
+ m.alias_type "integer", "int(4)"
391
+ m.alias_type "int", "int(4)"
392
+ register_class_with_limit m, "smallint(2)", SQLServer::Type::SmallInteger
393
+ m.alias_type "smallint", "smallint(2)"
394
+ register_class_with_limit m, "tinyint(1)", SQLServer::Type::TinyInteger
395
+ m.alias_type "tinyint", "tinyint(1)"
396
+ m.register_type "bit", SQLServer::Type::Boolean.new
397
+ m.register_type %r{\Adecimal}i do |sql_type|
398
+ scale = extract_scale(sql_type)
399
+ precision = extract_precision(sql_type)
400
+ if scale == 0
401
+ SQLServer::Type::DecimalWithoutScale.new(precision: precision)
402
+ else
403
+ SQLServer::Type::Decimal.new(precision: precision, scale: scale)
404
+ end
405
405
  end
406
- end
407
- m.alias_type %r{\Anumeric}i, "decimal"
408
- m.register_type "money", SQLServer::Type::Money.new
409
- m.register_type "smallmoney", SQLServer::Type::SmallMoney.new
410
-
411
- # Approximate Numerics
412
- m.register_type "float", SQLServer::Type::Float.new
413
- m.register_type "real", SQLServer::Type::Real.new
414
-
415
- # Date and Time
416
- m.register_type "date", SQLServer::Type::Date.new
417
- m.register_type %r{\Adatetime} do |sql_type|
418
- precision = extract_precision(sql_type)
419
- if precision
420
- SQLServer::Type::DateTime2.new precision: precision
421
- else
422
- SQLServer::Type::DateTime.new
406
+ m.alias_type %r{\Anumeric}i, "decimal"
407
+ m.register_type "money", SQLServer::Type::Money.new
408
+ m.register_type "smallmoney", SQLServer::Type::SmallMoney.new
409
+
410
+ # Approximate Numerics
411
+ m.register_type "float", SQLServer::Type::Float.new
412
+ m.register_type "real", SQLServer::Type::Real.new
413
+
414
+ # Date and Time
415
+ m.register_type "date", SQLServer::Type::Date.new
416
+ m.register_type %r{\Adatetime} do |sql_type|
417
+ precision = extract_precision(sql_type)
418
+ if precision
419
+ SQLServer::Type::DateTime2.new precision: precision
420
+ else
421
+ SQLServer::Type::DateTime.new
422
+ end
423
423
  end
424
+ m.register_type %r{\Adatetimeoffset}i do |sql_type|
425
+ precision = extract_precision(sql_type)
426
+ SQLServer::Type::DateTimeOffset.new precision: precision
427
+ end
428
+ m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new
429
+ m.register_type %r{\Atime}i do |sql_type|
430
+ precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION
431
+ SQLServer::Type::Time.new precision: precision
432
+ end
433
+
434
+ # Character Strings
435
+ register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
436
+ register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
437
+ m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new
438
+ m.register_type "text", SQLServer::Type::Text.new
439
+
440
+ # Unicode Character Strings
441
+ register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
442
+ register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
443
+ m.alias_type "string", "nvarchar(4000)"
444
+ m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
445
+ m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
446
+ m.register_type "ntext", SQLServer::Type::UnicodeText.new
447
+
448
+ # Binary Strings
449
+ register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
450
+ register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
451
+ m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new
452
+
453
+ # Other Data Types
454
+ m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new
455
+ m.register_type "timestamp", SQLServer::Type::Timestamp.new
424
456
  end
425
- m.register_type %r{\Adatetimeoffset}i do |sql_type|
426
- precision = extract_precision(sql_type)
427
- SQLServer::Type::DateTimeOffset.new precision: precision
428
- end
429
- m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new
430
- m.register_type %r{\Atime}i do |sql_type|
431
- precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION
432
- SQLServer::Type::Time.new precision: precision
433
- end
457
+ end
434
458
 
435
- # Character Strings
436
- register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
437
- register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
438
- m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new
439
- m.register_type "text", SQLServer::Type::Text.new
459
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
440
460
 
441
- # Unicode Character Strings
442
- register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
443
- register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
444
- m.alias_type "string", "nvarchar(4000)"
445
- m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
446
- m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
447
- m.register_type "ntext", SQLServer::Type::UnicodeText.new
461
+ protected
448
462
 
449
- # Binary Strings
450
- register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
451
- register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
452
- m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new
463
+ # === Abstract Adapter (Misc Support) =========================== #
453
464
 
454
- # Other Data Types
455
- m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new
456
- m.register_type "timestamp", SQLServer::Type::Timestamp.new
465
+ def type_map
466
+ TYPE_MAP
457
467
  end
458
468
 
459
469
  def translate_exception(e, message:, sql:, binds:)
@@ -467,7 +477,7 @@ module ActiveRecord
467
477
  when /has been chosen as the deadlock victim/i
468
478
  DeadlockVictim.new(message, sql: sql, binds: binds)
469
479
  when /database .* does not exist/i
470
- NoDatabaseError.new(message, sql: sql, binds: binds)
480
+ NoDatabaseError.new(message)
471
481
  when /data would be truncated/
472
482
  ValueTooLong.new(message, sql: sql, binds: binds)
473
483
  when /connection timed out/
@@ -83,6 +83,8 @@ module Arel
83
83
  # Monkey-patch start. Add query attribute bindings rather than just values.
84
84
  column_name = o.column_name
85
85
  column_type = o.attribute.relation.type_for_attribute(o.column_name)
86
+ # Use cast_type on encrypted attributes. Don't encrypt them again
87
+ column_type = column_type.cast_type if column_type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType)
86
88
  attrs = values.map { |value| ActiveRecord::Relation::QueryAttribute.new(column_name, value, column_type) }
87
89
 
88
90
  collector.add_binds(attrs, &bind_block)
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper_sqlserver"
4
+
5
+ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase
6
+ before do
7
+ connection.create_table :schema_test_table, force: true, id: false do |t|
8
+ t.column :foo, :string, limit: 100
9
+ t.column :state, :string
10
+ end
11
+ end
12
+
13
+ after do
14
+ connection.drop_table :schema_test_table rescue nil
15
+ end
16
+
17
+ it 'default index' do
18
+ assert_sql('CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do
19
+ connection.add_index :schema_test_table, "foo"
20
+ end
21
+ end
22
+
23
+ it 'unique index' do
24
+ assert_sql('CREATE UNIQUE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do
25
+ connection.add_index :schema_test_table, "foo", unique: true
26
+ end
27
+ end
28
+
29
+ it 'where condition on index' do
30
+ assert_sql("CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo]) WHERE state = 'active'") do
31
+ connection.add_index :schema_test_table, "foo", where: "state = 'active'"
32
+ end
33
+ end
34
+
35
+ it 'if index does not exist' do
36
+ assert_sql("IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = 'index_schema_test_table_on_foo') " \
37
+ "CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])") do
38
+ connection.add_index :schema_test_table, "foo", if_not_exists: true
39
+ end
40
+ end
41
+
42
+ describe "index types" do
43
+ it 'clustered index' do
44
+ assert_sql('CREATE CLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do
45
+ connection.add_index :schema_test_table, "foo", type: :clustered
46
+ end
47
+ end
48
+
49
+ it 'nonclustered index' do
50
+ assert_sql('CREATE NONCLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do
51
+ connection.add_index :schema_test_table, "foo", type: :nonclustered
52
+ end
53
+ end
54
+ end
55
+ end