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.
- data/CHANGELOG +77 -2
- data/install.rb +5 -0
- data/lib/active_record.rb +6 -2
- data/lib/active_record/acts/list.rb +56 -45
- data/lib/active_record/acts/tree.rb +3 -2
- data/lib/active_record/associations.rb +10 -62
- data/lib/active_record/associations/association_collection.rb +20 -23
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +36 -10
- data/lib/active_record/associations/has_many_association.rb +50 -25
- data/lib/active_record/base.rb +118 -80
- data/lib/active_record/callbacks.rb +51 -50
- data/lib/active_record/connection_adapters/abstract_adapter.rb +33 -12
- data/lib/active_record/connection_adapters/db2_adapter.rb +129 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +23 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +80 -90
- data/lib/active_record/fixtures.rb +1 -1
- data/lib/active_record/locking.rb +57 -0
- data/lib/active_record/support/class_attribute_accessors.rb +19 -5
- data/lib/active_record/support/class_inheritable_attributes.rb +4 -3
- data/lib/active_record/support/dependencies.rb +71 -0
- data/lib/active_record/support/inflector.rb +1 -0
- data/lib/active_record/support/misc.rb +29 -3
- data/lib/active_record/support/module_attribute_accessors.rb +57 -0
- data/lib/active_record/transactions.rb +18 -11
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record/vendor/db2.rb +357 -0
- data/lib/active_record/vendor/mysql.rb +1 -1
- data/rakefile +17 -5
- data/test/associations_test.rb +39 -4
- data/test/base_test.rb +13 -4
- data/test/binary_test.rb +43 -0
- data/test/callbacks_test.rb +230 -0
- data/test/connections/native_db2/connection.rb +24 -0
- data/test/connections/native_sqlserver/connection.rb +9 -3
- data/test/deprecated_associations_test.rb +16 -10
- data/test/finder_test.rb +65 -13
- data/test/fixtures/associations.png +0 -0
- data/test/fixtures/binary.rb +2 -0
- data/test/fixtures/companies.yml +21 -0
- data/test/fixtures/courses.yml +7 -0
- data/test/fixtures/customers.yml +7 -0
- data/test/fixtures/db_definitions/db2.sql +124 -0
- data/test/fixtures/db_definitions/db22.sql +4 -0
- data/test/fixtures/db_definitions/mysql.sql +12 -0
- data/test/fixtures/db_definitions/postgresql.sql +13 -0
- data/test/fixtures/db_definitions/sqlite.sql +9 -0
- data/test/fixtures/db_definitions/sqlserver.sql +13 -0
- data/test/fixtures/developers_projects.yml +13 -0
- data/test/fixtures/entrants.yml +14 -0
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/movies.yml +7 -0
- data/test/fixtures/people.yml +3 -0
- data/test/fixtures/person.rb +1 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/topics.yml +21 -0
- data/test/locking_test.rb +34 -0
- data/test/mixin_test.rb +6 -0
- data/test/validations_test.rb +1 -1
- metadata +33 -29
- data/test/fixtures/companies/first_client +0 -6
- data/test/fixtures/companies/first_firm +0 -4
- data/test/fixtures/companies/second_client +0 -6
- data/test/fixtures/courses/java +0 -2
- data/test/fixtures/courses/ruby +0 -2
- data/test/fixtures/customers/david +0 -6
- data/test/fixtures/entrants/first +0 -3
- data/test/fixtures/entrants/second +0 -3
- data/test/fixtures/entrants/third +0 -3
- data/test/fixtures/movies/first +0 -2
- data/test/fixtures/movies/second +0 -2
- data/test/fixtures/projects/action_controller +0 -2
- data/test/fixtures/projects/active_record +0 -2
- data/test/fixtures/topics/first +0 -10
- data/test/fixtures/topics/second +0 -8
- 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
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
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
|
-
#
|
46
|
-
#
|
47
|
-
#
|
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
|
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.
|
201
|
-
object.
|
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
|
-
|
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
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
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
|
-
|
330
|
-
|
331
|
-
|
329
|
+
def callbacks_for(method)
|
330
|
+
self.class.read_inheritable_attribute(method.to_sym) or []
|
331
|
+
end
|
332
332
|
|
333
|
-
|
334
|
-
|
335
|
-
|
333
|
+
def invoke_and_notify(method)
|
334
|
+
send(method) if respond_to_without_attributes?(method)
|
335
|
+
notify(method)
|
336
|
+
end
|
336
337
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
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.
|
233
|
+
Time.send(Base.default_timezone, *time_array) rescue nil
|
224
234
|
end
|
225
235
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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 /
|
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
|
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 <<
|
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
|
-
|
38
|
-
|
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
|
43
|
+
raise ArgumentError, "No database specified. Missing argument: database."
|
41
44
|
end
|
42
45
|
|
43
|
-
conn = DBI.connect(
|
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
|
-
"
|
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
|
-
|
200
|
-
|
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
|
-
|
241
|
-
|
212
|
+
def select(sql, name = nil)
|
213
|
+
rows = []
|
242
214
|
|
243
|
-
|
244
|
-
|
245
|
-
|
215
|
+
log(sql, name, @connection) do |conn|
|
216
|
+
conn.select_all(sql) do |row|
|
217
|
+
record = {}
|
246
218
|
|
247
|
-
|
248
|
-
|
249
|
-
|
219
|
+
row.column_names.each do |col|
|
220
|
+
record[col] = row[col]
|
221
|
+
end
|
250
222
|
|
251
|
-
|
223
|
+
rows << record
|
224
|
+
end
|
252
225
|
end
|
226
|
+
|
227
|
+
rows
|
253
228
|
end
|
254
229
|
|
255
|
-
|
256
|
-
|
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
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
265
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
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
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
283
|
-
|
265
|
+
def query_contains_identity_column(sql, col)
|
266
|
+
return sql =~ /[\(\.\,]\s*#{col}/
|
284
267
|
end
|
285
268
|
|
286
|
-
|
287
|
-
return
|
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
|
-
|
291
|
-
|
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
|
-
|
294
|
-
|
295
|
-
|
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
|