activerecord-sqlserver-adapter 4.2.6 → 4.2.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/Gemfile +9 -0
  4. data/README.md +40 -26
  5. data/VERSION +1 -0
  6. data/activerecord-sqlserver-adapter.gemspec +0 -10
  7. data/appveyor.yml +15 -3
  8. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +3 -3
  9. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +35 -11
  10. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +4 -16
  11. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +12 -2
  12. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +8 -0
  13. data/lib/active_record/connection_adapters/sqlserver/type.rb +3 -1
  14. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +9 -0
  15. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +18 -12
  16. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
  17. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +31 -0
  18. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +6 -6
  19. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +13 -29
  20. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +72 -0
  21. data/lib/active_record/connection_adapters/sqlserver/utils.rb +12 -12
  22. data/lib/active_record/connection_adapters/sqlserver/version.rb +1 -1
  23. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +41 -5
  24. data/lib/active_record/sqlserver_base.rb +2 -4
  25. data/lib/arel/visitors/sqlserver.rb +19 -0
  26. data/test/cases/adapter_test_sqlserver.rb +24 -0
  27. data/test/cases/coerced_tests.rb +14 -0
  28. data/test/cases/column_test_sqlserver.rb +120 -47
  29. data/test/cases/connection_test_sqlserver.rb +3 -3
  30. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
  31. data/test/cases/helper_sqlserver.rb +24 -16
  32. data/test/cases/migration_test_sqlserver.rb +0 -5
  33. data/test/cases/rake_test_sqlserver.rb +29 -10
  34. data/test/cases/schema_dumper_test_sqlserver.rb +28 -11
  35. data/test/cases/transaction_test_sqlserver.rb +2 -2
  36. data/test/cases/utils_test_sqlserver.rb +44 -14
  37. data/test/config.yml +2 -0
  38. data/test/debug.rb +14 -0
  39. data/test/schema/datatypes/2012.sql +8 -18
  40. data/test/schema/sqlserver_specific_schema.rb +14 -12
  41. data/test/support/connection_reflection.rb +37 -0
  42. metadata +13 -143
  43. data/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e84d27ebd75f08bcf37cf9243972440b0fc57280
4
- data.tar.gz: f1f0a3e19713a80cdeba715025b4cb8523574c25
3
+ metadata.gz: 6f37e4ce81b63e3fe3375111b70da855a95d1251
4
+ data.tar.gz: 7b11a582736c9718dbb20097b0b55c392f691b08
5
5
  SHA512:
6
- metadata.gz: f35f9ee3b2b34f7601f86d6867f2a86bf618203411e6e94ba4eec6b8d92f26cca09c63ae0596f50acacd4090fc644c4bfba43a3d2b7f2c33f849b170174d3cad
7
- data.tar.gz: e59869fcb2a59c6a68a3778a56f7a1834becaec3c15844b44ed522115ca1a157cffe18c378b48cb697904188580171cdca76940f800377228c030ebc00c57233
6
+ metadata.gz: f0066beca8922fb26506304059d80069a91f45b9392857e68a2af63640f4706ff300ca4b8e8bb18d807117bb168039188d50d397897488de255b31c454692a1c
7
+ data.tar.gz: e52261a203dcc9d9124ed6d04113fbcc15887e09b38f18076e213906da9b2b4fbf71e83032b5ee284483c206a28d9bb237f6f04e67cbdcc7e0b68ece791f8fbb
@@ -1,4 +1,38 @@
1
1
 
2
+ ## v4.2.8
3
+
4
+ #### Fixed
5
+
6
+ * Azure-Friendly Disable Referential Integrity. No more `sp_MSforeachtable` usage. Fixes #421
7
+ * Azure-Friendly DB create/drop. Fixes #442
8
+ - Create allows edition options like: MAXSIZE, EDITION, and SERVICE_OBJECTIVE.
9
+
10
+
11
+ ## v4.2.7
12
+
13
+ #### Added
14
+
15
+ * Support 2008 Datatypes Using TDSVER=7.3. Fixes #433
16
+
17
+ #### Changed
18
+
19
+ * Test now use latest v0.9.5 of TinyTDS. Includes tests for `defncopy` Windows binstub.
20
+ * Make linked servers stronger. Fixes #427. Thanks @jippeholwerda
21
+ * Use proper module for the `sqlserver_connection` method. Fixes #431. Thanks @jippeholwerda
22
+ * All datetime casting using the `Time::DATE_FORMATS[:_sqlserver_*]` formats set after connection.
23
+
24
+ #### Removed
25
+
26
+ * The `SQLServer::Utils.with_sqlserver_db_date_formats` helper and `quoted_date` hacks.
27
+ * The `Quoter` value type which allowed column => type special case quoting.
28
+
29
+ #### Fixed
30
+
31
+ * Every time datatype has perfect micro/nano second handling.
32
+ * All supported datatypes dump defaults properly to schema.rb
33
+ * Partial indexes using `:where` in schema dumper. Fixes #153
34
+
35
+
2
36
  ## v4.2.6
3
37
 
4
38
  #### Fixed
data/Gemfile CHANGED
@@ -37,6 +37,8 @@ end
37
37
  group :tinytds do
38
38
  if ENV['TINYTDS_SOURCE']
39
39
  gem 'tiny_tds', path: ENV['TINYTDS_SOURCE']
40
+ elsif ENV['TINYTDS_VERSION']
41
+ gem 'tiny_tds', ENV['TINYTDS_VERSION']
40
42
  else
41
43
  gem 'tiny_tds'
42
44
  end
@@ -46,3 +48,10 @@ group :odbc do
46
48
  gem 'ruby-odbc'
47
49
  end
48
50
 
51
+ group :development do
52
+ gem 'guard'
53
+ gem 'guard-minitest'
54
+ gem 'mocha'
55
+ gem 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint.
56
+ gem 'minitest-spec-rails'
57
+ end
data/README.md CHANGED
@@ -25,36 +25,50 @@ Account.execute_procedure :update_totals, named: 'params'
25
25
 
26
26
  #### Native Data Type Support
27
27
 
28
- We support every data type supported by FreeTDS and then a few more. All simplified Rails types in migrations will coorespond to a matching SQL Server national data type. Here is a basic chart. Always check the `initialize_native_database_types` method for an updated list.
28
+ We support every data type supported by FreeTDS and then a few more. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Here is a basic chart. Always check the `initialize_native_database_types` method for an updated list.
29
29
 
30
30
  ```ruby
31
- integer: { name: 'int', limit: 4 }
32
- bigint: { name: 'bigint' }
33
- boolean: { name: 'bit' }
34
- decimal: { name: 'decimal' }
35
- money: { name: 'money' }
36
- smallmoney: { name: 'smallmoney' }
37
- float: { name: 'float' }
38
- real: { name: 'real' }
39
- date: { name: 'date' }
40
- datetime: { name: 'datetime' }
41
- timestamp: { name: 'datetime' }
42
- time: { name: 'time' }
43
- char: { name: 'char' }
44
- varchar: { name: 'varchar', limit: 8000 }
45
- varchar_max: { name: 'varchar(max)' }
46
- text_basic: { name: 'text' }
47
- nchar: { name: 'nchar' }
48
- string: { name: 'nvarchar', limit: 4000 }
49
- text: { name: 'nvarchar(max)' }
50
- ntext: { name: 'ntext' }
51
- binary_basic: { name: 'binary' }
52
- varbinary: { name: 'varbinary', limit: 8000 }
53
- binary: { name: 'varbinary(max)' }
54
- uuid: { name: 'uniqueidentifier' }
55
- ss_timestamp: { name: 'timestamp' }
31
+ integer: { name: 'int', limit: 4 }
32
+ bigint: { name: 'bigint' }
33
+ boolean: { name: 'bit' }
34
+ decimal: { name: 'decimal' }
35
+ money: { name: 'money' }
36
+ smallmoney: { name: 'smallmoney' }
37
+ float: { name: 'float' }
38
+ real: { name: 'real' }
39
+ date: { name: 'date' }
40
+ datetime: { name: 'datetime' }
41
+ datetime2: { name: 'datetime2', precision: 7 }
42
+ datetimeoffset: { name: 'datetimeoffset', precision: 7 }
43
+ smalldatetime: { name: 'smalldatetime' }
44
+ timestamp: { name: 'datetime' }
45
+ time: { name: 'time' }
46
+ char: { name: 'char' }
47
+ varchar: { name: 'varchar', limit: 8000 }
48
+ varchar_max: { name: 'varchar(max)' }
49
+ text_basic: { name: 'text' }
50
+ nchar: { name: 'nchar' }
51
+ string: { name: 'nvarchar', limit: 4000 }
52
+ text: { name: 'nvarchar(max)' }
53
+ ntext: { name: 'ntext' }
54
+ binary_basic: { name: 'binary' }
55
+ varbinary: { name: 'varbinary', limit: 8000 }
56
+ binary: { name: 'varbinary(max)' }
57
+ uuid: { name: 'uniqueidentifier' }
58
+ ss_timestamp: { name: 'timestamp' }
56
59
  ```
57
60
 
61
+ The following types require TDS version 7.3 with TinyTDS. This requires the latest FreeTDS v0.95 or higher.
62
+
63
+ * date
64
+ * datetime2
65
+ * datetimeoffset
66
+ * time
67
+
68
+ Set `tds_version` in your database.yml or the `TDSVER` environment variable to `7.3` to ensure you are using the proper protocol version till 7.3 becomes the default.
69
+
70
+ **Zone Conversion** - The `[datetimeoffset]` type is the only ActiveRecord time based datatype that does not cast the zone to ActiveRecord's default - typically UTC. As intended, this datatype is meant to maintain the zone you pass to it and/or retreived from the database.
71
+
58
72
 
59
73
  #### Force Schema To Lowercase
60
74
 
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 4.2.8
@@ -17,14 +17,4 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ['lib']
19
19
  spec.add_dependency 'activerecord', '~> 4.2.1'
20
- spec.add_development_dependency 'bundler'
21
- spec.add_development_dependency 'guard'
22
- spec.add_development_dependency 'guard-minitest'
23
- spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint.
24
- spec.add_development_dependency 'minitest-focus'
25
- spec.add_development_dependency 'minitest-spec-rails'
26
- spec.add_development_dependency 'mocha'
27
- spec.add_development_dependency 'nokogiri'
28
- spec.add_development_dependency 'byebug'
29
- spec.add_development_dependency 'rake'
30
20
  end
@@ -1,13 +1,14 @@
1
- version: 4.2.5.{build}
2
1
  init:
3
2
  - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
3
  - SET PATH=C:\MinGW\msys\1.0\bin;%PATH%
5
4
  - SET RAKEOPT=-rdevkit
5
+ - SET TINYTDS_VERSION=0.9.5.rc.3
6
6
  clone_depth: 5
7
7
  skip_tags: true
8
8
  matrix:
9
9
  fast_finish: true
10
10
  install:
11
+ - ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number"
11
12
  - ruby --version
12
13
  - gem --version
13
14
  - bundle install --without odbc
@@ -19,12 +20,23 @@ test_script:
19
20
  - timeout /t 4 /nobreak > NUL
20
21
  - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql
21
22
  - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014"
23
+ - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" ACTIVERECORD_UNITTEST_TDSVERSION="7.3"
22
24
  - ps: Stop-Service 'MSSQL$SQL2014'
23
25
  - ps: Start-Service 'MSSQL$SQL2012SP1'
24
26
  - timeout /t 4 /nobreak > NUL
25
27
  - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql
26
28
  - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1"
29
+ - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3"
30
+ # - timeout /t 4 /nobreak > NUL
31
+ # - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1
32
+ # - timeout /t 4 /nobreak > NUL
33
+ # - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 TDSVER=7.3
27
34
  environment:
35
+ CI_AZURE_HOST:
36
+ secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg=
37
+ CI_AZURE_PASS:
38
+ secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w=
28
39
  matrix:
29
- - ruby_version: "200-x64"
30
- - ruby_version: "21-x64"
40
+ - ruby_version: "200"
41
+ - ruby_version: "22"
42
+ - ruby_version: "22-x64"
@@ -179,8 +179,8 @@ module ActiveRecord
179
179
  if sqlserver_azure?
180
180
  sql = %(SELECT CASE [transaction_isolation_level]
181
181
  WHEN 0 THEN NULL
182
- WHEN 1 THEN 'READ UNCOMITTED'
183
- WHEN 2 THEN 'READ COMITTED'
182
+ WHEN 1 THEN 'READ UNCOMMITTED'
183
+ WHEN 2 THEN 'READ COMMITTED'
184
184
  WHEN 3 THEN 'REPEATABLE READ'
185
185
  WHEN 4 THEN 'SERIALIZABLE'
186
186
  WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level]
@@ -216,7 +216,7 @@ module ActiveRecord
216
216
  end
217
217
 
218
218
  def sql_for_insert(sql, pk, id_value, sequence_name, binds)
219
- sql = if pk && self.class.use_output_inserted
219
+ sql = if pk && self.class.use_output_inserted && !database_prefix_remote_server?
220
220
  quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
221
221
  sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
222
222
  else
@@ -5,17 +5,9 @@ module ActiveRecord
5
5
 
6
6
  def create_database(database, options = {})
7
7
  name = SQLServer::Utils.extract_identifiers(database)
8
- options = {collation: @connection_options[:collation]}.merge!(options.symbolize_keys)
9
- options = options.select { |_, v| v.present? }
10
- option_string = options.inject("") do |memo, (key, value)|
11
- memo += case key
12
- when :collation
13
- " COLLATE #{value}"
14
- else
15
- ""
16
- end
17
- end
18
- do_execute "CREATE DATABASE #{name}#{option_string}"
8
+ db_options = create_database_options(options)
9
+ edition_options = create_database_edition_options(options)
10
+ do_execute "CREATE DATABASE #{name} #{db_options} #{edition_options}"
19
11
  end
20
12
 
21
13
  def drop_database(database)
@@ -35,6 +27,38 @@ module ActiveRecord
35
27
  select_value "SELECT DATABASEPROPERTYEX(DB_NAME(), 'Collation')"
36
28
  end
37
29
 
30
+
31
+ private
32
+
33
+ def create_database_options(options={})
34
+ keys = [:collate]
35
+ copts = @connection_options
36
+ options = {
37
+ collate: copts[:collation]
38
+ }.merge(options.symbolize_keys).select { |_, v|
39
+ v.present?
40
+ }.slice(*keys).map { |k,v|
41
+ "#{k.to_s.upcase} #{v}"
42
+ }.join(' ')
43
+ options
44
+ end
45
+
46
+ def create_database_edition_options(options={})
47
+ keys = [:maxsize, :edition, :service_objective]
48
+ copts = @connection_options
49
+ edition_options = {
50
+ maxsize: copts[:azure_maxsize],
51
+ edition: copts[:azure_edition],
52
+ service_objective: copts[:azure_service_objective]
53
+ }.merge(options.symbolize_keys).select { |_, v|
54
+ v.present?
55
+ }.slice(*keys).map { |k,v|
56
+ "#{k.to_s.upcase} = #{v}"
57
+ }.join(', ')
58
+ edition_options = "( #{edition_options} )" if edition_options.present?
59
+ edition_options
60
+ end
61
+
38
62
  end
39
63
  end
40
64
  end
@@ -40,15 +40,10 @@ module ActiveRecord
40
40
  end
41
41
 
42
42
  def quoted_date(value)
43
- SQLServer::Utils.with_sqlserver_db_date_formats do
44
- if value.acts_like?(:time) && value.respond_to?(:usec)
45
- precision = (BigDecimal(value.usec.to_s) / 1_000_000).round(3).to_s.split('.').last
46
- "#{super}.#{precision}"
47
- elsif value.acts_like?(:date)
48
- value.to_s(:_sqlserver_dateformat)
49
- else
50
- super
51
- end
43
+ if value.acts_like?(:date)
44
+ Type::Date.new.type_cast_for_database(value)
45
+ else value.acts_like?(:time)
46
+ Type::DateTime.new.type_cast_for_database(value)
52
47
  end
53
48
  end
54
49
 
@@ -59,8 +54,6 @@ module ActiveRecord
59
54
  case value
60
55
  when Type::Binary::Data
61
56
  "0x#{value.hex}"
62
- when SQLServer::Type::Quoter
63
- value.quote_ss_value
64
57
  when String, ActiveSupport::Multibyte::Chars
65
58
  if value.is_utf8?
66
59
  "#{QUOTED_STRING_PREFIX}#{super}"
@@ -72,11 +65,6 @@ module ActiveRecord
72
65
  end
73
66
  end
74
67
 
75
- def quoted_value_acts_like_time_filter(value)
76
- zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
77
- value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
78
- end
79
-
80
68
  end
81
69
  end
82
70
  end
@@ -32,12 +32,13 @@ module ActiveRecord
32
32
  else
33
33
  name = index[:index_name]
34
34
  unique = index[:index_description] =~ /unique/
35
+ where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}")
35
36
  columns = index[:index_keys].split(',').map do |column|
36
37
  column.strip!
37
38
  column.gsub! '(-)', '' if column.ends_with?('(-)')
38
39
  column
39
40
  end
40
- indexes << IndexDefinition.new(table_name, name, unique, columns)
41
+ indexes << IndexDefinition.new(table_name, name, unique, columns, nil, nil, where)
41
42
  end
42
43
  end
43
44
  end
@@ -201,6 +202,9 @@ module ActiveRecord
201
202
  real: { name: 'real' },
202
203
  date: { name: 'date' },
203
204
  datetime: { name: 'datetime' },
205
+ datetime2: { name: 'datetime2' },
206
+ datetimeoffset: { name: 'datetimeoffset' },
207
+ smalldatetime: { name: 'smalldatetime' },
204
208
  timestamp: { name: 'datetime' },
205
209
  time: { name: 'time' },
206
210
  char: { name: 'char' },
@@ -220,7 +224,11 @@ module ActiveRecord
220
224
  end
221
225
 
222
226
  def column_definitions(table_name)
223
- identifier = SQLServer::Utils.extract_identifiers(table_name)
227
+ identifier = if database_prefix_remote_server?
228
+ SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}")
229
+ else
230
+ SQLServer::Utils.extract_identifiers(table_name)
231
+ end
224
232
  database = identifier.fully_qualified_database_quoted
225
233
  view_exists = schema_cache.view_exists?(table_name)
226
234
  view_tblnm = table_name_or_views_table_name(table_name) if view_exists
@@ -282,6 +290,8 @@ module ActiveRecord
282
290
  ci[:type] = case ci[:type]
283
291
  when /^bit|image|text|ntext|datetime$/
284
292
  ci[:type]
293
+ when /^datetime2|datetimeoffset$/i
294
+ "#{ci[:type]}(#{ci[:datetime_precision]})"
285
295
  when /^time$/i
286
296
  "#{ci[:type]}(#{ci[:datetime_precision]})"
287
297
  when /^numeric|decimal$/i
@@ -18,6 +18,14 @@ module ActiveRecord
18
18
  column(name, :money, options)
19
19
  end
20
20
 
21
+ def datetime2(name, options = {})
22
+ column(name, :datetime2, options)
23
+ end
24
+
25
+ def datetimeoffset(name, options = {})
26
+ column(name, :datetimeoffset, options)
27
+ end
28
+
21
29
  def smallmoney(name, options = {})
22
30
  column(name, :smallmoney, options)
23
31
  end
@@ -1,5 +1,4 @@
1
1
  require 'active_record/type'
2
- require 'active_record/connection_adapters/sqlserver/type/quoter.rb'
3
2
  # Exact Numerics
4
3
  require 'active_record/connection_adapters/sqlserver/type/integer.rb'
5
4
  require 'active_record/connection_adapters/sqlserver/type/big_integer.rb'
@@ -13,8 +12,11 @@ require 'active_record/connection_adapters/sqlserver/type/small_money.rb'
13
12
  require 'active_record/connection_adapters/sqlserver/type/float.rb'
14
13
  require 'active_record/connection_adapters/sqlserver/type/real.rb'
15
14
  # Date and Time
15
+ require 'active_record/connection_adapters/sqlserver/type/time_value_fractional.rb'
16
16
  require 'active_record/connection_adapters/sqlserver/type/date.rb'
17
17
  require 'active_record/connection_adapters/sqlserver/type/datetime.rb'
18
+ require 'active_record/connection_adapters/sqlserver/type/datetime2.rb'
19
+ require 'active_record/connection_adapters/sqlserver/type/datetimeoffset.rb'
18
20
  require 'active_record/connection_adapters/sqlserver/type/smalldatetime.rb'
19
21
  require 'active_record/connection_adapters/sqlserver/type/time.rb'
20
22
  # Character Strings
@@ -4,6 +4,15 @@ module ActiveRecord
4
4
  module Type
5
5
  class Date < ActiveRecord::Type::Date
6
6
 
7
+ def type_cast_for_database(value)
8
+ return unless value.present?
9
+ return value if value.acts_like?(:string)
10
+ value.to_s(:_sqlserver_dateformat)
11
+ end
12
+
13
+ def type_cast_for_schema(value)
14
+ type_cast_for_database(value).inspect
15
+ end
7
16
 
8
17
  end
9
18
  end
@@ -4,28 +4,34 @@ module ActiveRecord
4
4
  module Type
5
5
  class DateTime < ActiveRecord::Type::DateTime
6
6
 
7
+ include TimeValueFractional
8
+
9
+ def type_cast_for_database(value)
10
+ return super unless value.acts_like?(:time)
11
+ value = zone_conversion(value)
12
+ datetime = value.to_s(:_sqlserver_datetime)
13
+ "#{datetime}".tap do |v|
14
+ fraction = quote_fractional(value)
15
+ v << ".#{fraction}" unless fraction.to_i.zero?
16
+ end
17
+ end
18
+
7
19
  def type_cast_for_schema(value)
8
- value.acts_like?(:string) ? "'#{value}'" : super
20
+ type_cast_for_database(value).inspect
9
21
  end
10
22
 
11
23
 
12
24
  private
13
25
 
14
26
  def cast_value(value)
15
- value = value.respond_to?(:usec) ? value : super
27
+ value = value.acts_like?(:time) ? value : super
16
28
  return unless value
17
- value.change usec: cast_usec(value)
18
- end
19
-
20
- def cast_usec(value)
21
- return 0 if !value.respond_to?(:usec) || value.usec.zero?
22
- seconds = value.usec.to_f / 1_000_000.0
23
- ss_seconds = ((seconds * (1 / second_precision)).round / (1 / second_precision)).round(3)
24
- (ss_seconds * 1_000_000).to_i
29
+ cast_fractional(value)
25
30
  end
26
31
 
27
- def second_precision
28
- 0.00333
32
+ def zone_conversion(value)
33
+ method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
34
+ value.respond_to?(method) ? value.send(method) : value
29
35
  end
30
36
 
31
37
  end