activerecord-sqlserver-adapter 2.3.1 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,6 +1,18 @@
1
1
 
2
2
  MASTER
3
3
 
4
+ 2.3.2
5
+
6
+ * Insert queries that include the word "insert" as a partial column name with the word
7
+ "id" as a value were falsely being matched as identity inserts. [Sean Caffery/bfabry]
8
+
9
+ * Delegate all low level #raw_connection calls to #raw_connection_run and #raw_connection_do
10
+ which abstract out the low level modes in the connection options at that point. [Ken Collins]
11
+
12
+ * Remove DBI dependency and go straight ODBC for speed improvement [Erik Bryn]
13
+
14
+ * Leave order by alone when same column crosses two tables [Ransom Briggs]
15
+
4
16
 
5
17
  * 2.3 * (December 1st, 2009)
6
18
 
data/README.rdoc CHANGED
@@ -6,6 +6,7 @@ The SQL Server adapter for rails is back for ActiveRecord 2.2 and up! We are cur
6
6
 
7
7
  == What's New
8
8
 
9
+ * Strict ODBC required! No DBI means around 20% faster!
9
10
  * Now supports SQL Server 2008 too!
10
11
  * Fully tested under 1.9!!! Correctly encodes/decodes UTF-8 types in ruby 1.9 too.
11
12
  * Now supports both rails 2.2 & 2.3!!!
@@ -128,24 +129,16 @@ It is our goal to match the adapter version with each version of rails. However
128
129
 
129
130
  == Installation
130
131
 
131
- First, you will need Ruby DBI and Ruby ODBC. If you are using the adapter under 1.9, then you need at least ruby-odbc version 0.9996. To my knowledge the ADO DBD for DBI is no longer supported. The installation below is not a comprehensive walk thru on how to get all the required moving parts like FreeTDS installed and/or configured. It will also assume gem installations of both the dependent libraries and the adapter itself.
132
+ You will need Ruby ODBC. If you are using the adapter under 1.9, then you need at least ruby-odbc version 0.9996. Currently ADO modes are not supported since we dropped the unnecessary DBI dependency and transport layer. This was done so we could incorporate other transports such as ADO.NET (w IronRuby) mode in the future or possibly a straight FreeTDS layer. The sky is the limit now and we have a code that can be accept these optional transports. If you are interested in helping, open a ticket and submit a patch. Or start a conversation on the Google Group.
132
133
 
133
- It should be noted that this version of the adapter was developed using both the ancient 0.0.23 version of DBI up to the current stable release of 0.4.1. Note that DBI 0.4.1 is the minimal for ruby 1.9 compatibility. Because later versions of DBI will be changing many things, IT IS HIGHLY NECESSARY that you max your install to version 0.4.1 which the examples below show. For the time being we are not supporting DBI versions higher than 0.4.1 this they settle down on new internal implementations.
134
-
135
- $ gem install dbi --version 0.4.1
136
- $ gem install dbd-odbc --version 0.2.4
137
134
  $ gem install activerecord-sqlserver-adapter
138
135
 
139
136
  Optionally configure your gem dependencies in your rails environment.rb file.
140
137
 
141
- config.gem 'dbi', :version => '0.4.1'
142
- config.gem 'dbd-odbc', :version => '0.2.4', :lib => 'dbd/ODBC'
143
- config.gem 'activerecord-sqlserver-adapter', :version => 'x.x.xx',
144
- :lib => 'active_record/connection_adapters/sqlserver_adapter'
138
+ config.gem 'activerecord-sqlserver-adapter', :version => 'x.x.xx'
145
139
 
146
140
  Here are some external links for libraries and/or tutorials on how to install any other additional components to use this adapter. If you know of a good one that we can include here, just let us know.
147
141
 
148
- * http://ruby-dbi.sourceforge.net/
149
142
  * http://www.ch-werner.de/rubyodbc/
150
143
 
151
144
 
@@ -1,6 +1,5 @@
1
+ require 'active_record'
1
2
  require 'active_record/connection_adapters/abstract_adapter'
2
- require_library_or_gem 'dbi' unless defined?(DBI)
3
- require 'active_record/connection_adapters/sqlserver_adapter/core_ext/dbi'
4
3
  require 'active_record/connection_adapters/sqlserver_adapter/core_ext/active_record'
5
4
  require 'base64'
6
5
 
@@ -9,21 +8,22 @@ module ActiveRecord
9
8
  class Base
10
9
 
11
10
  def self.sqlserver_connection(config) #:nodoc:
12
- config.symbolize_keys!
13
- mode = config[:mode] ? config[:mode].to_s.upcase : 'ADO'
14
- username = config[:username] ? config[:username].to_s : 'sa'
15
- password = config[:password] ? config[:password].to_s : ''
16
- if mode == "ODBC"
17
- raise ArgumentError, "Missing DSN. Argument ':dsn' must be set in order for this adapter to work." unless config.has_key?(:dsn)
18
- dsn = config[:dsn]
19
- driver_url = "DBI:ODBC:#{dsn}"
11
+ config = config.dup.symbolize_keys!
12
+ config.reverse_merge! :mode => :odbc, :host => 'localhost', :username => 'sa', :password => ''
13
+ mode = config[:mode].to_s.downcase.underscore.to_sym
14
+ case mode
15
+ when :odbc
16
+ require_library_or_gem 'odbc' unless defined?(ODBC)
17
+ require 'active_record/connection_adapters/sqlserver_adapter/core_ext/odbc'
18
+ raise ArgumentError, 'Missing :dsn configuration.' unless config.has_key?(:dsn)
19
+ config = config.slice :dsn, :username, :password
20
+ when :ado
21
+ raise NotImplementedError, 'Please use version 2.3.1 of the adapter for ADO connections. Future versions may support ADO.NET.'
22
+ raise ArgumentError, 'Missing :database configuration.' unless config.has_key?(:database)
20
23
  else
21
- raise ArgumentError, "Missing Database. Argument ':database' must be set in order for this adapter to work." unless config.has_key?(:database)
22
- database = config[:database]
23
- host = config[:host] ? config[:host].to_s : 'localhost'
24
- driver_url = "DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User ID=#{username};Password=#{password};"
24
+ raise ArgumentError, "Unknown connection mode in #{config.inspect}."
25
25
  end
26
- ConnectionAdapters::SQLServerAdapter.new(logger, [driver_url, username, password])
26
+ ConnectionAdapters::SQLServerAdapter.new(logger,config.merge(:mode=>mode))
27
27
  end
28
28
 
29
29
  protected
@@ -147,53 +147,30 @@ module ActiveRecord
147
147
 
148
148
  end #SQLServerColumn
149
149
 
150
- # In ADO mode, this adapter will ONLY work on Windows systems, since it relies on
151
- # Win32OLE, which, to my knowledge, is only available on Windows.
152
- #
153
- # This mode also relies on the ADO support in the DBI module. If you are using the
154
- # one-click installer of Ruby, then you already have DBI installed, but the ADO module
155
- # is *NOT* installed. You will need to get the latest source distribution of Ruby-DBI
156
- # from http://ruby-dbi.sourceforge.net/ unzip it, and copy the file from
157
- # <tt>src/lib/dbd_ado/ADO.rb</tt> to <tt>X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb</tt>
158
- #
159
- # You will more than likely need to create the ADO directory. Once you've installed
160
- # that file, you are ready to go.
161
- #
162
- # In ODBC mode, the adapter requires the ODBC support in the DBI module which requires
163
- # the Ruby ODBC module. Ruby ODBC 0.996 was used in development and testing,
164
- # and it is available at http://www.ch-werner.de/rubyodbc/
150
+ # In ODBC mode, the adapter requires Ruby ODBC and requires that you specify
151
+ # a :dsn option. Ruby ODBC is available at http://www.ch-werner.de/rubyodbc/
165
152
  #
166
153
  # Options:
167
154
  #
168
- # * <tt>:mode</tt> -- ADO or ODBC. Defaults to ADO.
169
155
  # * <tt>:username</tt> -- Defaults to sa.
170
- # * <tt>:password</tt> -- Defaults to empty string.
171
- # * <tt>:windows_auth</tt> -- Defaults to "User ID=#{username};Password=#{password}"
172
- #
173
- # ADO specific options:
174
- #
175
- # * <tt>:host</tt> -- Defaults to localhost.
176
- # * <tt>:database</tt> -- The name of the database. No default, must be provided.
177
- # * <tt>:windows_auth</tt> -- Use windows authentication instead of username/password.
178
- #
179
- # ODBC specific options:
180
- #
181
- # * <tt>:dsn</tt> -- Defaults to nothing.
156
+ # * <tt>:password</tt> -- Defaults to blank string.
157
+ # * <tt>:dsn</tt> -- An ODBC DSN. (required)
182
158
  #
183
159
  class SQLServerAdapter < AbstractAdapter
184
160
 
185
- ADAPTER_NAME = 'SQLServer'.freeze
186
- VERSION = '2.3'.freeze
187
- DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+(\d{4})/
188
- SUPPORTED_VERSIONS = [2000,2005,2008].freeze
189
- LIMITABLE_TYPES = ['string','integer','float','char','nchar','varchar','nvarchar'].freeze
190
-
191
- LOST_CONNECTION_EXCEPTIONS = [DBI::DatabaseError, DBI::InterfaceError]
192
- LOST_CONNECTION_MESSAGES = [
193
- 'Communication link failure',
194
- 'Read from the server failed',
195
- 'Write to the server failed',
196
- 'Database connection was already closed']
161
+ ADAPTER_NAME = 'SQLServer'.freeze
162
+ VERSION = '2.3.2'.freeze
163
+ DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+(\d{4})/
164
+ SUPPORTED_VERSIONS = [2000,2005,2008].freeze
165
+ LIMITABLE_TYPES = ['string','integer','float','char','nchar','varchar','nvarchar'].freeze
166
+ LOST_CONNECTION_EXCEPTIONS = {
167
+ :odbc => ['ODBC::Error'],
168
+ :ado => []
169
+ }
170
+ LOST_CONNECTION_MESSAGES = {
171
+ :odbc => [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i],
172
+ :ado => []
173
+ }
197
174
 
198
175
  cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
199
176
  :log_info_schema_queries, :enable_default_unicode_types, :auto_connect
@@ -206,8 +183,8 @@ module ActiveRecord
206
183
 
207
184
  end
208
185
 
209
- def initialize(logger, connection_options)
210
- @connection_options = connection_options
186
+ def initialize(logger,config)
187
+ @connection_options = config
211
188
  connect
212
189
  super(raw_connection, logger)
213
190
  initialize_sqlserver_caches
@@ -349,9 +326,9 @@ module ActiveRecord
349
326
  # CONNECTION MANAGEMENT ====================================#
350
327
 
351
328
  def active?
352
- raw_connection.execute("SELECT 1").finish
329
+ raw_connection_do("SELECT 1")
353
330
  true
354
- rescue *LOST_CONNECTION_EXCEPTIONS
331
+ rescue *lost_connection_exceptions
355
332
  false
356
333
  end
357
334
 
@@ -365,8 +342,28 @@ module ActiveRecord
365
342
  raw_connection.disconnect rescue nil
366
343
  end
367
344
 
345
+ def raw_connection_run(sql)
346
+ with_auto_reconnect do
347
+ case connection_mode
348
+ when :odbc
349
+ block_given? ? raw_connection.run_block(sql) { |handle| yield(handle) } : raw_connection.run(sql)
350
+ else :ado
351
+
352
+ end
353
+ end
354
+ end
355
+
356
+ def raw_connection_do(sql)
357
+ case connection_mode
358
+ when :odbc
359
+ raw_connection.do(sql)
360
+ else :ado
361
+
362
+ end
363
+ end
364
+
368
365
  def finish_statement_handle(handle)
369
- handle.finish if handle && handle.respond_to?(:finish) && !handle.finished?
366
+ handle.drop if handle && handle.respond_to?(:drop) && !handle.finished?
370
367
  handle
371
368
  end
372
369
 
@@ -771,22 +768,35 @@ module ActiveRecord
771
768
  # CONNECTION MANAGEMENT ====================================#
772
769
 
773
770
  def connect
774
- driver_url, username, password = @connection_options
775
- @connection = DBI.connect(driver_url, username, password)
776
- configure_connection
771
+ config = @connection_options
772
+ @connection = case connection_mode
773
+ when :odbc
774
+ ODBC.connect config[:dsn], config[:username], config[:password]
775
+ when :ado
776
+
777
+ end
777
778
  rescue
778
779
  raise unless @auto_connecting
779
780
  end
780
781
 
781
- def configure_connection
782
- raw_connection['AutoCommit'] = true
782
+ def connection_mode
783
+ @connection_options[:mode]
784
+ end
785
+
786
+ def lost_connection_exceptions
787
+ exceptions = LOST_CONNECTION_EXCEPTIONS[connection_mode]
788
+ @lost_connection_exceptions ||= exceptions ? exceptions.map(&:constantize) : []
789
+ end
790
+
791
+ def lost_connection_messages
792
+ LOST_CONNECTION_MESSAGES[connection_mode]
783
793
  end
784
794
 
785
795
  def with_auto_reconnect
786
796
  begin
787
797
  yield
788
- rescue *LOST_CONNECTION_EXCEPTIONS => e
789
- if LOST_CONNECTION_MESSAGES.any? { |lcm| e.message =~ Regexp.new(lcm,Regexp::IGNORECASE) }
798
+ rescue *lost_connection_exceptions => e
799
+ if lost_connection_messages.any? { |lcm| e.message =~ lcm }
790
800
  retry if auto_reconnected?
791
801
  end
792
802
  raise
@@ -837,36 +847,22 @@ module ActiveRecord
837
847
  end
838
848
 
839
849
  def raw_execute(sql, name = nil, &block)
840
- log(sql, name) do
841
- if block_given?
842
- with_auto_reconnect { raw_connection.execute(sql) { |handle| yield(handle) } }
843
- else
844
- with_auto_reconnect { raw_connection.execute(sql) }
845
- end
846
- end
847
- end
848
-
849
- def without_type_conversion
850
- raw_connection.convert_types = false if raw_connection.respond_to?(:convert_types=)
851
- yield
852
- ensure
853
- raw_connection.convert_types = true if raw_connection.respond_to?(:convert_types=)
850
+ log(sql,name) { raw_connection_run(sql) }
854
851
  end
855
852
 
856
853
  def do_execute(sql,name=nil)
857
854
  log(sql, name || 'EXECUTE') do
858
- with_auto_reconnect { raw_connection.do(sql) }
855
+ with_auto_reconnect { raw_connection_do(sql) }
859
856
  end
860
857
  end
861
858
 
862
859
  def raw_select(sql, name = nil)
863
860
  handle = raw_execute(sql,name)
864
- fields = handle.column_names
861
+ fields = handle.columns(true).map{|c|c.name}
865
862
  results = handle_as_array(handle)
866
863
  rows = results.inject([]) do |rows,row|
867
864
  row.each_with_index do |value, i|
868
- # DEPRECATED in DBI 0.4.0 and above. Remove when 0.2.2 and lower is no longer supported.
869
- if value.is_a? DBI::Timestamp
865
+ if value.is_a? ODBC::TimeStamp
870
866
  row[i] = value.to_sqlserver_string
871
867
  end
872
868
  end
@@ -946,7 +942,7 @@ module ActiveRecord
946
942
  if insert_sql?(sql)
947
943
  table_name = get_table_name(sql)
948
944
  id_column = identity_column(table_name)
949
- id_column && sql =~ /INSERT[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
945
+ id_column && sql =~ /^\s*INSERT[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
950
946
  else
951
947
  false
952
948
  end
@@ -1053,7 +1049,7 @@ module ActiveRecord
1053
1049
  WHERE columns.TABLE_NAME = '#{table_name}'
1054
1050
  ORDER BY columns.ordinal_position
1055
1051
  }.gsub(/[ \t\r\n]+/,' ')
1056
- results = info_schema_query { without_type_conversion{ select(sql,nil,true) } }
1052
+ results = info_schema_query { select(sql,nil,true) }
1057
1053
  results.collect do |ci|
1058
1054
  ci.symbolize_keys!
1059
1055
  ci[:type] = case ci[:type]
@@ -1070,7 +1066,7 @@ module ActiveRecord
1070
1066
  real_table_name = table_name_or_views_table_name(table_name)
1071
1067
  real_column_name = views_real_column_name(table_name,ci[:name])
1072
1068
  col_default_sql = "SELECT c.COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'"
1073
- ci[:default_value] = info_schema_query { without_type_conversion{ select_value(col_default_sql) } }
1069
+ ci[:default_value] = info_schema_query { select_value(col_default_sql) }
1074
1070
  end
1075
1071
  ci[:default_value] = case ci[:default_value]
1076
1072
  when nil, '(null)', '(NULL)'
@@ -83,10 +83,11 @@ module ActiveRecord
83
83
  else
84
84
  [nil, ord_tn_and_cn.first]
85
85
  end
86
- if (ord_table_name && ord_table_name == select_table_name && unique_order_hash[ord_column_name]) || unique_order_hash[ord_column_name]
86
+ unique_key = [(ord_table_name || select_table_name), ord_column_name]
87
+ if unique_order_hash[unique_key]
87
88
  array
88
89
  else
89
- unique_order_hash[ord_column_name] = true
90
+ unique_order_hash[unique_key] = true
90
91
  array << "#{ord} #{dir}".strip
91
92
  end
92
93
  end.join(', ')
@@ -0,0 +1,41 @@
1
+
2
+ module ActiveRecord
3
+ module ConnectionAdapters
4
+ module SQLServerCoreExtensions
5
+ module ODBC
6
+
7
+ module TimeStamp
8
+ def to_sqlserver_string
9
+ date, time, nanoseconds = to_s.split(' ')
10
+ "#{date} #{time}.#{sprintf("%03d",nanoseconds.to_i/1000000)}"
11
+ end
12
+ end
13
+
14
+ module Statement
15
+ def finished?
16
+ begin
17
+ connected?
18
+ false
19
+ rescue ::ODBC::Error => e
20
+ true
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ module Database
27
+ def run_block(*args)
28
+ yield sth = run(*args)
29
+ sth.drop
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ ODBC::TimeStamp.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ODBC::TimeStamp if defined?(ODBC::TimeStamp)
39
+ ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ODBC::Statement if defined?(ODBC::Statement)
40
+ ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ODBC::Database if defined?(ODBC::Database)
41
+
@@ -218,6 +218,10 @@ class AdapterTestSqlserver < ActiveRecord::TestCase
218
218
  assert_equal 'MIN(comments.id) DESC, MIN(comments.post_id) ASC', order_to_min_set(@two_orders_with_desc_and_asc)
219
219
  end
220
220
 
221
+ should 'leave order by alone when same column crosses two tables' do
222
+ assert_equal ' ORDER BY developers.name, projects.name', add_order!('developers.name, projects.name')
223
+ end
224
+
221
225
  end
222
226
 
223
227
  context 'with different language' do
@@ -12,21 +12,21 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase
12
12
  end
13
13
 
14
14
 
15
- should 'return finished DBI statment handle from #execute without block' do
15
+ should 'return finished ODBC statment handle from #execute without block' do
16
16
  handle = @connection.execute('SELECT * FROM [topics]')
17
- assert_instance_of DBI::StatementHandle, handle
17
+ assert_instance_of ODBC::Statement, handle
18
18
  assert handle.finished?
19
19
  end
20
20
 
21
- should 'finish DBI statment handle from #execute with block' do
21
+ should 'finish ODBC statment handle from #execute with block' do
22
22
  assert_all_statements_used_are_closed do
23
23
  @connection.execute('SELECT * FROM [topics]') { }
24
24
  end
25
25
  end
26
26
 
27
- should 'return an unfinished DBI statement handler from #raw_execute' do
27
+ should 'return an unfinished ODBC statement handler from #raw_execute' do
28
28
  handle = @connection.send(:raw_execute,'SELECT * FROM [topics]')
29
- assert_instance_of DBI::StatementHandle, handle
29
+ assert_instance_of ODBC::Statement, handle
30
30
  assert !handle.finished?
31
31
  end
32
32
 
@@ -37,7 +37,6 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase
37
37
  end
38
38
 
39
39
  should 'affect rows' do
40
- assert Topic.connection.instance_variable_get("@connection")["AutoCommit"]
41
40
  topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
42
41
  updated = Topic.update(topic_data.keys, topic_data.values)
43
42
  assert_equal 2, updated.size
@@ -114,15 +113,15 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase
114
113
 
115
114
  def assert_all_statements_used_are_closed(&block)
116
115
  existing_handles = []
117
- ObjectSpace.each_object(DBI::StatementHandle) {|handle| existing_handles << handle}
116
+ ObjectSpace.each_object(ODBC::Statement) {|handle| existing_handles << handle}
118
117
  GC.disable
119
118
  yield
120
119
  used_handles = []
121
- ObjectSpace.each_object(DBI::StatementHandle) {|handle| used_handles << handle unless existing_handles.include? handle}
120
+ ObjectSpace.each_object(ODBC::Statement) {|handle| used_handles << handle unless existing_handles.include? handle}
122
121
  assert_block "No statements were used within given block" do
123
122
  used_handles.size > 0
124
123
  end
125
- ObjectSpace.each_object(DBI::StatementHandle) do |handle|
124
+ ObjectSpace.each_object(ODBC::Statement) do |handle|
126
125
  assert_block "Statement should have been closed within given block" do
127
126
  handle.finished?
128
127
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-sqlserver-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken Collins
@@ -13,7 +13,7 @@ autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
15
 
16
- date: 2009-09-10 00:00:00 -04:00
16
+ date: 2010-02-15 00:00:00 -05:00
17
17
  default_executable:
18
18
  dependencies: []
19
19
 
@@ -37,7 +37,7 @@ files:
37
37
  - lib/activerecord-sqlserver-adapter.rb
38
38
  - lib/active_record/connection_adapters/sqlserver_adapter.rb
39
39
  - lib/active_record/connection_adapters/sqlserver_adapter/core_ext/active_record.rb
40
- - lib/active_record/connection_adapters/sqlserver_adapter/core_ext/dbi.rb
40
+ - lib/active_record/connection_adapters/sqlserver_adapter/core_ext/odbc.rb
41
41
  has_rdoc: true
42
42
  homepage: http://github.com/rails-sqlserver
43
43
  licenses: []
@@ -1,106 +0,0 @@
1
-
2
- module ActiveRecord
3
- module ConnectionAdapters
4
- module SQLServerCoreExtensions
5
-
6
-
7
- module DBI
8
-
9
- module Timestamp
10
- # Deprecated DBI. See documentation for Type::SqlserverTimestamp which
11
- # this method tries to mimic as ODBC is still going to convert SQL Server
12
- # milliconds to whole number representation of nanoseconds.
13
- def to_sqlserver_string
14
- datetime, nanoseconds = to_s.split('.')
15
- "#{datetime}.#{sprintf("%03d",nanoseconds.to_i/1000000)}"
16
- end
17
- end
18
-
19
- module Type
20
-
21
- # Make sure we get DBI::Type::Timestamp returning a string NOT a time object
22
- # that represents what is in the DB before type casting while letting core
23
- # ActiveRecord do the reset. It is assumed that DBI is using ODBC connections
24
- # and that ODBC::Timestamp is taking the native milliseconds that SQL Server
25
- # stores and returning them incorrect using ODBC::Timestamp#fraction which is
26
- # nanoseconds. Below shows the incorrect ODBC::Timestamp represented by DBI
27
- # and the conversion we expect to have in the DB before type casting.
28
- #
29
- # "1985-04-15 00:00:00 0" # => "1985-04-15 00:00:00.000"
30
- # "2008-11-08 10:24:36 30000000" # => "2008-11-08 10:24:36.003"
31
- # "2008-11-08 10:24:36 123000000" # => "2008-11-08 10:24:36.123"
32
- class SqlserverTimestamp
33
- def self.parse(obj)
34
- return nil if ::DBI::Type::Null.parse(obj).nil?
35
- date, time, nanoseconds = obj.split(' ')
36
- "#{date} #{time}.#{sprintf("%03d",nanoseconds.to_i/1000000)}"
37
- end
38
- end
39
-
40
- # The adapter and rails will parse our floats, decimals, and money field correctly
41
- # from a string. Do not let the DBI::Type classes create Float/BigDecimal objects
42
- # for us. Trust rails .type_cast to do what it is built to do.
43
- class SqlserverForcedString
44
- def self.parse(obj)
45
- return nil if ::DBI::Type::Null.parse(obj).nil?
46
- obj.to_s
47
- end
48
- end
49
-
50
- # We want our true 1 to 255 tinyint range.
51
- class SqlserverForcedTinyint
52
- def self.parse(obj)
53
- return nil if ::DBI::Type::Null.parse(obj).nil?
54
- obj.to_i
55
- end
56
- end
57
-
58
- end
59
-
60
- module TypeUtil
61
-
62
- def self.included(klass)
63
- klass.extend ClassMethods
64
- class << klass
65
- alias_method_chain :type_name_to_module, :sqlserver_types
66
- end
67
- end
68
-
69
- module ClassMethods
70
-
71
- # Capture all types classes that we need to handle directly for SQL Server
72
- # and allow normal processing for those that we do not.
73
- def type_name_to_module_with_sqlserver_types(type_name)
74
- case type_name
75
- when /^timestamp$/i
76
- DBI::Type::SqlserverTimestamp
77
- when /^float|decimal|money$/i
78
- DBI::Type::SqlserverForcedString
79
- when /^tinyint$/i
80
- DBI::Type::SqlserverForcedTinyint
81
- else
82
- type_name_to_module_without_sqlserver_types(type_name)
83
- end
84
- end
85
-
86
- end
87
-
88
- end
89
-
90
- end
91
-
92
-
93
- end
94
- end
95
- end
96
-
97
-
98
-
99
-
100
- if defined?(DBI::TypeUtil)
101
- DBI::Type.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::DBI::Type
102
- DBI::TypeUtil.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::DBI::TypeUtil
103
- elsif defined?(DBI::Timestamp) # DEPRECATED in DBI 0.4.0 and above. Remove when 0.2.2 and lower is no longer supported.
104
- DBI::Timestamp.send :include, ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::DBI::Timestamp
105
- end
106
-