activerecord 1.3.0 → 1.4.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 (75) hide show
  1. data/CHANGELOG +77 -2
  2. data/install.rb +5 -0
  3. data/lib/active_record.rb +6 -2
  4. data/lib/active_record/acts/list.rb +56 -45
  5. data/lib/active_record/acts/tree.rb +3 -2
  6. data/lib/active_record/associations.rb +10 -62
  7. data/lib/active_record/associations/association_collection.rb +20 -23
  8. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +36 -10
  9. data/lib/active_record/associations/has_many_association.rb +50 -25
  10. data/lib/active_record/base.rb +118 -80
  11. data/lib/active_record/callbacks.rb +51 -50
  12. data/lib/active_record/connection_adapters/abstract_adapter.rb +33 -12
  13. data/lib/active_record/connection_adapters/db2_adapter.rb +129 -0
  14. data/lib/active_record/connection_adapters/sqlite_adapter.rb +23 -1
  15. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +80 -90
  16. data/lib/active_record/fixtures.rb +1 -1
  17. data/lib/active_record/locking.rb +57 -0
  18. data/lib/active_record/support/class_attribute_accessors.rb +19 -5
  19. data/lib/active_record/support/class_inheritable_attributes.rb +4 -3
  20. data/lib/active_record/support/dependencies.rb +71 -0
  21. data/lib/active_record/support/inflector.rb +1 -0
  22. data/lib/active_record/support/misc.rb +29 -3
  23. data/lib/active_record/support/module_attribute_accessors.rb +57 -0
  24. data/lib/active_record/transactions.rb +18 -11
  25. data/lib/active_record/validations.rb +3 -3
  26. data/lib/active_record/vendor/db2.rb +357 -0
  27. data/lib/active_record/vendor/mysql.rb +1 -1
  28. data/rakefile +17 -5
  29. data/test/associations_test.rb +39 -4
  30. data/test/base_test.rb +13 -4
  31. data/test/binary_test.rb +43 -0
  32. data/test/callbacks_test.rb +230 -0
  33. data/test/connections/native_db2/connection.rb +24 -0
  34. data/test/connections/native_sqlserver/connection.rb +9 -3
  35. data/test/deprecated_associations_test.rb +16 -10
  36. data/test/finder_test.rb +65 -13
  37. data/test/fixtures/associations.png +0 -0
  38. data/test/fixtures/binary.rb +2 -0
  39. data/test/fixtures/companies.yml +21 -0
  40. data/test/fixtures/courses.yml +7 -0
  41. data/test/fixtures/customers.yml +7 -0
  42. data/test/fixtures/db_definitions/db2.sql +124 -0
  43. data/test/fixtures/db_definitions/db22.sql +4 -0
  44. data/test/fixtures/db_definitions/mysql.sql +12 -0
  45. data/test/fixtures/db_definitions/postgresql.sql +13 -0
  46. data/test/fixtures/db_definitions/sqlite.sql +9 -0
  47. data/test/fixtures/db_definitions/sqlserver.sql +13 -0
  48. data/test/fixtures/developers_projects.yml +13 -0
  49. data/test/fixtures/entrants.yml +14 -0
  50. data/test/fixtures/fixture_database.sqlite +0 -0
  51. data/test/fixtures/movies.yml +7 -0
  52. data/test/fixtures/people.yml +3 -0
  53. data/test/fixtures/person.rb +1 -0
  54. data/test/fixtures/projects.yml +7 -0
  55. data/test/fixtures/topics.yml +21 -0
  56. data/test/locking_test.rb +34 -0
  57. data/test/mixin_test.rb +6 -0
  58. data/test/validations_test.rb +1 -1
  59. metadata +33 -29
  60. data/test/fixtures/companies/first_client +0 -6
  61. data/test/fixtures/companies/first_firm +0 -4
  62. data/test/fixtures/companies/second_client +0 -6
  63. data/test/fixtures/courses/java +0 -2
  64. data/test/fixtures/courses/ruby +0 -2
  65. data/test/fixtures/customers/david +0 -6
  66. data/test/fixtures/entrants/first +0 -3
  67. data/test/fixtures/entrants/second +0 -3
  68. data/test/fixtures/entrants/third +0 -3
  69. data/test/fixtures/movies/first +0 -2
  70. data/test/fixtures/movies/second +0 -2
  71. data/test/fixtures/projects/action_controller +0 -2
  72. data/test/fixtures/projects/active_record +0 -2
  73. data/test/fixtures/topics/first +0 -10
  74. data/test/fixtures/topics/second +0 -8
  75. data/test/inflector_test.rb +0 -122
@@ -34,18 +34,19 @@ module ActiveRecord
34
34
  # end
35
35
  #
36
36
  # class Subscription < ActiveRecord::Base
37
- # # Automatically assign the signup date
38
- # def before_create
39
- # self.signed_up_on = Date.today
40
- # end
37
+ # before_create :record_signup
38
+ #
39
+ # private
40
+ # def record_signup
41
+ # self.signed_up_on = Date.today
42
+ # end
41
43
  # end
42
44
  #
43
45
  # class Firm < ActiveRecord::Base
44
46
  # # Destroys the associated clients and people when the firm is destroyed
45
- # def before_destroy
46
- # Client.destroy_all "client_of = #{id}"
47
- # Person.destroy_all "firm_id = #{id}"
48
- # end
47
+ # before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
48
+ # before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
49
+ # end
49
50
  #
50
51
  # == Inheritable callback queues
51
52
  #
@@ -169,9 +170,7 @@ module ActiveRecord
169
170
  alias_method :instantiate_without_callbacks, :instantiate
170
171
  alias_method :instantiate, :instantiate_with_callbacks
171
172
  end
172
- end
173
173
 
174
- base.class_eval do
175
174
  alias_method :initialize_without_callbacks, :initialize
176
175
  alias_method :initialize, :initialize_with_callbacks
177
176
 
@@ -191,14 +190,21 @@ module ActiveRecord
191
190
  alias_method :destroy, :destroy_with_callbacks
192
191
  end
193
192
 
194
- CALLBACKS.each { |cb| base.class_eval("def self.#{cb}(*methods) write_inheritable_array(\"#{cb}\", methods) end") }
193
+ CALLBACKS.each do |method|
194
+ base.class_eval <<-"end_eval"
195
+ def self.#{method}(*callbacks, &block)
196
+ callbacks << block if block_given?
197
+ write_inheritable_array(#{method.to_sym.inspect}, callbacks)
198
+ end
199
+ end_eval
200
+ end
195
201
  end
196
202
 
197
203
  module ClassMethods #:nodoc:
198
204
  def instantiate_with_callbacks(record)
199
205
  object = instantiate_without_callbacks(record)
200
- object.callback(:after_find) if object.respond_to_without_attributes?(:after_find)
201
- object.callback(:after_initialize) if object.respond_to_without_attributes?(:after_initialize)
206
+ object.send(:invoke_and_notify, :after_find)
207
+ object.send(:invoke_and_notify, :after_initialize)
202
208
  object
203
209
  end
204
210
  end
@@ -211,7 +217,7 @@ module ActiveRecord
211
217
  def initialize_with_callbacks(attributes = nil) #:nodoc:
212
218
  initialize_without_callbacks(attributes)
213
219
  result = yield self if block_given?
214
- after_initialize if respond_to_without_attributes?(:after_initialize)
220
+ invoke_and_notify(:after_initialize)
215
221
  result
216
222
  end
217
223
 
@@ -298,45 +304,40 @@ module ActiveRecord
298
304
  result
299
305
  end
300
306
 
301
- def callback(callback_method) #:nodoc:
302
- run_callbacks(callback_method)
303
- send(callback_method)
304
- notify(callback_method)
305
- end
306
-
307
- def run_callbacks(callback_method)
308
- filters = self.class.read_inheritable_attribute(callback_method.to_s)
309
- if filters.nil? then return end
310
- filters.each do |filter|
311
- if Symbol === filter
312
- self.send(filter)
313
- elsif String === filter
314
- eval(filter, binding)
315
- elsif filter_block?(filter)
316
- filter.call(self)
317
- elsif filter_class?(filter, callback_method)
318
- filter.send(callback_method, self)
319
- else
320
- raise(
321
- ActiveRecordError,
322
- "Filters need to be either a symbol, string (to be eval'ed), proc/method, or " +
323
- "class implementing a static filter method"
324
- )
307
+ private
308
+ def callback(method)
309
+ callbacks_for(method).each do |callback|
310
+ case callback
311
+ when Symbol
312
+ self.send(callback)
313
+ when String
314
+ eval(callback, binding)
315
+ when Proc, Method
316
+ callback.call(self)
317
+ else
318
+ if callback.respond_to?(method)
319
+ callback.send(method, self)
320
+ else
321
+ raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
322
+ end
323
+ end
325
324
  end
325
+
326
+ invoke_and_notify(method)
326
327
  end
327
- end
328
328
 
329
- def filter_block?(filter)
330
- filter.respond_to?("call") && (filter.arity == 1 || filter.arity == -1)
331
- end
329
+ def callbacks_for(method)
330
+ self.class.read_inheritable_attribute(method.to_sym) or []
331
+ end
332
332
 
333
- def filter_class?(filter, callback_method)
334
- filter.respond_to?(callback_method)
335
- end
333
+ def invoke_and_notify(method)
334
+ send(method) if respond_to_without_attributes?(method)
335
+ notify(method)
336
+ end
336
337
 
337
- def notify(callback_method) #:nodoc:
338
- self.class.changed
339
- self.class.notify_observers(callback_method, self)
340
- end
338
+ def notify(method) #:nodoc:
339
+ self.class.changed
340
+ self.class.notify_observers(method, self)
341
+ end
341
342
  end
342
- end
343
+ end
@@ -184,6 +184,7 @@ module ActiveRecord
184
184
  when :timestamp then Time
185
185
  when :time then Time
186
186
  when :text, :string then String
187
+ when :binary then String
187
188
  when :boolean then Object
188
189
  end
189
190
  end
@@ -199,6 +200,7 @@ module ActiveRecord
199
200
  when :timestamp then string_to_time(value)
200
201
  when :time then string_to_dummy_time(value)
201
202
  when :date then string_to_date(value)
203
+ when :binary then binary_to_string(value)
202
204
  when :boolean then (value == "t" or value == true ? true : false)
203
205
  else value
204
206
  end
@@ -207,6 +209,14 @@ module ActiveRecord
207
209
  def human_name
208
210
  Base.human_attribute_name(@name)
209
211
  end
212
+
213
+ def string_to_binary(value)
214
+ value
215
+ end
216
+
217
+ def binary_to_string(value)
218
+ value
219
+ end
210
220
 
211
221
  private
212
222
  def string_to_date(string)
@@ -220,21 +230,21 @@ module ActiveRecord
220
230
  return string if Time === string
221
231
  time_array = ParseDate.parsedate(string).compact
222
232
  # treat 0000-00-00 00:00:00 as nil
223
- Time.local(*time_array) rescue nil
233
+ Time.send(Base.default_timezone, *time_array) rescue nil
224
234
  end
225
235
 
226
- def string_to_dummy_time(string)
227
- return string if Time === string
228
- time_array = ParseDate.parsedate(string)
229
- # pad the resulting array with dummy date information
230
- time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
231
- Time.local(*time_array) rescue nil
232
- end
233
-
236
+ def string_to_dummy_time(string)
237
+ return string if Time === string
238
+ time_array = ParseDate.parsedate(string)
239
+ # pad the resulting array with dummy date information
240
+ time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
241
+ Time.send(Base.default_timezone, *time_array) rescue nil
242
+ end
243
+
234
244
  def extract_limit(sql_type)
235
245
  $1.to_i if sql_type =~ /\((.*)\)/
236
246
  end
237
-
247
+
238
248
  def simplified_type(field_type)
239
249
  case field_type
240
250
  when /int/i
@@ -249,8 +259,10 @@ module ActiveRecord
249
259
  :time
250
260
  when /date/i
251
261
  :date
252
- when /(c|b)lob/i, /text/i
262
+ when /clob/i, /text/i
253
263
  :text
264
+ when /blob/i, /binary/i
265
+ :binary
254
266
  when /char/i, /string/i
255
267
  :string
256
268
  when /boolean/i
@@ -323,7 +335,12 @@ module ActiveRecord
323
335
 
324
336
  def quote(value, column = nil)
325
337
  case value
326
- when String then "'#{quote_string(value)}'" # ' (for ruby-mode)
338
+ when String
339
+ if column && column.type == :binary
340
+ "'#{quote_string(column.string_to_binary(value))}'" # ' (for ruby-mode)
341
+ else
342
+ "'#{quote_string(value)}'" # ' (for ruby-mode)
343
+ end
327
344
  when NilClass then "NULL"
328
345
  when TrueClass then (column && column.type == :boolean ? "'t'" : "1")
329
346
  when FalseClass then (column && column.type == :boolean ? "'f'" : "0")
@@ -345,6 +362,10 @@ module ActiveRecord
345
362
  # Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database.
346
363
  def structure_dump() end
347
364
 
365
+ def add_limit!(sql, limit)
366
+ sql << " LIMIT #{limit}"
367
+ end
368
+
348
369
  protected
349
370
  def log(sql, name, connection, &action)
350
371
  begin
@@ -0,0 +1,129 @@
1
+ # db2_adapter.rb
2
+ # author: Maik Schmidt <contact@maik-schmidt.de>
3
+
4
+ require 'active_record/connection_adapters/abstract_adapter'
5
+
6
+ begin
7
+ require 'db2/db2cli' unless self.class.const_defined?(:DB2CLI)
8
+ require 'active_record/vendor/db2'
9
+
10
+ module ActiveRecord
11
+ class Base
12
+ # Establishes a connection to the database that's used by
13
+ # all Active Record objects
14
+ def self.db2_connection(config) # :nodoc:
15
+ symbolize_strings_in_hash(config)
16
+ usr = config[:username]
17
+ pwd = config[:password]
18
+
19
+ if config.has_key?(:database)
20
+ database = config[:database]
21
+ else
22
+ raise ArgumentError, "No database specified. Missing argument: database."
23
+ end
24
+
25
+ connection = DB2::Connection.new(DB2::Environment.new)
26
+ connection.connect(database, usr, pwd)
27
+ ConnectionAdapters::DB2Adapter.new(connection)
28
+ end
29
+ end
30
+
31
+ module ConnectionAdapters
32
+ class DB2Adapter < AbstractAdapter # :nodoc:
33
+ def select_all(sql, name = nil)
34
+ select(sql, name)
35
+ end
36
+
37
+ def select_one(sql, name = nil)
38
+ select(sql, name).first
39
+ end
40
+
41
+ def insert(sql, name = nil, pk = nil, id_value = nil)
42
+ execute(sql, name = nil)
43
+ id_value || last_insert_id
44
+ end
45
+
46
+ def execute(sql, name = nil)
47
+ rows_affected = 0
48
+
49
+ log(sql, name, @connection) do |connection|
50
+ stmt = DB2::Statement.new(connection)
51
+ stmt.exec_direct(sql)
52
+ rows_affected = stmt.row_count
53
+ stmt.free
54
+ end
55
+
56
+ rows_affected
57
+ end
58
+
59
+ alias_method :update, :execute
60
+ alias_method :delete, :execute
61
+
62
+ def begin_db_transaction
63
+ @connection.set_auto_commit_off
64
+ end
65
+
66
+ def commit_db_transaction
67
+ @connection.commit
68
+ @connection.set_auto_commit_on
69
+ end
70
+
71
+ def rollback_db_transaction
72
+ @connection.rollback
73
+ @connection.set_auto_commit_on
74
+ end
75
+
76
+ def quote_column_name(name) name; end
77
+
78
+ def quote_string(s)
79
+ s.gsub(/'/, "''") # ' (for ruby-mode)
80
+ end
81
+
82
+ def add_limit!(sql, limit)
83
+ sql << " FETCH FIRST #{limit} ROWS ONLY"
84
+ end
85
+
86
+ def columns(table_name, name = nil)
87
+ stmt = DB2::Statement.new(@connection)
88
+ result = []
89
+
90
+ stmt.columns(table_name.upcase).each do |c|
91
+ c_name = c[3].downcase
92
+ c_default = c[12] == 'NULL' ? nil : c[12]
93
+ c_type = c[5].downcase
94
+ c_type += "(#{c[6]})" if !c[6].nil? && c[6] != ''
95
+ result << Column.new(c_name, c_default, c_type)
96
+ end
97
+
98
+ stmt.free
99
+ result
100
+ end
101
+
102
+ private
103
+ def last_insert_id
104
+ row = select_one(<<-GETID.strip)
105
+ with temp(id) as (values (identity_val_local())) select * from temp
106
+ GETID
107
+ row['id'].to_i
108
+ end
109
+
110
+ def select(sql, name = nil)
111
+ stmt = nil
112
+ log(sql, name, @connection) do |connection|
113
+ stmt = DB2::Statement.new(connection)
114
+ stmt.exec_direct(sql + " with ur")
115
+ end
116
+
117
+ rows = []
118
+ while row = stmt.fetch_as_hash
119
+ rows << row
120
+ end
121
+ stmt.free
122
+ rows
123
+ end
124
+ end
125
+ end
126
+ end
127
+ rescue LoadError
128
+ # DB2 driver is unavailable.
129
+ end
@@ -25,6 +25,28 @@ module ActiveRecord
25
25
  end
26
26
 
27
27
  module ConnectionAdapters
28
+
29
+ class SQLiteColumn < Column
30
+
31
+ def string_to_binary(value)
32
+ value.gsub(/(\0|\%)/) do
33
+ case $1
34
+ when "\0" then "%00"
35
+ when "%" then "%25"
36
+ end
37
+ end
38
+ end
39
+
40
+ def binary_to_string(value)
41
+ value.gsub(/(%00|%25)/) do
42
+ case $1
43
+ when "%00" then "\0"
44
+ when "%25" then "%"
45
+ end
46
+ end
47
+ end
48
+
49
+ end
28
50
  class SQLiteAdapter < AbstractAdapter # :nodoc:
29
51
  def select_all(sql, name = nil)
30
52
  select(sql, name)
@@ -37,7 +59,7 @@ module ActiveRecord
37
59
 
38
60
  def columns(table_name, name = nil)
39
61
  table_structure(table_name).inject([]) do |columns, field|
40
- columns << Column.new(field['name'], field['dflt_value'], field['type'])
62
+ columns << SQLiteColumn.new(field['name'], field['dflt_value'], field['type'])
41
63
  columns
42
64
  end
43
65
  end
@@ -30,67 +30,26 @@ module ActiveRecord
30
30
  class Base
31
31
  def self.sqlserver_connection(config)
32
32
  require_library_or_gem 'dbi' unless self.class.const_defined?(:DBI)
33
- class_eval { include ActiveRecord::SQLServerBaseExtensions }
34
33
 
35
34
  symbolize_strings_in_hash(config)
36
35
 
37
- if config.has_key? :dsn
38
- dsn = config[:dsn]
36
+ host = config[:host]
37
+ username = config[:username] ? config[:username].to_s : 'sa'
38
+ password = config[:password].to_s
39
+
40
+ if config.has_key? :database
41
+ database = config[:database]
39
42
  else
40
- raise ArgumentError, "No DSN specified"
43
+ raise ArgumentError, "No database specified. Missing argument: database."
41
44
  end
42
45
 
43
- conn = DBI.connect(dsn)
46
+ conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User Id=#{username};Password=#{password};")
44
47
  conn["AutoCommit"] = true
45
48
 
46
49
  ConnectionAdapters::SQLServerAdapter.new(conn, logger)
47
50
  end
48
51
  end
49
52
 
50
- module SQLServerBaseExtensions #:nodoc:
51
- def self.append_features(base)
52
- super
53
- base.extend(ClassMethods)
54
- end
55
-
56
- module ClassMethods #:nodoc:
57
- def find_first(conditions = nil, orderings = nil)
58
- sql = "SELECT TOP 1 * FROM #{table_name} "
59
- add_conditions!(sql, conditions)
60
- sql << "ORDER BY #{orderings} " unless orderings.nil?
61
-
62
- record = connection.select_one(sql, "#{name} Load First")
63
- instantiate(record) unless record.nil?
64
- end
65
-
66
- def find_all(conditions = nil, orderings = nil, limit = nil, joins = nil)
67
- sql = "SELECT "
68
- sql << "TOP #{limit} " unless limit.nil?
69
- sql << " * FROM #{table_name} "
70
- sql << "#{joins} " if joins
71
- add_conditions!(sql, conditions)
72
- sql << "ORDER BY #{orderings} " unless orderings.nil?
73
-
74
- find_by_sql(sql)
75
- end
76
- end
77
-
78
- def attributes_with_quotes
79
- columns_hash = self.class.columns_hash
80
-
81
- attrs = @attributes.dup
82
-
83
- attrs = attrs.reject do |name, value|
84
- columns_hash[name].identity
85
- end
86
-
87
- attrs.inject({}) do |attrs_quoted, pair|
88
- attrs_quoted[pair.first] = quote(pair.last, columns_hash[pair.first])
89
- attrs_quoted
90
- end
91
- end
92
- end
93
-
94
53
  module ConnectionAdapters
95
54
  class ColumnWithIdentity < Column# :nodoc:
96
55
  attr_reader :identity
@@ -104,7 +63,7 @@ module ActiveRecord
104
63
 
105
64
  class SQLServerAdapter < AbstractAdapter # :nodoc:
106
65
  def quote_column_name(name)
107
- " [#{name}] "
66
+ "[#{name}]"
108
67
  end
109
68
 
110
69
  def select_all(sql, name = nil)
@@ -127,7 +86,6 @@ JOIN sysobjects AS s ON (c.id = s.id)
127
86
  LEFT OUTER JOIN syscomments AS com ON (c.cdefault = com.id)
128
87
  WHERE s.name = '#{table_name}'
129
88
  EOL
130
-
131
89
  columns = []
132
90
 
133
91
  log(sql, name, @connection) do |conn|
@@ -196,8 +154,12 @@ EOL
196
154
  end
197
155
  end
198
156
 
199
- alias_method :update, :execute
200
- alias_method :delete, :execute
157
+ def update(sql, name = nil)
158
+ execute(sql, name)
159
+ affected_rows(name)
160
+ end
161
+
162
+ alias_method :delete, :update
201
163
 
202
164
  def begin_db_transaction
203
165
  begin
@@ -236,63 +198,91 @@ EOL
236
198
  execute "CREATE DATABASE #{name}"
237
199
  end
238
200
 
201
+ def add_limit!(sql, limit)
202
+ limit_amount = limit.to_s.include?("OFFSET") ? get_offset_amount(limit) : Array.new([limit])
203
+ order_by = sql.include?("ORDER BY") ? get_order_by(sql.sub(/.*ORDER\sBY./, "")) : nil
204
+ if limit_amount.size == 2
205
+ sql.gsub!(/SELECT/i, "SELECT * FROM ( SELECT TOP #{limit_amount[0]} * FROM ( SELECT TOP #{limit_amount[1]}")<<" ) AS tmp1 ORDER BY #{order_by[1]} ) AS tmp2 ORDER BY #{order_by[0]}"
206
+ else
207
+ sql.gsub!(/SELECT/i, "SELECT TOP #{limit_amount[0]}")
208
+ end
209
+ end
210
+
239
211
  private
240
- def select(sql, name = nil)
241
- rows = []
212
+ def select(sql, name = nil)
213
+ rows = []
242
214
 
243
- log(sql, name, @connection) do |conn|
244
- conn.select_all(sql) do |row|
245
- record = {}
215
+ log(sql, name, @connection) do |conn|
216
+ conn.select_all(sql) do |row|
217
+ record = {}
246
218
 
247
- row.column_names.each do |col|
248
- record[col] = row[col]
249
- end
219
+ row.column_names.each do |col|
220
+ record[col] = row[col]
221
+ end
250
222
 
251
- rows << record
223
+ rows << record
224
+ end
252
225
  end
226
+
227
+ rows
253
228
  end
254
229
 
255
- rows
256
- end
230
+ def enable_identity_insert(table_name, enable = true)
231
+ if has_identity_column(table_name)
232
+ "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
233
+ end
234
+ end
257
235
 
258
- def enable_identity_insert(table_name, enable = true)
259
- if has_identity_column(table_name)
260
- "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
236
+ def get_table_name(sql)
237
+ if sql =~ /into\s*([^\s]+)\s*/i or
238
+ sql =~ /update\s*([^\s]+)\s*/i
239
+ $1
240
+ else
241
+ nil
242
+ end
261
243
  end
262
- end
263
244
 
264
- def get_table_name(sql)
265
- if sql =~ /into\s*([^\s]+)\s*/i or
266
- sql =~ /update\s*([^\s]+)\s*/i
267
- $1
268
- else
269
- nil
245
+ def has_identity_column(table_name)
246
+ return get_identity_column(table_name) != nil
270
247
  end
271
- end
272
248
 
273
- def has_identity_column(table_name)
274
- return get_identity_column(table_name) != nil
275
- end
249
+ def get_identity_column(table_name)
250
+ if not @table_columns
251
+ @table_columns = {}
252
+ end
253
+
254
+ if @table_columns[table_name] == nil
255
+ @table_columns[table_name] = columns(table_name)
256
+ end
276
257
 
277
- def get_identity_column(table_name)
278
- if not @table_columns
279
- @table_columns = {}
258
+ @table_columns[table_name].each do |col|
259
+ return col.name if col.identity
260
+ end
261
+
262
+ return nil
280
263
  end
281
264
 
282
- if @table_columns[table_name] == nil
283
- @table_columns[table_name] = columns(table_name)
265
+ def query_contains_identity_column(sql, col)
266
+ return sql =~ /[\(\.\,]\s*#{col}/
284
267
  end
285
268
 
286
- @table_columns[table_name].each do |col|
287
- return col.name if col.identity
269
+ def get_order_by(sql)
270
+ return sql, sql.gsub(/\s*DESC\s*/, "").gsub(/\s*ASC\s*/, " DESC")
288
271
  end
289
272
 
290
- return nil
291
- end
273
+ def get_offset_amount(limit)
274
+ limit = limit.gsub!(/.OFFSET./i, ",").split(',')
275
+ return limit[0].to_i, limit[0].to_i+limit[1].to_i
276
+ end
292
277
 
293
- def query_contains_identity_column(sql, col)
294
- return sql =~ /[\(\.\,]\s*#{col}/
295
- end
278
+ def affected_rows(name = nil)
279
+ sql = "SELECT @@ROWCOUNT AS AffectedRows"
280
+ log(sql, name, @connection) do |conn|
281
+ conn.select_all(sql) do |row|
282
+ return row[:AffectedRows].to_i
283
+ end
284
+ end
285
+ end
296
286
  end
297
287
  end
298
288
  end