activerecord-sqlserver-adapter 3.1.0.rc2 → 3.1.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,6 +1,19 @@
1
1
 
2
2
  * 3.1.0 *
3
3
 
4
+ * Quote most time objects to use ISO8601 format to be multi-language dateformat compatible. The [datetime] data type is
5
+ automatically limited to milliseconds while [time] & [datetimeoffset] have full support. Even included a Date/Time
6
+ ActiveSupport formatter that is used per the language settings of the connection.
7
+
8
+ * Include a visit_Arel_Nodes_UpdateStatement method in our Arel visitor to add a limit/top for update
9
+ that has order and no limit/top. https://github.com/rails/rails/commit/787194ee43ab1fb0a7dc8bfbbfbd5079b047d833
10
+
11
+ * Allow drop_database to be called even when DB does not exist.
12
+
13
+ * Remove totally broken ADONET connection mode. Want it back, submit a patch.
14
+
15
+ * Schema reflection now finds primary key for all occasions. Fixed #60 [Boško Ivanišević]
16
+
4
17
  * Allow complex order objects to not be molested by our visitor overrides. Fixes #99
5
18
 
6
19
  * Default unicode datatypes!
@@ -1,15 +1,16 @@
1
1
 
2
2
  == SQL Server 2005/2008 & Azure Adapter For ActiveRecord
3
3
 
4
- The SQL Server adapter for ActiveRecord.
4
+ The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2000, you are still in the right spot. Just install the latest 2.3.x version of the adapter. Note, we follow a rational versioning policy that tracks ActiveRecord. That means that our 2.3.x version of the adapter is only for the latest 2.3 version of Rails.
5
5
 
6
6
 
7
7
  == What's New
8
8
 
9
9
  * Rails 3.1 prepared statement support leverages cached query plans.
10
+ * We now support your native language date/time formats automatically!
10
11
  * Default unicode datatypes! Disable with #enable_default_unicode_types to false.
11
12
  * New #lowercase_schema_reflection configuration option for legacy DBs.
12
- * New dblib connection mode using TinyTds!
13
+ * New dblib connection mode using TinyTDS! Default mode too!
13
14
 
14
15
 
15
16
  ==== Testing Rake Tasks Support
@@ -118,39 +119,22 @@ By default the adapter will auto connect to lost DB connections. For every query
118
119
 
119
120
  == Versions
120
121
 
121
- It is our goal to match the adapter version with each version of rails. However we will track our own tiny version independent of ActiveRecord. For example, an adapter version of 2.3.x will work on any 2.3.x version of ActiveRecord. Version 3.x will track ActiveRecord 3. This convention will be used in both the Git tags as well as the gems versioning.
122
+ The adapter follows a rational versioning policy that also tracks ActiveRecord's major and minor version. That means the latest 3.1.x version of the adapter will always work for the latest 3.1.x version of ActiveRecord.
122
123
 
123
124
 
124
125
  == Installation
125
126
 
126
- You will need Ruby ODBC. If you are using the adapter under 1.9, then you need at least ruby-odbc version 0.99992. ODBC is the preferred mode, however if you are using IronRuby you can use the ADONET connection mode which uses native System.Data connection. Other connection modes may be supported, possibly a straight FreeTDS layer. The sky is the limit for optional transports. If you are interested in helping, open a ticket and submit a patch. Or start a conversation on the Google Group.
127
+ The adapter has no strict gem dependencies outside of ActiveRecord. You will have to pick a connection mode, the default is dblib which uses the TinyTDS gem. Just bundle the gem and the adapter will use it.
127
128
 
128
- $ gem install activerecord-sqlserver-adapter
129
+ gem 'tiny_tds'
130
+ gem 'activerecord-sqlserver-adapter', '~> 3.1.0'
129
131
 
130
- Optionally configure your gem dependencies in your Gemfile.
131
-
132
- gem 'activerecord-sqlserver-adapter', '3.x.xx'
133
-
134
- If you have any troubles installing the lower level libraries for the adapter, please consult the wiki pages for various platform installation guides. Tons of good info can be found and we ask that you contribute too!
132
+ If you want to use ruby ODBC, please use at least version 0.99992 since that contains fixes for both native types as well as fixes for proper encoding support under 1.9. If you have any troubles installing the lower level libraries for the adapter, please consult the wiki pages for various platform installation guides. Tons of good info can be found and we ask that you contribute too!
135
133
 
136
134
  http://wiki.github.com/rails-sqlserver/activerecord-sqlserver-adapter/platform-installation
137
135
 
138
136
 
139
137
 
140
- == IronRuby ADONET Mode
141
-
142
- A few details on this implementation. All that is needed in your database.yml configuration file is "mode: adonet" vs "odbc" and if you are running IronRuby, the connection will be native. You can also specify an "integrated_security: true" option in your configuration, remember to remove the username/password options too. To use this adapter, you will not need need ANY DBI middle layer or special extension gems to the adapter.
143
-
144
- This adapter is opinionated in regards to IronRuby on types going in and out of the DB. For example strings will be String, not System::String and DateTime vs System::Datetime. There are many more examples but the rule of thumb is that the types will be simple types that correlate to a standard Ruby implementation. We enforce this basic rule because it is necessary to pass the tests and let the framework do its job. We recommend sticking to native Ruby types in your application code too.
145
-
146
- The adapter establishes a System::Data::SqlClient connection that has both MultipleActiveResultSets (MARS) and Pooling turned off. There are good reasons for this one because the connection would not work otherwise for all the code issued by ActiveRecord. Remember too that ActiveRecord has it's own connection pooling and these underlying features like MARS/Pooling work against the adapter code.
147
-
148
- Currently IronRuby is passing most of the ActiveRecord and Adapter tests. Here is a list of the ones remaining. Some are in the adapter's realm and some are in Marshaling area of IronRuby's core to fix. Feel like helping knock these out? Submit a patch to github issues.
149
-
150
- http://gist.github.com/381101
151
-
152
-
153
-
154
138
  == Contributing
155
139
 
156
140
  If you’d like to contribute a feature or bugfix, thanks! To make sure your fix/feature has a high chance of being added, please read the following guidelines. First, ask on the Google list, IRC, or post a ticket on github issues. Second, make sure there are tests! We will not accept any patch that is not tested. Please read the RUNNING_UNIT_TESTS file for the details of how to run the unit tests.
@@ -182,7 +166,7 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord-
182
166
  * jeremydurham (Jeremy Durham)
183
167
 
184
168
  === Donators
185
- http://pledgie.com/campaigns/11630
169
+ http://pledgie.com/campaigns/15531
186
170
 
187
171
  == License
188
172
 
@@ -109,10 +109,6 @@ module ActiveRecord
109
109
  end
110
110
  end
111
111
  results.many? ? results : results.first
112
- when :adonet
113
- results = []
114
- results << select(sql, name).map { |r| r.with_indifferent_access }
115
- results.many? ? results : results.first
116
112
  end
117
113
  end
118
114
  end
@@ -183,6 +179,8 @@ module ActiveRecord
183
179
  retry_count += 1
184
180
  remove_database_connections_and_rollback(database)
185
181
  retry
182
+ elsif err.message =~ /does not exist/i
183
+ nil
186
184
  else
187
185
  raise
188
186
  end
@@ -241,7 +239,7 @@ module ActiveRecord
241
239
  next if ar_column && column.sql_type == 'timestamp'
242
240
  v = value
243
241
  names_and_types << if ar_column
244
- v = value.to_i if column.is_integer?
242
+ v = value.to_i if column.is_integer? && value.present?
245
243
  "@#{index} #{column.sql_type_for_statement}"
246
244
  elsif column.acts_like?(:string)
247
245
  "@#{index} nvarchar(max)"
@@ -265,8 +263,6 @@ module ActiveRecord
265
263
  @connection.execute(sql).do
266
264
  when :odbc
267
265
  @connection.do(sql)
268
- else :adonet
269
- @connection.create_command.tap{ |cmd| cmd.command_text = sql }.execute_non_query
270
266
  end
271
267
  ensure
272
268
  @update_sql = false
@@ -292,8 +288,6 @@ module ActiveRecord
292
288
  @connection.execute(sql)
293
289
  when :odbc
294
290
  block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
295
- else :adonet
296
- @connection.create_command.tap{ |cmd| cmd.command_text = sql }.execute_reader
297
291
  end
298
292
  end
299
293
  end
@@ -303,8 +297,6 @@ module ActiveRecord
303
297
  when :dblib
304
298
  when :odbc
305
299
  handle.more_results
306
- when :adonet
307
- handle.next_result
308
300
  end
309
301
  end
310
302
 
@@ -314,8 +306,6 @@ module ActiveRecord
314
306
  handle_to_names_and_values_dblib(handle, options)
315
307
  when :odbc
316
308
  handle_to_names_and_values_odbc(handle, options)
317
- when :adonet
318
- handle_to_names_and_values_adonet(handle, options)
319
309
  end
320
310
  end
321
311
 
@@ -344,62 +334,12 @@ module ActiveRecord
344
334
  end
345
335
  end
346
336
  end
347
-
348
- def handle_to_names_and_values_adonet(handle, options={})
349
- if handle.has_rows
350
- names = []
351
- rows = []
352
- fields_named = options[:fetch] == :rows
353
- while handle.read
354
- row = []
355
- handle.visible_field_count.times do |row_index|
356
- value = handle.get_value(row_index)
357
- value = case value
358
- when System::String
359
- value.to_s
360
- when System::DBNull
361
- nil
362
- when System::DateTime
363
- value.to_string("yyyy-MM-dd HH:mm:ss.fff").to_s
364
- when @@array_of_bytes ||= System::Array[System::Byte]
365
- String.new(value)
366
- else
367
- value
368
- end
369
- row << value
370
- names << handle.get_name(row_index).to_s unless fields_named
371
- end
372
- rows << row
373
- fields_named = true
374
- end
375
- else
376
- rows = []
377
- end
378
- if options[:fetch] != :rows
379
- names_and_values = []
380
- rows.each do |row|
381
- h = {}
382
- i = 0
383
- while i < row.size
384
- h[names[i]] = row[i]
385
- i += 1
386
- end
387
- names_and_values << h
388
- end
389
- names_and_values
390
- else
391
- rows
392
- end
393
- end
394
337
 
395
338
  def finish_statement_handle(handle)
396
339
  case @connection_options[:mode]
397
340
  when :dblib
398
341
  when :odbc
399
342
  handle.drop if handle && handle.respond_to?(:drop) && !handle.finished?
400
- when :adonet
401
- handle.close if handle && handle.respond_to?(:close) && !handle.is_closed
402
- handle.dispose if handle && handle.respond_to?(:dispose)
403
343
  end
404
344
  handle
405
345
  end
@@ -9,14 +9,12 @@ module ActiveRecord
9
9
 
10
10
  LOST_CONNECTION_EXCEPTIONS = {
11
11
  :dblib => ['TinyTds::Error'],
12
- :odbc => ['ODBC::Error'],
13
- :adonet => ['TypeError','System::Data::SqlClient::SqlException']
12
+ :odbc => ['ODBC::Error']
14
13
  }.freeze
15
14
 
16
15
  LOST_CONNECTION_MESSAGES = {
17
16
  :dblib => [/closed connection/, /dead or not enabled/, /server failed/i],
18
- :odbc => [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i],
19
- :adonet => [/current state is closed/, /network-related/]
17
+ :odbc => [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i]
20
18
  }.freeze
21
19
 
22
20
 
@@ -8,13 +8,23 @@ module ActiveRecord
8
8
  def quote(value, column = nil)
9
9
  case value
10
10
  when String, ActiveSupport::Multibyte::Chars
11
- if column && column.type == :binary
11
+ if column && column.type == :integer && value.blank?
12
+ nil
13
+ elsif column && column.type == :binary
12
14
  column.class.string_to_binary(value)
13
15
  elsif value.is_utf8? || (column && column.type == :string)
14
16
  "N'#{quote_string(value)}'"
15
17
  else
16
18
  super
17
19
  end
20
+ when Date, Time
21
+ if column && column.sql_type == 'datetime'
22
+ "'#{quoted_datetime(value)}'"
23
+ elsif column && (column.sql_type == 'datetimeoffset' || column.sql_type == 'time')
24
+ "'#{quoted_full_iso8601(value)}'"
25
+ else
26
+ super
27
+ end
18
28
  when nil
19
29
  column.respond_to?(:sql_type) && column.sql_type == 'timestamp' ? 'DEFAULT' : super
20
30
  else
@@ -51,14 +61,39 @@ module ActiveRecord
51
61
  QUOTED_FALSE
52
62
  end
53
63
 
64
+ def quoted_datetime(value)
65
+ if value.acts_like?(:time)
66
+ value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(3).to(22)
67
+ else
68
+ quoted_date(value)
69
+ end
70
+ end
71
+
72
+ def quoted_full_iso8601(value)
73
+ if value.acts_like?(:time)
74
+ value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(7).to(22)
75
+ else
76
+ quoted_date(value)
77
+ end
78
+ end
79
+
54
80
  def quoted_date(value)
55
81
  if value.acts_like?(:time) && value.respond_to?(:usec)
56
82
  "#{super}.#{sprintf("%03d",value.usec/1000)}"
83
+ elsif value.acts_like?(:date)
84
+ value.to_s(:_sqlserver_dateformat)
57
85
  else
58
86
  super
59
87
  end
60
88
  end
61
89
 
90
+ protected
91
+
92
+ def quoted_value_acts_like_time_filter(value)
93
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
94
+ value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
95
+ end
96
+
62
97
  end
63
98
  end
64
99
  end
@@ -7,9 +7,9 @@ module ActiveRecord
7
7
  @native_database_types ||= initialize_native_database_types.freeze
8
8
  end
9
9
 
10
- def tables(name = nil)
10
+ def tables(name = nil, table_type = 'BASE TABLE')
11
11
  info_schema_query do
12
- select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME <> 'dtproperties' AND TABLE_SCHEMA = schema_name()"
12
+ select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES #{"WHERE TABLE_TYPE = '#{table_type}'" if table_type} ORDER BY TABLE_NAME"
13
13
  end
14
14
  end
15
15
 
@@ -19,8 +19,8 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  def indexes(table_name, name = nil)
22
- unquoted_table_name = unqualify_table_name(table_name)
23
- select("EXEC sp_helpindex #{quote_table_name(unquoted_table_name)}",name).inject([]) do |indexes,index|
22
+ data = select("EXEC sp_helpindex #{quote(table_name)}",name) rescue []
23
+ data.inject([]) do |indexes,index|
24
24
  index = index.with_indifferent_access
25
25
  if index[:index_description] =~ /primary key/
26
26
  indexes
@@ -39,16 +39,31 @@ module ActiveRecord
39
39
 
40
40
  def columns(table_name, name = nil)
41
41
  return [] if table_name.blank?
42
- column_definitions(table_name).collect do |ci|
42
+ @sqlserver_columns_cache[table_name] ||= column_definitions(table_name).collect do |ci|
43
43
  sqlserver_options = ci.except(:name,:default_value,:type,:null).merge(:database_year=>database_year)
44
44
  SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
45
45
  end
46
46
  end
47
47
 
48
+ def create_table(table_name, options = {})
49
+ super
50
+ clear_cache!
51
+ end
52
+
48
53
  def rename_table(table_name, new_name)
49
54
  do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
50
55
  end
56
+
57
+ def drop_table(table_name, options = {})
58
+ super
59
+ clear_cache!
60
+ end
51
61
 
62
+ def add_column(table_name, column_name, type, options = {})
63
+ super
64
+ clear_cache!
65
+ end
66
+
52
67
  def remove_column(table_name, *column_names)
53
68
  raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
54
69
  column_names.flatten.each do |column_name|
@@ -57,6 +72,7 @@ module ActiveRecord
57
72
  remove_indexes(table_name, column_name)
58
73
  do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
59
74
  end
75
+ clear_cache!
60
76
  end
61
77
 
62
78
  def change_column(table_name, column_name, type, options = {})
@@ -72,16 +88,19 @@ module ActiveRecord
72
88
  sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}"
73
89
  end
74
90
  sql_commands.each { |c| do_execute(c) }
91
+ clear_cache!
75
92
  end
76
93
 
77
94
  def change_column_default(table_name, column_name, default)
78
95
  remove_default_constraint(table_name, column_name)
79
96
  do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
97
+ clear_cache!
80
98
  end
81
99
 
82
100
  def rename_column(table_name, column_name, new_column_name)
83
101
  detect_column_for!(table_name,column_name)
84
102
  do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
103
+ clear_cache!
85
104
  end
86
105
 
87
106
  def remove_index!(table_name, index_name)
@@ -117,8 +136,7 @@ module ActiveRecord
117
136
  # === SQLServer Specific ======================================== #
118
137
 
119
138
  def views(name = nil)
120
- @sqlserver_views_cache ||=
121
- info_schema_query { select_values("SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME NOT IN ('sysconstraints','syssegments')") }
139
+ @sqlserver_views_cache ||= tables(name,'VIEW')
122
140
  end
123
141
 
124
142
 
@@ -150,36 +168,41 @@ module ActiveRecord
150
168
  :ss_timestamp => { :name => 'timestamp' }
151
169
  }
152
170
  end
153
-
171
+
154
172
  def column_definitions(table_name)
155
173
  db_name = unqualify_db_name(table_name)
156
174
  db_name_with_period = "#{db_name}." if db_name
157
175
  table_schema = unqualify_table_schema(table_name)
158
176
  table_name = unqualify_table_name(table_name)
159
177
  sql = %{
160
- SELECT
178
+ SELECT DISTINCT
161
179
  #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name,
162
180
  #{lowercase_schema_reflection_sql('columns.COLUMN_NAME')} AS name,
163
181
  columns.DATA_TYPE AS type,
164
182
  columns.COLUMN_DEFAULT AS default_value,
165
183
  columns.NUMERIC_SCALE AS numeric_scale,
166
184
  columns.NUMERIC_PRECISION AS numeric_precision,
185
+ columns.ordinal_position,
167
186
  CASE
168
187
  WHEN columns.DATA_TYPE IN ('nchar','nvarchar') THEN columns.CHARACTER_MAXIMUM_LENGTH
169
188
  ELSE COL_LENGTH(columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME)
170
- END AS length,
189
+ END AS [length],
171
190
  CASE
172
191
  WHEN columns.IS_NULLABLE = 'YES' THEN 1
173
192
  ELSE NULL
174
- END AS is_nullable,
175
- CASE
176
- WHEN COLUMNPROPERTY(OBJECT_ID(columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME), columns.COLUMN_NAME, 'IsIdentity') = 0 THEN NULL
177
- ELSE 1
178
- END AS is_identity
193
+ END AS [is_nullable],
194
+ CASE
195
+ WHEN CCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1
196
+ WHEN COLUMNPROPERTY(OBJECT_ID(columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME), columns.COLUMN_NAME, 'IsIdentity') = 1 THEN 1
197
+ ELSE NULL
198
+ END AS [is_identity]
179
199
  FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS columns
200
+ LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON TC.TABLE_NAME = columns.TABLE_NAME AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
201
+ LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS CCU ON TC.CONSTRAINT_NAME = CCU.CONSTRAINT_NAME AND CCU.COLUMN_NAME = columns.COLUMN_NAME
180
202
  WHERE columns.TABLE_NAME = @0
181
203
  AND columns.TABLE_SCHEMA = #{table_schema.blank? ? "schema_name()" : "@1"}
182
204
  ORDER BY columns.ordinal_position
205
+
183
206
  }.gsub(/[ \t\r\n]+/,' ')
184
207
  binds = [['table_name', table_name]]
185
208
  binds << ['table_schema',table_schema] unless table_schema.blank?
@@ -213,6 +236,7 @@ module ActiveRecord
213
236
  match_data ? match_data[1] : nil
214
237
  end
215
238
  ci[:null] = ci[:is_nullable].to_i == 1 ; ci.delete(:is_nullable)
239
+ ci[:is_identity] = ci[:is_identity].to_i == 1
216
240
  ci
217
241
  end
218
242
  end
@@ -324,6 +348,7 @@ module ActiveRecord
324
348
  # === SQLServer Specific (Column/View Caches) =================== #
325
349
 
326
350
  def initialize_sqlserver_caches
351
+ @sqlserver_columns_cache = {}
327
352
  @sqlserver_views_cache = nil
328
353
  @sqlserver_view_information_cache = {}
329
354
  @sqlserver_quoted_column_and_table_names = {}
@@ -361,7 +386,7 @@ module ActiveRecord
361
386
  end
362
387
 
363
388
  def identity_column(table_name)
364
- columns(table_name).detect(&:is_identity?)
389
+ columns(table_name).detect(&:primary) || columns(table_name).detect(&:is_identity?)
365
390
  end
366
391
 
367
392
  end
@@ -21,15 +21,12 @@ module ActiveRecord
21
21
  mode = config[:mode].to_s.downcase.underscore.to_sym
22
22
  case mode
23
23
  when :dblib
24
- require_library_or_gem 'tiny_tds'
24
+ require 'tiny_tds'
25
25
  warn("TinyTds v0.4.3 or higher required. Using #{TinyTds::VERSION}") unless TinyTds::Client.instance_methods.map(&:to_s).include?("active?")
26
26
  when :odbc
27
27
  raise ArgumentError, 'Missing :dsn configuration.' unless config.has_key?(:dsn)
28
- require_library_or_gem 'odbc'
28
+ require 'odbc'
29
29
  require 'active_record/connection_adapters/sqlserver/core_ext/odbc'
30
- when :adonet
31
- require 'System.Data'
32
- raise ArgumentError, 'Missing :database configuration.' unless config.has_key?(:database)
33
30
  else
34
31
  raise ArgumentError, "Unknown connection mode in #{config.inspect}."
35
32
  end
@@ -55,6 +52,7 @@ module ActiveRecord
55
52
  def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {})
56
53
  @sqlserver_options = sqlserver_options.symbolize_keys
57
54
  super(name, default, sql_type, null)
55
+ @primary = @sqlserver_options[:is_identity]
58
56
  end
59
57
 
60
58
  class << self
@@ -74,11 +72,11 @@ module ActiveRecord
74
72
  end
75
73
 
76
74
  def is_utf8?
77
- @sql_type =~ /nvarchar|ntext|nchar/i
75
+ !!(@sql_type =~ /nvarchar|ntext|nchar/i)
78
76
  end
79
77
 
80
78
  def is_integer?
81
- @sql_type =~ /int/i
79
+ !!(@sql_type =~ /int/i)
82
80
  end
83
81
 
84
82
  def sql_type_for_statement
@@ -161,7 +159,7 @@ module ActiveRecord
161
159
  include Sqlserver::Errors
162
160
 
163
161
  ADAPTER_NAME = 'SQLServer'.freeze
164
- VERSION = '3.1.0.rc2'.freeze
162
+ VERSION = '3.1.0.rc5'.freeze
165
163
  DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/
166
164
  SUPPORTED_VERSIONS = [2005,2008,2010,2011].freeze
167
165
 
@@ -190,6 +188,7 @@ module ActiveRecord
190
188
  rescue
191
189
  0
192
190
  end
191
+ initialize_dateformatter
193
192
  initialize_sqlserver_caches
194
193
  use_database
195
194
  unless SUPPORTED_VERSIONS.include?(@database_year)
@@ -255,8 +254,6 @@ module ActiveRecord
255
254
  @connection.close rescue nil
256
255
  when :odbc
257
256
  @connection.disconnect rescue nil
258
- else :adonet
259
- @connection.close rescue nil
260
257
  end
261
258
  end
262
259
 
@@ -409,27 +406,20 @@ module ActiveRecord
409
406
  warn "Ruby ODBC v0.99992 or higher is required."
410
407
  end
411
408
  end
412
- when :adonet
413
- System::Data::SqlClient::SqlConnection.new.tap do |connection|
414
- connection.connection_string = System::Data::SqlClient::SqlConnectionStringBuilder.new.tap do |cs|
415
- if config[:integrated_security]
416
- cs.integrated_security = true
417
- else
418
- cs.user_i_d = config[:username]
419
- cs.password = config[:password]
420
- end
421
- cs.add 'Server', config[:host].to_clr_string
422
- cs.initial_catalog = config[:database]
423
- cs.multiple_active_result_sets = false
424
- cs.pooling = false
425
- end.to_s
426
- connection.open
427
- end
428
409
  end
429
410
  rescue
430
411
  raise unless @auto_connecting
431
412
  end
432
413
 
414
+ def initialize_dateformatter
415
+ @database_dateformat = user_options['dateformat']
416
+ a, b, c = @database_dateformat.each_char.to_a
417
+ [a,b,c].each { |f| f.upcase! if f == 'y' }
418
+ dateformat = "%#{a}-%#{b}-%#{c}"
419
+ ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
420
+ ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
421
+ end
422
+
433
423
  def remove_database_connections_and_rollback(database=nil)
434
424
  database ||= current_database
435
425
  do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
@@ -6,12 +6,12 @@ module Arel
6
6
 
7
7
  # Extending the Ordering class to be comparrison friendly which allows us to call #uniq on a
8
8
  # collection of them. See SelectManager#order for more details.
9
- class Ordering < Arel::Nodes::Binary
9
+ class Ordering < Arel::Nodes::Unary
10
10
  def hash
11
11
  expr.hash
12
12
  end
13
13
  def ==(other)
14
- self.class == other.class && self.expr == other.expr
14
+ other.is_a?(Arel::Nodes::Ordering) && self.expr == other.expr
15
15
  end
16
16
  def eql?(other)
17
17
  self == other
@@ -27,26 +27,25 @@ module Arel
27
27
  # to grouping objects for the inner sql during a select statment with an offset/rownumber. So this
28
28
  # is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects.
29
29
  alias :order_without_sqlserver :order
30
- def order(*exprs)
31
- return order_without_sqlserver(*exprs) unless Arel::Visitors::SQLServer === @visitor
32
- @ast.orders.concat(exprs.map{ |x|
30
+ def order(*expr)
31
+ return order_without_sqlserver(*expr) unless Arel::Visitors::SQLServer === @visitor
32
+ @ast.orders.concat(expr.map{ |x|
33
33
  case x
34
34
  when Arel::Attributes::Attribute
35
35
  table = Arel::Table.new(x.relation.table_alias || x.relation.name)
36
- expr = table[x.name]
37
- Arel::Nodes::Ordering.new expr
36
+ e = table[x.name]
37
+ Arel::Nodes::Ascending.new e
38
38
  when Arel::Nodes::Ordering
39
39
  x
40
40
  when String
41
41
  x.split(',').map do |s|
42
- expr, direction = s.split
43
- expr = Arel.sql(expr)
44
- direction = direction =~ /desc/i ? :desc : :asc
45
- Arel::Nodes::Ordering.new expr, direction
42
+ e, d = s.split
43
+ e = Arel.sql(e)
44
+ d =~ /desc/i ? Arel::Nodes::Descending.new(e) : Arel::Nodes::Ascending.new(e)
46
45
  end
47
46
  else
48
- expr = Arel.sql(x.to_s)
49
- Arel::Nodes::Ordering.new expr
47
+ e = Arel.sql(x.to_s)
48
+ Arel::Nodes::Ascending.new e
50
49
  end
51
50
  }.flatten)
52
51
  self
@@ -89,6 +88,13 @@ module Arel
89
88
  visit_Arel_Nodes_SelectStatementWithOutOffset(o)
90
89
  end
91
90
  end
91
+
92
+ def visit_Arel_Nodes_UpdateStatement(o)
93
+ if o.orders.any? && o.limit.nil?
94
+ o.limit = Nodes::Limit.new(2147483647)
95
+ end
96
+ super
97
+ end
92
98
 
93
99
  def visit_Arel_Nodes_Offset(o)
94
100
  "WHERE [__rnt].[__rn] > (#{visit o.expr})"
@@ -101,7 +107,15 @@ module Arel
101
107
  def visit_Arel_Nodes_Lock(o)
102
108
  visit o.expr
103
109
  end
104
-
110
+
111
+ def visit_Arel_Nodes_Ordering(o)
112
+ if o.respond_to?(:direction)
113
+ "#{visit o.expr} #{o.ascending? ? 'ASC' : 'DESC'}"
114
+ else
115
+ visit o.expr
116
+ end
117
+ end
118
+
105
119
  def visit_Arel_Nodes_Bin(o)
106
120
  "#{visit o.expr} #{@engine.connection.cs_equality_operator}"
107
121
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-sqlserver-adapter
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15424097
4
+ hash: 15424111
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 3
8
8
  - 1
9
9
  - 0
10
10
  - rc
11
- - 2
12
- version: 3.1.0.rc2
11
+ - 5
12
+ version: 3.1.0.rc5
13
13
  platform: ruby
14
14
  authors:
15
15
  - Ken Collins
@@ -21,7 +21,7 @@ autorequire:
21
21
  bindir: bin
22
22
  cert_chain: []
23
23
 
24
- date: 2011-05-31 00:00:00 -04:00
24
+ date: 2011-07-26 00:00:00 -04:00
25
25
  default_executable:
26
26
  dependencies:
27
27
  - !ruby/object:Gem::Dependency
@@ -32,14 +32,14 @@ dependencies:
32
32
  requirements:
33
33
  - - ~>
34
34
  - !ruby/object:Gem::Version
35
- hash: 15424103
35
+ hash: 15424111
36
36
  segments:
37
37
  - 3
38
38
  - 1
39
39
  - 0
40
40
  - rc
41
- - 1
42
- version: 3.1.0.rc1
41
+ - 5
42
+ version: 3.1.0.rc5
43
43
  type: :runtime
44
44
  version_requirements: *id001
45
45
  description: SQL Server 2005 and 2008 Adapter For ActiveRecord