activerecord 1.1.0 → 1.2.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.
- data/CHANGELOG +250 -0
- data/README +17 -9
- data/dev-utils/eval_debugger.rb +1 -1
- data/install.rb +3 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/acts/list.rb +178 -0
- data/lib/active_record/acts/tree.rb +44 -0
- data/lib/active_record/associations.rb +45 -8
- data/lib/active_record/associations/association_collection.rb +18 -9
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +14 -13
- data/lib/active_record/associations/has_many_association.rb +21 -12
- data/lib/active_record/base.rb +137 -37
- data/lib/active_record/callbacks.rb +30 -25
- data/lib/active_record/connection_adapters/abstract_adapter.rb +57 -33
- data/lib/active_record/connection_adapters/mysql_adapter.rb +4 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +298 -0
- data/lib/active_record/fixtures.rb +241 -147
- data/lib/active_record/support/class_inheritable_attributes.rb +5 -2
- data/lib/active_record/support/inflector.rb +13 -12
- data/lib/active_record/support/misc.rb +6 -0
- data/lib/active_record/timestamp.rb +33 -0
- data/lib/active_record/transactions.rb +1 -1
- data/lib/active_record/validations.rb +294 -16
- data/rakefile +3 -7
- data/test/abstract_unit.rb +1 -4
- data/test/associations_test.rb +17 -4
- data/test/base_test.rb +37 -5
- data/test/connections/native_sqlserver/connection.rb +15 -0
- data/test/deprecated_associations_test.rb +40 -38
- data/test/finder_test.rb +82 -4
- data/test/fixtures/accounts.yml +8 -0
- data/test/fixtures/company.rb +6 -0
- data/test/fixtures/company_in_module.rb +1 -1
- data/test/fixtures/db_definitions/mysql.sql +13 -0
- data/test/fixtures/db_definitions/postgresql.sql +13 -0
- data/test/fixtures/db_definitions/sqlite.sql +14 -0
- data/test/fixtures/db_definitions/sqlserver.sql +110 -0
- data/test/fixtures/db_definitions/sqlserver2.sql +4 -0
- data/test/fixtures/developer.rb +2 -2
- data/test/fixtures/developers.yml +13 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/mixin.rb +17 -0
- data/test/fixtures/mixins.yml +14 -0
- data/test/fixtures/naked/csv/accounts.csv +1 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/project.rb +6 -0
- data/test/fixtures/reply.rb +14 -1
- data/test/fixtures/topic.rb +2 -2
- data/test/fixtures/topics/first +1 -0
- data/test/fixtures_test.rb +42 -12
- data/test/inflector_test.rb +2 -1
- data/test/inheritance_test.rb +22 -12
- data/test/mixin_test.rb +138 -0
- data/test/pk_test.rb +4 -2
- data/test/reflection_test.rb +3 -3
- data/test/transactions_test.rb +15 -0
- data/test/validations_test.rb +229 -4
- metadata +24 -10
- data/lib/active_record/associations.rb.orig +0 -555
- data/test/deprecated_associations_test.rb.orig +0 -334
- data/test/fixtures/accounts/signals37 +0 -3
- data/test/fixtures/accounts/unknown +0 -2
- data/test/fixtures/developers/david +0 -2
- data/test/fixtures/developers/jamis +0 -2
@@ -2,7 +2,7 @@ require 'observer'
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
# Callbacks are hooks into the lifecycle of an Active Record object that allows you to trigger logic
|
5
|
-
# before or after an alteration of the object state. This can be used to make sure that associated and
|
5
|
+
# before or after an alteration of the object state. This can be used to make sure that associated and
|
6
6
|
# dependent objects are deleted when destroy is called (by overwriting before_destroy) or to massage attributes
|
7
7
|
# before they're validated (by overwriting before_validation). As an example of the callbacks initiated, consider
|
8
8
|
# the Base#save call:
|
@@ -14,19 +14,19 @@ module ActiveRecord
|
|
14
14
|
# * (-) validate
|
15
15
|
# * (-) validate_on_create
|
16
16
|
# * (4) after_validation
|
17
|
-
# * (5) after_validation_on_create
|
17
|
+
# * (5) after_validation_on_create
|
18
18
|
# * (6) before_save
|
19
19
|
# * (7) before_create
|
20
20
|
# * (-) create
|
21
21
|
# * (8) after_create
|
22
22
|
# * (9) after_save
|
23
|
-
#
|
23
|
+
#
|
24
24
|
# That's a total of nine callbacks, which gives you immense power to react and prepare for each state in the
|
25
25
|
# Active Record lifecyle.
|
26
26
|
#
|
27
27
|
# Examples:
|
28
28
|
# class CreditCard < ActiveRecord::Base
|
29
|
-
# # Strip everything but digits, so the user can specify "555 234 34" or
|
29
|
+
# # Strip everything but digits, so the user can specify "555 234 34" or
|
30
30
|
# # "5552-3434" or both will mean "55523434"
|
31
31
|
# def before_validation_on_create
|
32
32
|
# self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
|
@@ -73,13 +73,13 @@ module ActiveRecord
|
|
73
73
|
# def before_destroy() destroy_readers end
|
74
74
|
# end
|
75
75
|
#
|
76
|
-
# In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So use the callback macros when
|
76
|
+
# In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So use the callback macros when
|
77
77
|
# you want to ensure that a certain callback is called for the entire hierarchy and the regular overwriteable methods when you
|
78
78
|
# want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
|
79
79
|
#
|
80
80
|
# == Types of callbacks
|
81
81
|
#
|
82
|
-
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
|
82
|
+
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
|
83
83
|
# inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects are the
|
84
84
|
# recommended approaches, inline methods using a proc is some times appropriate (such as for creating mix-ins), and inline
|
85
85
|
# eval methods are deprecated.
|
@@ -115,7 +115,7 @@ module ActiveRecord
|
|
115
115
|
# def after_save(record)
|
116
116
|
# record.credit_card_number = decrypt(record.credit_card_number)
|
117
117
|
# end
|
118
|
-
#
|
118
|
+
#
|
119
119
|
# alias_method :after_initialize, :after_save
|
120
120
|
#
|
121
121
|
# private
|
@@ -142,7 +142,7 @@ module ActiveRecord
|
|
142
142
|
# inline callbacks can be stacked just like the regular ones:
|
143
143
|
#
|
144
144
|
# class Topic < ActiveRecord::Base
|
145
|
-
# before_destroy 'self.class.delete_all "parent_id = #{id}"',
|
145
|
+
# before_destroy 'self.class.delete_all "parent_id = #{id}"',
|
146
146
|
# 'puts "Evaluated after parents are destroyed"'
|
147
147
|
# end
|
148
148
|
#
|
@@ -153,8 +153,8 @@ module ActiveRecord
|
|
153
153
|
# after_initialize can only be declared using an explicit implementation. So using the inheritable callback queue for after_find and
|
154
154
|
# after_initialize won't work.
|
155
155
|
module Callbacks
|
156
|
-
CALLBACKS = %w(
|
157
|
-
after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
|
156
|
+
CALLBACKS = %w(
|
157
|
+
after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation
|
158
158
|
after_validation before_validation_on_create after_validation_on_create before_validation_on_update
|
159
159
|
after_validation_on_update before_destroy after_destroy
|
160
160
|
)
|
@@ -210,10 +210,11 @@ module ActiveRecord
|
|
210
210
|
# def after_initialize() end
|
211
211
|
def initialize_with_callbacks(attributes = nil) #:nodoc:
|
212
212
|
initialize_without_callbacks(attributes)
|
213
|
-
yield self if block_given?
|
213
|
+
result = yield self if block_given?
|
214
214
|
after_initialize if respond_to_without_attributes?(:after_initialize)
|
215
|
+
result
|
215
216
|
end
|
216
|
-
|
217
|
+
|
217
218
|
# Is called _before_ Base.save (regardless of whether it's a create or update save).
|
218
219
|
def before_save() end
|
219
220
|
|
@@ -221,8 +222,9 @@ module ActiveRecord
|
|
221
222
|
def after_save() end
|
222
223
|
def create_or_update_with_callbacks #:nodoc:
|
223
224
|
callback(:before_save)
|
224
|
-
create_or_update_without_callbacks
|
225
|
+
result = create_or_update_without_callbacks
|
225
226
|
callback(:after_save)
|
227
|
+
result
|
226
228
|
end
|
227
229
|
|
228
230
|
# Is called _before_ Base.save on new objects that haven't been saved yet (no record exists).
|
@@ -232,8 +234,9 @@ module ActiveRecord
|
|
232
234
|
def after_create() end
|
233
235
|
def create_with_callbacks #:nodoc:
|
234
236
|
callback(:before_create)
|
235
|
-
create_without_callbacks
|
237
|
+
result = create_without_callbacks
|
236
238
|
callback(:after_create)
|
239
|
+
result
|
237
240
|
end
|
238
241
|
|
239
242
|
# Is called _before_ Base.save on existing objects that has a record.
|
@@ -244,8 +247,9 @@ module ActiveRecord
|
|
244
247
|
|
245
248
|
def update_with_callbacks #:nodoc:
|
246
249
|
callback(:before_update)
|
247
|
-
update_without_callbacks
|
250
|
+
result = update_without_callbacks
|
248
251
|
callback(:after_update)
|
252
|
+
result
|
249
253
|
end
|
250
254
|
|
251
255
|
# Is called _before_ Validations.validate (which is part of the Base.save call).
|
@@ -262,11 +266,11 @@ module ActiveRecord
|
|
262
266
|
# that haven't been saved yet (no record exists).
|
263
267
|
def after_validation_on_create() end
|
264
268
|
|
265
|
-
# Is called _before_ Validations.validate (which is part of the Base.save call) on
|
269
|
+
# Is called _before_ Validations.validate (which is part of the Base.save call) on
|
266
270
|
# existing objects that has a record.
|
267
271
|
def before_validation_on_update() end
|
268
272
|
|
269
|
-
# Is called _after_ Validations.validate (which is part of the Base.save call) on
|
273
|
+
# Is called _after_ Validations.validate (which is part of the Base.save call) on
|
270
274
|
# existing objects that has a record.
|
271
275
|
def after_validation_on_update() end
|
272
276
|
|
@@ -278,7 +282,7 @@ module ActiveRecord
|
|
278
282
|
|
279
283
|
callback(:after_validation)
|
280
284
|
if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end
|
281
|
-
|
285
|
+
|
282
286
|
return result
|
283
287
|
end
|
284
288
|
|
@@ -289,8 +293,9 @@ module ActiveRecord
|
|
289
293
|
def after_destroy() end
|
290
294
|
def destroy_with_callbacks #:nodoc:
|
291
295
|
callback(:before_destroy)
|
292
|
-
destroy_without_callbacks
|
296
|
+
result = destroy_without_callbacks
|
293
297
|
callback(:after_destroy)
|
298
|
+
result
|
294
299
|
end
|
295
300
|
|
296
301
|
def callback(callback_method) #:nodoc:
|
@@ -302,7 +307,7 @@ module ActiveRecord
|
|
302
307
|
def run_callbacks(callback_method)
|
303
308
|
filters = self.class.read_inheritable_attribute(callback_method.to_s)
|
304
309
|
if filters.nil? then return end
|
305
|
-
filters.each do |filter|
|
310
|
+
filters.each do |filter|
|
306
311
|
if Symbol === filter
|
307
312
|
self.send(filter)
|
308
313
|
elsif String === filter
|
@@ -313,25 +318,25 @@ module ActiveRecord
|
|
313
318
|
filter.send(callback_method, self)
|
314
319
|
else
|
315
320
|
raise(
|
316
|
-
ActiveRecordError,
|
321
|
+
ActiveRecordError,
|
317
322
|
"Filters need to be either a symbol, string (to be eval'ed), proc/method, or " +
|
318
323
|
"class implementing a static filter method"
|
319
324
|
)
|
320
325
|
end
|
321
326
|
end
|
322
327
|
end
|
323
|
-
|
328
|
+
|
324
329
|
def filter_block?(filter)
|
325
330
|
filter.respond_to?("call") && (filter.arity == 1 || filter.arity == -1)
|
326
331
|
end
|
327
|
-
|
332
|
+
|
328
333
|
def filter_class?(filter, callback_method)
|
329
334
|
filter.respond_to?(callback_method)
|
330
335
|
end
|
331
|
-
|
336
|
+
|
332
337
|
def notify(callback_method) #:nodoc:
|
333
338
|
self.class.changed
|
334
339
|
self.class.notify_observers(callback_method, self)
|
335
340
|
end
|
336
341
|
end
|
337
|
-
end
|
342
|
+
end
|
@@ -75,21 +75,30 @@ module ActiveRecord
|
|
75
75
|
# end
|
76
76
|
#
|
77
77
|
# Courses.establish_connection( ... )
|
78
|
-
def self.establish_connection(spec)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
78
|
+
def self.establish_connection(spec = nil)
|
79
|
+
case spec
|
80
|
+
when nil
|
81
|
+
raise AdapterNotSpecified unless defined? RAILS_ENV
|
82
|
+
establish_connection(RAILS_ENV)
|
83
|
+
when ConnectionSpecification
|
84
|
+
@@defined_connections[self] = spec
|
85
|
+
when Symbol, String
|
86
|
+
if configuration = configurations[spec.to_s]
|
87
|
+
establish_connection(configuration)
|
88
|
+
else
|
89
|
+
raise AdapterNotSpecified
|
90
|
+
end
|
91
|
+
else
|
92
|
+
spec = symbolize_strings_in_hash(spec)
|
93
|
+
unless spec.key?(:adapter) then raise AdapterNotSpecified end
|
94
|
+
|
95
|
+
adapter_method = "#{spec[:adapter]}_connection"
|
96
|
+
unless respond_to?(adapter_method) then raise AdapterNotFound end
|
97
|
+
remove_connection
|
98
|
+
establish_connection(ConnectionSpecification.new(spec, adapter_method))
|
90
99
|
end
|
91
100
|
end
|
92
|
-
|
101
|
+
|
93
102
|
# Locate the connection of the nearest super class. This can be an
|
94
103
|
# active or defined connections: if it is the latter, it will be
|
95
104
|
# opened and set as the active connection for the class it was defined
|
@@ -144,11 +153,9 @@ module ActiveRecord
|
|
144
153
|
|
145
154
|
# Converts all strings in a hash to symbols.
|
146
155
|
def self.symbolize_strings_in_hash(hash)
|
147
|
-
hash.
|
148
|
-
|
149
|
-
|
150
|
-
hash[key.intern] = value
|
151
|
-
end
|
156
|
+
hash.inject({}) do |hash_with_symbolized_strings, pair|
|
157
|
+
hash_with_symbolized_strings[pair.first.to_sym] = pair.last
|
158
|
+
hash_with_symbolized_strings
|
152
159
|
end
|
153
160
|
end
|
154
161
|
end
|
@@ -175,6 +182,8 @@ module ActiveRecord
|
|
175
182
|
when :float then Float
|
176
183
|
when :datetime then Time
|
177
184
|
when :date then Date
|
185
|
+
when :timestamp then Time
|
186
|
+
when :time then Time
|
178
187
|
when :text, :string then String
|
179
188
|
when :boolean then Object
|
180
189
|
end
|
@@ -183,13 +192,15 @@ module ActiveRecord
|
|
183
192
|
def type_cast(value)
|
184
193
|
if value.nil? then return nil end
|
185
194
|
case type
|
186
|
-
when :string
|
187
|
-
when :text
|
188
|
-
when :integer
|
189
|
-
when :float
|
190
|
-
when :datetime
|
191
|
-
when :
|
192
|
-
when :
|
195
|
+
when :string then value
|
196
|
+
when :text then value
|
197
|
+
when :integer then value.to_i
|
198
|
+
when :float then value.to_f
|
199
|
+
when :datetime then string_to_time(value)
|
200
|
+
when :timestamp then string_to_time(value)
|
201
|
+
when :time then string_to_dummy_time(value)
|
202
|
+
when :date then string_to_date(value)
|
203
|
+
when :boolean then (value == "t" or value == true ? true : false)
|
193
204
|
else value
|
194
205
|
end
|
195
206
|
end
|
@@ -213,6 +224,14 @@ module ActiveRecord
|
|
213
224
|
Time.local(*time_array) rescue nil
|
214
225
|
end
|
215
226
|
|
227
|
+
def string_to_dummy_time(string)
|
228
|
+
return string if Time === string
|
229
|
+
time_array = ParseDate.parsedate(string)
|
230
|
+
# pad the resulting array with dummy date information
|
231
|
+
time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
|
232
|
+
Time.local(*time_array) rescue nil
|
233
|
+
end
|
234
|
+
|
216
235
|
def extract_limit(sql_type)
|
217
236
|
$1.to_i if sql_type =~ /\((.*)\)/
|
218
237
|
end
|
@@ -223,8 +242,12 @@ module ActiveRecord
|
|
223
242
|
:integer
|
224
243
|
when /float|double|decimal|numeric/i
|
225
244
|
:float
|
226
|
-
when /
|
245
|
+
when /datetime/i
|
227
246
|
:datetime
|
247
|
+
when /timestamp/i
|
248
|
+
:timestamp
|
249
|
+
when /time/i
|
250
|
+
:time
|
228
251
|
when /date/i
|
229
252
|
:date
|
230
253
|
when /(c|b)lob/i, /text/i
|
@@ -301,13 +324,14 @@ module ActiveRecord
|
|
301
324
|
|
302
325
|
def quote(value, column = nil)
|
303
326
|
case value
|
304
|
-
when String
|
305
|
-
when NilClass
|
306
|
-
when TrueClass
|
307
|
-
when FalseClass
|
308
|
-
when Float, Fixnum, Bignum
|
309
|
-
when
|
310
|
-
|
327
|
+
when String then "'#{quote_string(value)}'" # ' (for ruby-mode)
|
328
|
+
when NilClass then "NULL"
|
329
|
+
when TrueClass then (column && column.type == :boolean ? "'t'" : "1")
|
330
|
+
when FalseClass then (column && column.type == :boolean ? "'f'" : "0")
|
331
|
+
when Float, Fixnum, Bignum then value.to_s
|
332
|
+
when Date then "'#{value.to_s}'"
|
333
|
+
when Time, DateTime then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
334
|
+
else "'#{quote_string(value.to_yaml)}'"
|
311
335
|
end
|
312
336
|
end
|
313
337
|
|
@@ -12,7 +12,8 @@ module ActiveRecord
|
|
12
12
|
unless config.has_key?(:dbfile)
|
13
13
|
raise ArgumentError, "No database file specified. Missing argument: dbfile"
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
|
+
config[:dbfile] = File.expand_path(config[:dbfile], RAILS_ROOT) if Object.const_defined?(:RAILS_ROOT)
|
16
17
|
db = SQLite::Database.new(config[:dbfile], 0)
|
17
18
|
|
18
19
|
db.show_datatypes = "ON" if !defined? SQLite::Version
|
@@ -102,4 +103,4 @@ module ActiveRecord
|
|
102
103
|
end
|
103
104
|
end
|
104
105
|
end
|
105
|
-
end
|
106
|
+
end
|
@@ -0,0 +1,298 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
2
|
+
|
3
|
+
# sqlserver_adapter.rb -- ActiveRecord adapter for Microsoft SQL Server
|
4
|
+
#
|
5
|
+
# Author: Joey Gibson <joey@joeygibson.com>
|
6
|
+
# Date: 10/14/2004
|
7
|
+
#
|
8
|
+
# REQUIREMENTS:
|
9
|
+
#
|
10
|
+
# This adapter will ONLY work on Windows systems, since it relies on Win32OLE, which,
|
11
|
+
# to my knowledge, is only available on Window.
|
12
|
+
#
|
13
|
+
# It relies on the ADO support in the DBI module. If you are using the
|
14
|
+
# one-click installer of Ruby, then you already have DBI installed, but
|
15
|
+
# the ADO module is *NOT* installed. You will need to get the latest
|
16
|
+
# source distribution of Ruby-DBI from http://ruby-dbi.sourceforge.net/
|
17
|
+
# unzip it, and copy the file src/lib/dbd_ado/ADO.rb to
|
18
|
+
# X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb (you will need to create
|
19
|
+
# the ADO directory). Once you've installed that file, you are ready to go.
|
20
|
+
#
|
21
|
+
# This module uses the ADO-style DSNs for connection. For example:
|
22
|
+
# "DBI:ADO:Provider=SQLOLEDB;Data Source=(local);Initial Catalog=test;User Id=sa;Password=password;"
|
23
|
+
# with User Id replaced with your proper login, and Password with your
|
24
|
+
# password.
|
25
|
+
#
|
26
|
+
# I have tested this code on a WindowsXP Pro SP1 system,
|
27
|
+
# ruby 1.8.2 (2004-07-29) [i386-mswin32], SQL Server 2000.
|
28
|
+
#
|
29
|
+
module ActiveRecord
|
30
|
+
class Base
|
31
|
+
def self.sqlserver_connection(config)
|
32
|
+
require_library_or_gem 'dbi' unless self.class.const_defined?(:DBI)
|
33
|
+
class_eval { include ActiveRecord::SQLServerBaseExtensions }
|
34
|
+
|
35
|
+
symbolize_strings_in_hash(config)
|
36
|
+
|
37
|
+
if config.has_key? :dsn
|
38
|
+
dsn = config[:dsn]
|
39
|
+
else
|
40
|
+
raise ArgumentError, "No DSN specified"
|
41
|
+
end
|
42
|
+
|
43
|
+
conn = DBI.connect(dsn)
|
44
|
+
conn["AutoCommit"] = true
|
45
|
+
|
46
|
+
ConnectionAdapters::SQLServerAdapter.new(conn, logger)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
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
|
+
module ConnectionAdapters
|
95
|
+
class ColumnWithIdentity < Column# :nodoc:
|
96
|
+
attr_reader :identity
|
97
|
+
|
98
|
+
def initialize(name, default, sql_type = nil, is_identity = false)
|
99
|
+
super(name, default, sql_type)
|
100
|
+
|
101
|
+
@identity = is_identity
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class SQLServerAdapter < AbstractAdapter # :nodoc:
|
106
|
+
def quote_column_name(name)
|
107
|
+
" [#{name}] "
|
108
|
+
end
|
109
|
+
|
110
|
+
def select_all(sql, name = nil)
|
111
|
+
select(sql, name)
|
112
|
+
end
|
113
|
+
|
114
|
+
def select_one(sql, name = nil)
|
115
|
+
result = select(sql, name)
|
116
|
+
result.nil? ? nil : result.first
|
117
|
+
end
|
118
|
+
|
119
|
+
def columns(table_name, name = nil)
|
120
|
+
sql = <<EOL
|
121
|
+
SELECT s.name AS TableName, c.id AS ColId, c.name AS ColName, t.name AS ColType, c.length AS Length,
|
122
|
+
c.AutoVal AS IsIdentity,
|
123
|
+
c.cdefault AS DefaultId, com.text AS DefaultValue
|
124
|
+
FROM syscolumns AS c
|
125
|
+
JOIN systypes AS t ON (c.xtype = t.xtype AND c.usertype = t.usertype)
|
126
|
+
JOIN sysobjects AS s ON (c.id = s.id)
|
127
|
+
LEFT OUTER JOIN syscomments AS com ON (c.cdefault = com.id)
|
128
|
+
WHERE s.name = '#{table_name}'
|
129
|
+
EOL
|
130
|
+
|
131
|
+
columns = []
|
132
|
+
|
133
|
+
log(sql, name, @connection) do |conn|
|
134
|
+
conn.select_all(sql) do |row|
|
135
|
+
default_value = row[:DefaultValue]
|
136
|
+
|
137
|
+
if default_value =~ /null/i
|
138
|
+
default_value = nil
|
139
|
+
else
|
140
|
+
default_value =~ /\(([^)]+)\)/
|
141
|
+
default_value = $1
|
142
|
+
end
|
143
|
+
|
144
|
+
col = ColumnWithIdentity.new(row[:ColName], default_value, "#{row[:ColType]}(#{row[:Length]})", row[:IsIdentity] != nil)
|
145
|
+
|
146
|
+
columns << col
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
columns
|
151
|
+
end
|
152
|
+
|
153
|
+
def insert(sql, name = nil, pk = nil, id_value = nil)
|
154
|
+
begin
|
155
|
+
table_name = get_table_name(sql)
|
156
|
+
|
157
|
+
col = get_identity_column(table_name)
|
158
|
+
|
159
|
+
ii_enabled = false
|
160
|
+
|
161
|
+
if col != nil
|
162
|
+
if query_contains_identity_column(sql, col)
|
163
|
+
begin
|
164
|
+
execute enable_identity_insert(table_name, true)
|
165
|
+
ii_enabled = true
|
166
|
+
rescue Exception => e
|
167
|
+
# Coulnd't turn on IDENTITY_INSERT
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
log(sql, name, @connection) do |conn|
|
173
|
+
conn.execute(sql)
|
174
|
+
|
175
|
+
select_one("SELECT @@IDENTITY AS Ident")["Ident"]
|
176
|
+
end
|
177
|
+
ensure
|
178
|
+
if ii_enabled
|
179
|
+
begin
|
180
|
+
execute enable_identity_insert(table_name, false)
|
181
|
+
|
182
|
+
rescue Exception => e
|
183
|
+
# Couldn't turn off IDENTITY_INSERT
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def execute(sql, name = nil)
|
190
|
+
if sql =~ /^INSERT/i
|
191
|
+
insert(sql, name)
|
192
|
+
else
|
193
|
+
log(sql, name, @connection) do |conn|
|
194
|
+
conn.execute(sql)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
alias_method :update, :execute
|
200
|
+
alias_method :delete, :execute
|
201
|
+
|
202
|
+
def begin_db_transaction
|
203
|
+
begin
|
204
|
+
@connection["AutoCommit"] = false
|
205
|
+
rescue Exception => e
|
206
|
+
@connection["AutoCommit"] = true
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def commit_db_transaction
|
211
|
+
begin
|
212
|
+
@connection.commit
|
213
|
+
ensure
|
214
|
+
@connection["AutoCommit"] = true
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def rollback_db_transaction
|
219
|
+
begin
|
220
|
+
@connection.rollback
|
221
|
+
ensure
|
222
|
+
@connection["AutoCommit"] = true
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def recreate_database(name)
|
227
|
+
drop_database(name)
|
228
|
+
create_database(name)
|
229
|
+
end
|
230
|
+
|
231
|
+
def drop_database(name)
|
232
|
+
execute "DROP DATABASE #{name}"
|
233
|
+
end
|
234
|
+
|
235
|
+
def create_database(name)
|
236
|
+
execute "CREATE DATABASE #{name}"
|
237
|
+
end
|
238
|
+
|
239
|
+
private
|
240
|
+
def select(sql, name = nil)
|
241
|
+
rows = []
|
242
|
+
|
243
|
+
log(sql, name, @connection) do |conn|
|
244
|
+
conn.select_all(sql) do |row|
|
245
|
+
record = {}
|
246
|
+
|
247
|
+
row.column_names.each do |col|
|
248
|
+
record[col] = row[col]
|
249
|
+
end
|
250
|
+
|
251
|
+
rows << record
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
rows
|
256
|
+
end
|
257
|
+
|
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'}"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
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
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def has_identity_column(table_name)
|
274
|
+
return get_identity_column(table_name) != nil
|
275
|
+
end
|
276
|
+
|
277
|
+
def get_identity_column(table_name)
|
278
|
+
if not @table_columns
|
279
|
+
@table_columns = {}
|
280
|
+
end
|
281
|
+
|
282
|
+
if @table_columns[table_name] == nil
|
283
|
+
@table_columns[table_name] = columns(table_name)
|
284
|
+
end
|
285
|
+
|
286
|
+
@table_columns[table_name].each do |col|
|
287
|
+
return col.name if col.identity
|
288
|
+
end
|
289
|
+
|
290
|
+
return nil
|
291
|
+
end
|
292
|
+
|
293
|
+
def query_contains_identity_column(sql, col)
|
294
|
+
return sql =~ /[\(\.\,]\s*#{col}/
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|