activerecord 1.10.1 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (84) hide show
  1. data/CHANGELOG +187 -19
  2. data/RUNNING_UNIT_TESTS +11 -0
  3. data/lib/active_record.rb +3 -1
  4. data/lib/active_record/acts/list.rb +25 -14
  5. data/lib/active_record/acts/nested_set.rb +4 -4
  6. data/lib/active_record/acts/tree.rb +18 -1
  7. data/lib/active_record/associations.rb +90 -17
  8. data/lib/active_record/associations/association_collection.rb +44 -5
  9. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +17 -4
  10. data/lib/active_record/associations/has_many_association.rb +13 -3
  11. data/lib/active_record/associations/has_one_association.rb +19 -0
  12. data/lib/active_record/base.rb +292 -268
  13. data/lib/active_record/callbacks.rb +14 -14
  14. data/lib/active_record/connection_adapters/abstract_adapter.rb +137 -75
  15. data/lib/active_record/connection_adapters/db2_adapter.rb +10 -8
  16. data/lib/active_record/connection_adapters/mysql_adapter.rb +91 -64
  17. data/lib/active_record/connection_adapters/oci_adapter.rb +6 -6
  18. data/lib/active_record/connection_adapters/postgresql_adapter.rb +113 -60
  19. data/lib/active_record/connection_adapters/sqlite_adapter.rb +15 -12
  20. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +159 -132
  21. data/lib/active_record/fixtures.rb +59 -12
  22. data/lib/active_record/locking.rb +10 -9
  23. data/lib/active_record/migration.rb +112 -5
  24. data/lib/active_record/query_cache.rb +64 -0
  25. data/lib/active_record/timestamp.rb +10 -8
  26. data/lib/active_record/validations.rb +121 -26
  27. data/rakefile +16 -10
  28. data/test/aaa_create_tables_test.rb +26 -48
  29. data/test/abstract_unit.rb +3 -0
  30. data/test/aggregations_test.rb +19 -19
  31. data/test/association_callbacks_test.rb +110 -0
  32. data/test/associations_go_eager_test.rb +48 -14
  33. data/test/associations_test.rb +344 -142
  34. data/test/base_test.rb +150 -31
  35. data/test/binary_test.rb +7 -0
  36. data/test/callbacks_test.rb +24 -5
  37. data/test/column_alias_test.rb +2 -2
  38. data/test/connections/native_sqlserver_odbc/connection.rb +26 -0
  39. data/test/deprecated_associations_test.rb +27 -28
  40. data/test/deprecated_finder_test.rb +8 -9
  41. data/test/finder_test.rb +52 -17
  42. data/test/fixtures/author.rb +39 -0
  43. data/test/fixtures/categories.yml +7 -0
  44. data/test/fixtures/categories_posts.yml +8 -0
  45. data/test/fixtures/category.rb +2 -0
  46. data/test/fixtures/comment.rb +3 -1
  47. data/test/fixtures/comments.yml +43 -1
  48. data/test/fixtures/companies.yml +14 -0
  49. data/test/fixtures/company.rb +1 -1
  50. data/test/fixtures/computers.yml +2 -1
  51. data/test/fixtures/db_definitions/db2.sql +7 -2
  52. data/test/fixtures/db_definitions/mysql.drop.sql +2 -0
  53. data/test/fixtures/db_definitions/mysql.sql +11 -6
  54. data/test/fixtures/db_definitions/oci.sql +7 -2
  55. data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
  56. data/test/fixtures/db_definitions/postgresql.sql +8 -5
  57. data/test/fixtures/db_definitions/sqlite.drop.sql +2 -0
  58. data/test/fixtures/db_definitions/sqlite.sql +9 -4
  59. data/test/fixtures/db_definitions/sqlserver.drop.sql +2 -0
  60. data/test/fixtures/db_definitions/sqlserver.sql +12 -7
  61. data/test/fixtures/developer.rb +8 -1
  62. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  63. data/test/fixtures/post.rb +8 -2
  64. data/test/fixtures/posts.yml +21 -0
  65. data/test/fixtures/project.rb +14 -1
  66. data/test/fixtures/subscriber.rb +3 -0
  67. data/test/fixtures_test.rb +14 -0
  68. data/test/inheritance_test.rb +30 -22
  69. data/test/lifecycle_test.rb +3 -4
  70. data/test/locking_test.rb +2 -4
  71. data/test/migration_test.rb +186 -0
  72. data/test/mixin_nested_set_test.rb +19 -19
  73. data/test/mixin_test.rb +88 -88
  74. data/test/modules_test.rb +5 -10
  75. data/test/multiple_db_test.rb +2 -0
  76. data/test/pk_test.rb +8 -12
  77. data/test/reflection_test.rb +8 -4
  78. data/test/schema_test_postgresql.rb +63 -0
  79. data/test/thread_safety_test.rb +4 -1
  80. data/test/transactions_test.rb +9 -2
  81. data/test/unconnected_test.rb +1 -0
  82. data/test/validations_test.rb +151 -8
  83. metadata +11 -5
  84. data/test/migration_mysql.rb +0 -104
@@ -90,20 +90,21 @@ module ActiveRecord
90
90
  def native_database_types
91
91
  {
92
92
  :primary_key => "INTEGER PRIMARY KEY NOT NULL",
93
- :string => "VARCHAR(255)",
94
- :text => "TEXT",
95
- :integer => "INTEGER",
96
- :float => "float",
97
- :datetime => "DATETIME",
98
- :timestamp => "DATETIME",
99
- :time => "DATETIME",
100
- :date => "DATE",
101
- :binary => "BLOB",
102
- :boolean => "INTEGER"
93
+ :string => { :name => "varchar", :limit => 255 },
94
+ :text => { :name => "text" },
95
+ :integer => { :name => "integer" },
96
+ :float => { :name => "float" },
97
+ :datetime => { :name => "datetime" },
98
+ :timestamp => { :name => "datetime" },
99
+ :time => { :name => "datetime" },
100
+ :date => { :name => "date" },
101
+ :binary => { :name => "blob" },
102
+ :boolean => { :name => "integer" }
103
103
  }
104
104
  end
105
105
 
106
106
  def execute(sql, name = nil)
107
+ #log(sql, name, @connection) { |connection| connection.execute(sql) }
107
108
  log(sql, name) { @connection.execute(sql) }
108
109
  end
109
110
 
@@ -127,7 +128,9 @@ module ActiveRecord
127
128
  execute(sql, name).map do |row|
128
129
  record = {}
129
130
  row.each_key do |key|
130
- record[key.sub(/\w+\./, '')] = row[key] unless key.is_a?(Fixnum)
131
+ if key.is_a?(String)
132
+ record[key.sub(/^\w+\./, '')] = row[key]
133
+ end
131
134
  end
132
135
  record
133
136
  end
@@ -159,7 +162,7 @@ module ActiveRecord
159
162
  end
160
163
 
161
164
  def quote_column_name(name)
162
- return "'#{name}'"
165
+ "'#{name}'"
163
166
  end
164
167
 
165
168
  def adapter_name()
@@ -8,26 +8,8 @@ require 'active_record/connection_adapters/abstract_adapter'
8
8
  # Modifications: DeLynn Berry <delynnb@megastarfinancial.com>
9
9
  # Date: 3/22/2005
10
10
  #
11
- # This adapter will ONLY work on Windows systems, since it relies on Win32OLE, which,
12
- # to my knowledge, is only available on Window.
13
- #
14
- # It relies on the ADO support in the DBI module. If you are using the
15
- # one-click installer of Ruby, then you already have DBI installed, but
16
- # the ADO module is *NOT* installed. You will need to get the latest
17
- # source distribution of Ruby-DBI from http://ruby-dbi.sourceforge.net/
18
- # unzip it, and copy the file <tt>src/lib/dbd_ado/ADO.rb</tt> to
19
- # <tt>X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb</tt> (you will need to create
20
- # the ADO directory). Once you've installed that file, you are ready to go.
21
- #
22
- # Options:
23
- #
24
- # * <tt>:host</tt> -- Defaults to localhost
25
- # * <tt>:username</tt> -- Defaults to sa
26
- # * <tt>:password</tt> -- Defaults to nothing
27
- # * <tt>:database</tt> -- The name of the database. No default, must be provided.
28
- #
29
- # I have tested this code on a WindowsXP Pro SP1 system,
30
- # ruby 1.8.2 (2004-07-29) [i386-mswin32], SQL Server 2000.
11
+ # Modifications (ODBC): Mark Imbriaco <mark.imbriaco@pobox.com>
12
+ # Date: 6/26/2005
31
13
  #
32
14
  module ActiveRecord
33
15
  class Base
@@ -36,44 +18,47 @@ module ActiveRecord
36
18
 
37
19
  symbolize_strings_in_hash(config)
38
20
 
39
- host = config[:host]
40
- username = config[:username] ? config[:username].to_s : 'sa'
41
- password = config[:password].to_s
42
-
43
- if config.has_key?(:database)
44
- database = config[:database]
21
+ mode = config[:mode] ? config[:mode].to_s.upcase : 'ADO'
22
+ username = config[:username] ? config[:username].to_s : 'sa'
23
+ password = config[:password] ? config[:password].to_s : ''
24
+ if mode == "ODBC"
25
+ raise ArgumentError, "Missing DSN. Argument ':dsn' must be set in order for this adapter to work." unless config.has_key?(:dsn)
26
+ dsn = config[:dsn]
27
+ conn = DBI.connect("DBI:ODBC:#{dsn}", username, password)
45
28
  else
46
- raise ArgumentError, "No database specified. Missing argument: database."
29
+ raise ArgumentError, "Missing Database. Argument ':database' must be set in order for this adapter to work." unless config.has_key?(:database)
30
+ database = config[:database]
31
+ host = config[:host] ? config[:host].to_s : 'localhost'
32
+ conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};")
47
33
  end
48
34
 
49
- conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};")
50
35
  conn["AutoCommit"] = true
51
-
52
36
  ConnectionAdapters::SQLServerAdapter.new(conn, logger)
53
37
  end
54
- end
38
+ end # class Base
55
39
 
56
40
  module ConnectionAdapters
57
41
  class ColumnWithIdentity < Column# :nodoc:
58
- attr_reader :identity, :scale
42
+ attr_reader :identity, :is_special, :scale
59
43
 
60
44
  def initialize(name, default, sql_type = nil, is_identity = false, scale_value = 0)
61
45
  super(name, default, sql_type)
62
- @scale = scale_value
63
46
  @identity = is_identity
47
+ @is_special = sql_type =~ /text|ntext|image/i ? true : false
48
+ @scale = scale_value
64
49
  end
65
50
 
66
51
  def simplified_type(field_type)
67
52
  case field_type
68
- when /int|bigint|smallint|tinyint/i : :integer
69
- when /float|double|decimal|money|numeric|real|smallmoney/i : @scale == 0 ? :integer : :float
70
- when /datetime|smalldatetime/i : :datetime
71
- when /timestamp/i : :timestamp
72
- when /time/i : :time
73
- when /text|ntext/i : :text
74
- when /binary|image|varbinary/i : :binary
75
- when /char|nchar|nvarchar|string|varchar/i : :string
76
- when /bit/i : :boolean
53
+ when /int|bigint|smallint|tinyint/i then :integer
54
+ when /float|double|decimal|money|numeric|real|smallmoney/i then @scale == 0 ? :integer : :float
55
+ when /datetime|smalldatetime/i then :datetime
56
+ when /timestamp/i then :timestamp
57
+ when /time/i then :time
58
+ when /text|ntext/i then :text
59
+ when /binary|image|varbinary/i then :binary
60
+ when /char|nchar|nvarchar|string|varchar/i then :string
61
+ when /bit/i then :boolean
77
62
  end
78
63
  end
79
64
 
@@ -83,28 +68,32 @@ module ActiveRecord
83
68
  when :string then value
84
69
  when :integer then value == true || value == false ? value == true ? '1' : '0' : value.to_i
85
70
  when :float then value.to_f
86
- when :datetime then cast_to_date_or_time(value)
71
+ when :datetime then cast_to_datetime(value)
87
72
  when :timestamp then cast_to_time(value)
88
73
  when :time then cast_to_time(value)
89
74
  else value
90
75
  end
91
76
  end
92
77
 
93
- def cast_to_date_or_time(value)
94
- return value if value.is_a?(Date)
95
- guess_date_or_time (value.is_a?(Time)) ? value : cast_to_time(value)
96
- end
97
-
98
78
  def cast_to_time(value)
99
79
  return value if value.is_a?(Time)
100
- time_array = ParseDate.parsedate value
101
- time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
102
- Time.send Base.default_timezone, *time_array
80
+ time_array = ParseDate.parsedate(value)
81
+ time_array[0] ||= 2000
82
+ time_array[1] ||= 1
83
+ time_array[2] ||= 1
84
+ Time.send(Base.default_timezone, *time_array) rescue nil
103
85
  end
104
86
 
105
- def guess_date_or_time(value)
106
- (value.hour == 0 and value.min == 0 and value.sec == 0) ?
107
- Date.new(value.year, value.month, value.day) : value
87
+ def cast_to_datetime(value)
88
+ if value.is_a?(Time)
89
+ if value.year != 0 and value.month != 0 and value.day != 0
90
+ return value
91
+ else
92
+ return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
93
+ end
94
+ end
95
+ return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
96
+ value
108
97
  end
109
98
 
110
99
  # These methods will only allow the adapter to insert binary data with a length of 7K or less
@@ -112,14 +101,10 @@ module ActiveRecord
112
101
  def string_to_binary(value)
113
102
  value.gsub(/(\r|\n|\0|\x1a)/) do
114
103
  case $1
115
- when "\r"
116
- "%00"
117
- when "\n"
118
- "%01"
119
- when "\0"
120
- "%02"
121
- when "\x1a"
122
- "%03"
104
+ when "\r" then "%00"
105
+ when "\n" then "%01"
106
+ when "\0" then "%02"
107
+ when "\x1a" then "%03"
123
108
  end
124
109
  end
125
110
  end
@@ -127,22 +112,56 @@ module ActiveRecord
127
112
  def binary_to_string(value)
128
113
  value.gsub(/(%00|%01|%02|%03)/) do
129
114
  case $1
130
- when "%00"
131
- "\r"
132
- when "%01"
133
- "\n"
134
- when "%02\0"
135
- "\0"
136
- when "%03"
137
- "\x1a"
115
+ when "%00" then "\r"
116
+ when "%01" then "\n"
117
+ when "%02\0" then "\0"
118
+ when "%03" then "\x1a"
138
119
  end
139
120
  end
140
121
  end
141
-
142
122
  end
143
123
 
124
+ # In ADO mode, this adapter will ONLY work on Windows systems,
125
+ # since it relies on Win32OLE, which, to my knowledge, is only
126
+ # available on Windows.
127
+ #
128
+ # This mode also relies on the ADO support in the DBI module. If you are using the
129
+ # one-click installer of Ruby, then you already have DBI installed, but
130
+ # the ADO module is *NOT* installed. You will need to get the latest
131
+ # source distribution of Ruby-DBI from http://ruby-dbi.sourceforge.net/
132
+ # unzip it, and copy the file
133
+ # <tt>src/lib/dbd_ado/ADO.rb</tt>
134
+ # to
135
+ # <tt>X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb</tt>
136
+ # (you will more than likely need to create the ADO directory).
137
+ # Once you've installed that file, you are ready to go.
138
+ #
139
+ # In ODBC mode, the adapter requires the ODBC support in the DBI module which requires
140
+ # the Ruby ODBC module. Ruby ODBC 0.996 was used in development and testing,
141
+ # and it is available at http://www.ch-werner.de/rubyodbc/
142
+ #
143
+ # Options:
144
+ #
145
+ # * <tt>:mode</tt> -- ADO or ODBC. Defaults to ADO.
146
+ # * <tt>:username</tt> -- Defaults to sa.
147
+ # * <tt>:password</tt> -- Defaults to empty string.
148
+ #
149
+ # ADO specific options:
150
+ #
151
+ # * <tt>:host</tt> -- Defaults to localhost.
152
+ # * <tt>:database</tt> -- The name of the database. No default, must be provided.
153
+ #
154
+ # ODBC specific options:
155
+ #
156
+ # * <tt>:dsn</tt> -- Defaults to nothing.
157
+ #
158
+ # ADO code tested on Windows 2000 and higher systems,
159
+ # running ruby 1.8.2 (2004-07-29) [i386-mswin32], and SQL Server 2000 SP3.
160
+ #
161
+ # ODBC code tested on a Fedora Core 4 system, running FreeTDS 0.63,
162
+ # unixODBC 2.2.11, Ruby ODBC 0.996, Ruby DBI 0.0.23 and Ruby 1.8.2.
163
+ # [Linux strongmad 2.6.11-1.1369_FC4 #1 Thu Jun 2 22:55:56 EDT 2005 i686 i686 i386 GNU/Linux]
144
164
  class SQLServerAdapter < AbstractAdapter
145
-
146
165
  def native_database_types
147
166
  {
148
167
  :primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
@@ -164,7 +183,6 @@ module ActiveRecord
164
183
  end
165
184
 
166
185
  def select_all(sql, name = nil)
167
- add_limit!(sql, nil)
168
186
  select(sql, name)
169
187
  end
170
188
 
@@ -175,12 +193,11 @@ module ActiveRecord
175
193
  end
176
194
 
177
195
  def columns(table_name, name = nil)
178
- sql = "SELECT COLUMN_NAME as ColName, COLUMN_DEFAULT as DefaultValue, DATA_TYPE as ColType, COL_LENGTH('#{table_name}', COLUMN_NAME) as Length, COLUMNPROPERTY(OBJECT_ID('#{table_name}'), COLUMN_NAME, 'IsIdentity') as IsIdentity, NUMERIC_SCALE as Scale FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = '#{table_name}'"
179
- result = nil
180
- # Uncomment if you want to have the Columns select statment logged.
196
+ sql = "SELECT COLUMN_NAME as ColName, COLUMN_DEFAULT as DefaultValue, DATA_TYPE as ColType, COL_LENGTH('#{table_name}', COLUMN_NAME) as Length, COLUMNPROPERTY(OBJECT_ID('#{table_name}'), COLUMN_NAME, 'IsIdentity') as IsIdentity, NUMERIC_SCALE as Scale FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '#{table_name}'"
197
+ # Comment out if you want to have the Columns select statment logged.
181
198
  # Personnally, I think it adds unneccessary bloat to the log.
182
- # If you do uncomment, make sure to comment the "result" line that follows
183
- log(sql, name, @connection) { |conn| result = conn.select_all(sql) }
199
+ # If you do comment it out, make sure to un-comment the "result" line that follows
200
+ result = log(sql, name) { @connection.select_all(sql) }
184
201
  #result = @connection.select_all(sql)
185
202
  columns = []
186
203
  result.each { |field| columns << ColumnWithIdentity.new(field[:ColName], field[:DefaultValue].to_s.gsub!(/[()\']/,"") =~ /null/ ? nil : field[:DefaultValue], "#{field[:ColType]}(#{field[:Length]})", field[:IsIdentity] == 1 ? true : false, field[:Scale]) }
@@ -199,12 +216,12 @@ module ActiveRecord
199
216
  execute enable_identity_insert(table_name, true)
200
217
  ii_enabled = true
201
218
  rescue Exception => e
202
- # Coulnd't turn on IDENTITY_INSERT
219
+ raise ActiveRecordError, "IDENTITY_INSERT could not be turned ON"
203
220
  end
204
221
  end
205
222
  end
206
- log(sql, name, @connection) do |conn|
207
- conn.execute(sql)
223
+ log(sql, name) do
224
+ @connection.execute(sql)
208
225
  select_one("SELECT @@IDENTITY AS Ident")["Ident"]
209
226
  end
210
227
  ensure
@@ -212,7 +229,7 @@ module ActiveRecord
212
229
  begin
213
230
  execute enable_identity_insert(table_name, false)
214
231
  rescue Exception => e
215
- # Couldn't turn off IDENTITY_INSERT
232
+ raise ActiveRecordError, "IDENTITY_INSERT could not be turned OFF"
216
233
  end
217
234
  end
218
235
  end
@@ -222,14 +239,12 @@ module ActiveRecord
222
239
  if sql =~ /^INSERT/i
223
240
  insert(sql, name)
224
241
  elsif sql =~ /^UPDATE|DELETE/i
225
- log(sql, name, @connection) do |conn|
226
- conn.execute(sql)
242
+ log(sql, name) do
243
+ @connection.execute(sql)
227
244
  retVal = select_one("SELECT @@ROWCOUNT AS AffectedRows")["AffectedRows"]
228
245
  end
229
246
  else
230
- log(sql, name, @connection) do |conn|
231
- conn.execute(sql)
232
- end
247
+ log(sql, name) { @connection.execute(sql) }
233
248
  end
234
249
  end
235
250
 
@@ -239,27 +254,21 @@ module ActiveRecord
239
254
  alias_method :delete, :update
240
255
 
241
256
  def begin_db_transaction
242
- begin
243
- @connection["AutoCommit"] = false
244
- rescue Exception => e
245
- @connection["AutoCommit"] = true
246
- end
257
+ @connection["AutoCommit"] = false
258
+ rescue Exception => e
259
+ @connection["AutoCommit"] = true
247
260
  end
248
261
 
249
262
  def commit_db_transaction
250
- begin
251
- @connection.commit
252
- ensure
253
- @connection["AutoCommit"] = true
254
- end
263
+ @connection.commit
264
+ ensure
265
+ @connection["AutoCommit"] = true
255
266
  end
256
267
 
257
268
  def rollback_db_transaction
258
- begin
259
- @connection.rollback
260
- ensure
261
- @connection["AutoCommit"] = true
262
- end
269
+ @connection.rollback
270
+ ensure
271
+ @connection["AutoCommit"] = true
263
272
  end
264
273
 
265
274
  def quote(value, column = nil)
@@ -280,21 +289,25 @@ module ActiveRecord
280
289
  end
281
290
  end
282
291
 
283
- def quote_string(s)
284
- s.gsub(/\'/, "''")
292
+ def quote_string(string)
293
+ string.gsub(/\'/, "''")
285
294
  end
286
295
 
287
296
  def quote_column_name(name)
288
297
  "[#{name}]"
289
298
  end
290
299
 
291
- def add_limit_with_offset!(sql, limit, offset)
292
- order_by = sql.include?("ORDER BY") ? get_order_by(sql.sub(/.*ORDER\sBY./, "")) : nil
293
- sql.gsub!(/SELECT/i, "SELECT * FROM ( SELECT TOP #{limit} * FROM ( SELECT TOP #{limit + offset}")<<" ) AS tmp1 ORDER BY #{order_by[1]} ) AS tmp2 ORDER BY #{order_by[0]}"
294
- end
295
-
296
- def add_limit_without_offset!(sql, limit)
297
- limit.nil? ? sql : sql.gsub!(/SELECT/i, "SELECT TOP #{limit}")
300
+ def add_limit_offset!(sql, options)
301
+ if options.has_key?(:limit) and options.has_key?(:offset) and !options[:limit].nil? and !options[:offset].nil?
302
+ options[:order] ||= "id ASC"
303
+ total_rows = @connection.select_all("SELECT count(*) as TotalRows from #{get_table_name(sql)}")[0][:TotalRows].to_i
304
+ if (options[:limit] + options[:offset]) > total_rows
305
+ options[:limit] = (total_rows - options[:offset] > 0) ? (total_rows - options[:offset]) : 1
306
+ end
307
+ sql.gsub!(/SELECT/i, "SELECT * FROM ( SELECT TOP #{options[:limit]} * FROM ( SELECT TOP #{options[:limit] + options[:offset]}")<<" ) AS tmp1 ORDER BY #{change_order_direction(options[:order])} ) AS tmp2 ORDER BY #{options[:order]}"
308
+ else
309
+ sql.gsub!(/SELECT/i, "SELECT TOP #{options[:limit]}") unless options[:limit].nil?
310
+ end
298
311
  end
299
312
 
300
313
  def recreate_database(name)
@@ -313,8 +326,9 @@ module ActiveRecord
313
326
  private
314
327
  def select(sql, name = nil)
315
328
  rows = []
316
- log(sql, name, @connection) do |conn|
317
- conn.select_all(sql) do |row|
329
+ repair_special_columns(sql)
330
+ log(sql, name) do
331
+ @connection.select_all(sql) do |row|
318
332
  record = {}
319
333
  row.column_names.each do |col|
320
334
  record[col] = row[col]
@@ -332,8 +346,9 @@ module ActiveRecord
332
346
  end
333
347
 
334
348
  def get_table_name(sql)
335
- if sql =~ /into\s*([^\s]+)\s*/i or
336
- sql =~ /update\s*([^\s]+)\s*/i
349
+ if sql =~ /into\s*([^\s]+)\s*|update\s*([^\s]+)\s*/i
350
+ $1
351
+ elsif sql =~ /from\s*([^\s]+)\s*/i
337
352
  $1
338
353
  else
339
354
  nil
@@ -341,18 +356,12 @@ module ActiveRecord
341
356
  end
342
357
 
343
358
  def has_identity_column(table_name)
344
- return get_identity_column(table_name) != nil
359
+ !get_identity_column(table_name).nil?
345
360
  end
346
361
 
347
362
  def get_identity_column(table_name)
348
- if not @table_columns
349
- @table_columns = {}
350
- end
351
-
352
- if @table_columns[table_name] == nil
353
- @table_columns[table_name] = columns(table_name)
354
- end
355
-
363
+ @table_columns = {} unless @table_columns
364
+ @table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
356
365
  @table_columns[table_name].each do |col|
357
366
  return col.name if col.identity
358
367
  end
@@ -361,17 +370,35 @@ module ActiveRecord
361
370
  end
362
371
 
363
372
  def query_contains_identity_column(sql, col)
364
- return sql =~ /[\[.,]\s*#{col}/
373
+ sql =~ /\[#{col}\]/
374
+ end
375
+
376
+ def change_order_direction(order)
377
+ case order
378
+ when /DESC/i then order.gsub(/DESC/i, "ASC")
379
+ when /ASC/i then order.gsub(/ASC/i, "DESC")
380
+ else String.new(order).insert(-1, " DESC")
381
+ end
365
382
  end
366
383
 
367
- def get_order_by(sql)
368
- return sql, sql.gsub(/\s*DESC\s*/, "").gsub(/\s*ASC\s*/, " DESC")
384
+ def get_special_columns(table_name)
385
+ special = []
386
+ @table_columns = {} unless @table_columns
387
+ @table_columns[table_name] = columns(table_name) if @table_columns[table_name] == nil
388
+ @table_columns[table_name].each do |col|
389
+ special << col.name if col.is_special
390
+ end
391
+ special
369
392
  end
370
393
 
371
- def get_offset_amount(limit)
372
- limit = limit.gsub!(/.OFFSET./i, ",").split(',')
373
- return limit[0].to_i, limit[0].to_i+limit[1].to_i
394
+ def repair_special_columns(sql)
395
+ special_cols = get_special_columns(get_table_name(sql))
396
+ for col in special_cols.to_a
397
+ sql.gsub!(Regexp.new(" #{col.to_s} = "), " #{col.to_s} LIKE ")
398
+ end
399
+ sql
374
400
  end
375
- end
376
- end
377
- end
401
+
402
+ end #class SQLServerAdapter < AbstractAdapter
403
+ end #module ConnectionAdapters
404
+ end #module ActiveRecord