activerecord-sqlserver-adapter 4.2.6 → 4.2.8

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