activerecord 2.2.3 → 2.3.2
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 +438 -396
- data/Rakefile +4 -2
- data/lib/active_record.rb +46 -43
- data/lib/active_record/association_preload.rb +34 -19
- data/lib/active_record/associations.rb +193 -251
- data/lib/active_record/associations/association_collection.rb +38 -21
- data/lib/active_record/associations/association_proxy.rb +11 -4
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -2
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +8 -8
- data/lib/active_record/associations/has_one_association.rb +11 -2
- data/lib/active_record/attribute_methods.rb +1 -0
- data/lib/active_record/autosave_association.rb +349 -0
- data/lib/active_record/base.rb +292 -106
- data/lib/active_record/batches.rb +73 -0
- data/lib/active_record/calculations.rb +34 -16
- data/lib/active_record/callbacks.rb +37 -8
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +16 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +103 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +28 -25
- data/lib/active_record/connection_adapters/abstract_adapter.rb +29 -5
- data/lib/active_record/connection_adapters/mysql_adapter.rb +50 -21
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -41
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +41 -21
- data/lib/active_record/dirty.rb +1 -1
- data/lib/active_record/dynamic_scope_match.rb +25 -0
- data/lib/active_record/fixtures.rb +193 -198
- data/lib/active_record/locale/en.yml +1 -1
- data/lib/active_record/locking/optimistic.rb +33 -0
- data/lib/active_record/migration.rb +8 -2
- data/lib/active_record/named_scope.rb +13 -6
- data/lib/active_record/nested_attributes.rb +329 -0
- data/lib/active_record/query_cache.rb +25 -13
- data/lib/active_record/reflection.rb +6 -1
- data/lib/active_record/schema_dumper.rb +2 -0
- data/lib/active_record/serialization.rb +3 -1
- data/lib/active_record/serializers/json_serializer.rb +19 -0
- data/lib/active_record/serializers/xml_serializer.rb +28 -13
- data/lib/active_record/session_store.rb +318 -0
- data/lib/active_record/test_case.rb +15 -9
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/transactions.rb +58 -8
- data/lib/active_record/validations.rb +29 -24
- data/lib/active_record/version.rb +2 -2
- data/test/cases/ar_schema_test.rb +0 -1
- data/test/cases/associations/belongs_to_associations_test.rb +35 -131
- data/test/cases/associations/cascaded_eager_loading_test.rb +8 -0
- data/test/cases/associations/eager_load_nested_include_test.rb +29 -0
- data/test/cases/associations/eager_test.rb +137 -7
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +45 -7
- data/test/cases/associations/has_many_associations_test.rb +110 -149
- data/test/cases/associations/has_many_through_associations_test.rb +39 -7
- data/test/cases/associations/has_one_associations_test.rb +39 -92
- data/test/cases/associations/has_one_through_associations_test.rb +34 -3
- data/test/cases/associations/inner_join_association_test.rb +0 -5
- data/test/cases/associations/join_model_test.rb +5 -7
- data/test/cases/attribute_methods_test.rb +13 -1
- data/test/cases/autosave_association_test.rb +901 -0
- data/test/cases/base_test.rb +41 -21
- data/test/cases/batches_test.rb +61 -0
- data/test/cases/calculations_test.rb +37 -17
- data/test/cases/callbacks_test.rb +43 -5
- data/test/cases/connection_pool_test.rb +25 -0
- data/test/cases/copy_table_test_sqlite.rb +11 -0
- data/test/cases/datatype_test_postgresql.rb +1 -0
- data/test/cases/defaults_test.rb +37 -26
- data/test/cases/dirty_test.rb +26 -2
- data/test/cases/finder_test.rb +79 -44
- data/test/cases/fixtures_test.rb +15 -19
- data/test/cases/helper.rb +26 -19
- data/test/cases/inheritance_test.rb +2 -2
- data/test/cases/json_serialization_test.rb +1 -1
- data/test/cases/locking_test.rb +23 -5
- data/test/cases/method_scoping_test.rb +126 -3
- data/test/cases/migration_test.rb +253 -237
- data/test/cases/named_scope_test.rb +73 -3
- data/test/cases/nested_attributes_test.rb +509 -0
- data/test/cases/query_cache_test.rb +0 -4
- data/test/cases/reflection_test.rb +13 -3
- data/test/cases/reload_models_test.rb +3 -1
- data/test/cases/repair_helper.rb +50 -0
- data/test/cases/schema_dumper_test.rb +0 -1
- data/test/cases/transactions_test.rb +177 -12
- data/test/cases/validations_i18n_test.rb +288 -294
- data/test/cases/validations_test.rb +230 -180
- data/test/cases/xml_serialization_test.rb +19 -1
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/fixtures/member_types.yml +6 -0
- data/test/fixtures/members.yml +3 -1
- data/test/fixtures/people.yml +10 -1
- data/test/fixtures/toys.yml +4 -0
- data/test/models/author.rb +1 -2
- data/test/models/bird.rb +3 -0
- data/test/models/category.rb +1 -0
- data/test/models/company.rb +3 -0
- data/test/models/developer.rb +12 -0
- data/test/models/event.rb +3 -0
- data/test/models/member.rb +1 -0
- data/test/models/member_detail.rb +1 -0
- data/test/models/member_type.rb +3 -0
- data/test/models/owner.rb +2 -1
- data/test/models/parrot.rb +2 -0
- data/test/models/person.rb +6 -0
- data/test/models/pet.rb +2 -1
- data/test/models/pirate.rb +55 -1
- data/test/models/post.rb +6 -0
- data/test/models/project.rb +1 -0
- data/test/models/reply.rb +6 -0
- data/test/models/ship.rb +8 -1
- data/test/models/ship_part.rb +5 -0
- data/test/models/topic.rb +13 -1
- data/test/models/toy.rb +4 -0
- data/test/schema/schema.rb +35 -2
- metadata +70 -9
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
@@ -14,12 +14,12 @@ module ActiveRecord
|
|
14
14
|
def dirties_query_cache(base, *method_names)
|
15
15
|
method_names.each do |method_name|
|
16
16
|
base.class_eval <<-end_code, __FILE__, __LINE__
|
17
|
-
def #{method_name}_with_query_dirty(*args)
|
18
|
-
clear_query_cache if @query_cache_enabled
|
19
|
-
#{method_name}_without_query_dirty(*args)
|
20
|
-
end
|
21
|
-
|
22
|
-
alias_method_chain :#{method_name}, :query_dirty
|
17
|
+
def #{method_name}_with_query_dirty(*args) # def update_with_query_dirty(*args)
|
18
|
+
clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
|
19
|
+
#{method_name}_without_query_dirty(*args) # update_without_query_dirty(*args)
|
20
|
+
end # end
|
21
|
+
#
|
22
|
+
alias_method_chain :#{method_name}, :query_dirty # alias_method_chain :update, :query_dirty
|
23
23
|
end_code
|
24
24
|
end
|
25
25
|
end
|
@@ -8,6 +8,7 @@ module ActiveRecord
|
|
8
8
|
# An abstract definition of a column in a table.
|
9
9
|
class Column
|
10
10
|
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
|
11
|
+
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
|
11
12
|
|
12
13
|
module Format
|
13
14
|
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
|
@@ -32,10 +33,12 @@ module ActiveRecord
|
|
32
33
|
@primary = nil
|
33
34
|
end
|
34
35
|
|
36
|
+
# Returns +true+ if the column is either of type string or text.
|
35
37
|
def text?
|
36
38
|
type == :string || type == :text
|
37
39
|
end
|
38
40
|
|
41
|
+
# Returns +true+ if the column is either of type integer, float or decimal.
|
39
42
|
def number?
|
40
43
|
type == :integer || type == :float || type == :decimal
|
41
44
|
end
|
@@ -295,7 +298,7 @@ module ActiveRecord
|
|
295
298
|
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
|
296
299
|
# end
|
297
300
|
# end
|
298
|
-
#
|
301
|
+
#
|
299
302
|
# def self.down
|
300
303
|
# ...
|
301
304
|
# end
|
@@ -474,12 +477,12 @@ module ActiveRecord
|
|
474
477
|
|
475
478
|
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
|
476
479
|
class_eval <<-EOV
|
477
|
-
def #{column_type}(*args)
|
478
|
-
options = args.extract_options!
|
479
|
-
column_names = args
|
480
|
-
|
481
|
-
column_names.each { |name| column(name, '#{column_type}', options) }
|
482
|
-
end
|
480
|
+
def #{column_type}(*args) # def string(*args)
|
481
|
+
options = args.extract_options! # options = args.extract_options!
|
482
|
+
column_names = args # column_names = args
|
483
|
+
#
|
484
|
+
column_names.each { |name| column(name, '#{column_type}', options) } # column_names.each { |name| column(name, 'string', options) }
|
485
|
+
end # end
|
483
486
|
EOV
|
484
487
|
end
|
485
488
|
|
@@ -674,24 +677,24 @@ module ActiveRecord
|
|
674
677
|
# t.string(:goat, :sheep)
|
675
678
|
%w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
|
676
679
|
class_eval <<-EOV
|
677
|
-
def #{column_type}(*args)
|
678
|
-
options = args.extract_options!
|
679
|
-
column_names = args
|
680
|
-
|
681
|
-
column_names.each do |name|
|
682
|
-
column = ColumnDefinition.new(@base, name, '#{column_type}')
|
683
|
-
if options[:limit]
|
684
|
-
column.limit = options[:limit]
|
685
|
-
elsif native['#{column_type}'.to_sym].is_a?(Hash)
|
686
|
-
column.limit = native['#{column_type}'.to_sym][:limit]
|
687
|
-
end
|
688
|
-
column.precision = options[:precision]
|
689
|
-
column.scale = options[:scale]
|
690
|
-
column.default = options[:default]
|
691
|
-
column.null = options[:null]
|
692
|
-
@base.add_column(@table_name, name, column.sql_type, options)
|
693
|
-
end
|
694
|
-
end
|
680
|
+
def #{column_type}(*args) # def string(*args)
|
681
|
+
options = args.extract_options! # options = args.extract_options!
|
682
|
+
column_names = args # column_names = args
|
683
|
+
#
|
684
|
+
column_names.each do |name| # column_names.each do |name|
|
685
|
+
column = ColumnDefinition.new(@base, name, '#{column_type}') # column = ColumnDefinition.new(@base, name, 'string')
|
686
|
+
if options[:limit] # if options[:limit]
|
687
|
+
column.limit = options[:limit] # column.limit = options[:limit]
|
688
|
+
elsif native['#{column_type}'.to_sym].is_a?(Hash) # elsif native['string'.to_sym].is_a?(Hash)
|
689
|
+
column.limit = native['#{column_type}'.to_sym][:limit] # column.limit = native['string'.to_sym][:limit]
|
690
|
+
end # end
|
691
|
+
column.precision = options[:precision] # column.precision = options[:precision]
|
692
|
+
column.scale = options[:scale] # column.scale = options[:scale]
|
693
|
+
column.default = options[:default] # column.default = options[:default]
|
694
|
+
column.null = options[:null] # column.null = options[:null]
|
695
|
+
@base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options)
|
696
|
+
end # end
|
697
|
+
end # end
|
695
698
|
EOV
|
696
699
|
end
|
697
700
|
|
@@ -3,6 +3,7 @@ require 'date'
|
|
3
3
|
require 'bigdecimal'
|
4
4
|
require 'bigdecimal/util'
|
5
5
|
|
6
|
+
# TODO: Autoload these files
|
6
7
|
require 'active_record/connection_adapters/abstract/schema_definitions'
|
7
8
|
require 'active_record/connection_adapters/abstract/schema_statements'
|
8
9
|
require 'active_record/connection_adapters/abstract/database_statements'
|
@@ -65,6 +66,12 @@ module ActiveRecord
|
|
65
66
|
def supports_ddl_transactions?
|
66
67
|
false
|
67
68
|
end
|
69
|
+
|
70
|
+
# Does this adapter support savepoints? PostgreSQL and MySQL do, SQLite
|
71
|
+
# does not.
|
72
|
+
def supports_savepoints?
|
73
|
+
false
|
74
|
+
end
|
68
75
|
|
69
76
|
# Should primary key values be selected from their corresponding
|
70
77
|
# sequence before the insert statement? If true, next_sequence_value
|
@@ -159,9 +166,26 @@ module ActiveRecord
|
|
159
166
|
@open_transactions -= 1
|
160
167
|
end
|
161
168
|
|
162
|
-
def
|
169
|
+
def transaction_joinable=(joinable)
|
170
|
+
@transaction_joinable = joinable
|
171
|
+
end
|
172
|
+
|
173
|
+
def create_savepoint
|
174
|
+
end
|
175
|
+
|
176
|
+
def rollback_to_savepoint
|
177
|
+
end
|
178
|
+
|
179
|
+
def release_savepoint
|
180
|
+
end
|
181
|
+
|
182
|
+
def current_savepoint_name
|
183
|
+
"active_record_#{open_transactions}"
|
184
|
+
end
|
185
|
+
|
186
|
+
def log_info(sql, name, ms)
|
163
187
|
if @logger && @logger.debug?
|
164
|
-
name =
|
188
|
+
name = '%s (%.1fms)' % [name || 'SQL', ms]
|
165
189
|
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
|
166
190
|
end
|
167
191
|
end
|
@@ -170,9 +194,9 @@ module ActiveRecord
|
|
170
194
|
def log(sql, name)
|
171
195
|
if block_given?
|
172
196
|
result = nil
|
173
|
-
|
174
|
-
@runtime +=
|
175
|
-
log_info(sql, name,
|
197
|
+
ms = Benchmark.ms { result = yield }
|
198
|
+
@runtime += ms
|
199
|
+
log_info(sql, name, ms)
|
176
200
|
result
|
177
201
|
else
|
178
202
|
log_info(sql, name, 0)
|
@@ -13,23 +13,25 @@ module MysqlCompat #:nodoc:
|
|
13
13
|
# C driver >= 2.7 returns null values in each_hash
|
14
14
|
if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
|
15
15
|
target.class_eval <<-'end_eval'
|
16
|
-
def all_hashes
|
17
|
-
rows = []
|
18
|
-
each_hash { |row| rows << row }
|
19
|
-
rows
|
20
|
-
end
|
16
|
+
def all_hashes # def all_hashes
|
17
|
+
rows = [] # rows = []
|
18
|
+
each_hash { |row| rows << row } # each_hash { |row| rows << row }
|
19
|
+
rows # rows
|
20
|
+
end # end
|
21
21
|
end_eval
|
22
22
|
|
23
23
|
# adapters before 2.7 don't have a version constant
|
24
24
|
# and don't return null values in each_hash
|
25
25
|
else
|
26
26
|
target.class_eval <<-'end_eval'
|
27
|
-
def all_hashes
|
28
|
-
rows = []
|
29
|
-
all_fields = fetch_fields.inject({}) { |fields, f|
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
def all_hashes # def all_hashes
|
28
|
+
rows = [] # rows = []
|
29
|
+
all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f|
|
30
|
+
fields[f.name] = nil; fields # fields[f.name] = nil; fields
|
31
|
+
} # }
|
32
|
+
each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) }
|
33
|
+
rows # rows
|
34
|
+
end # end
|
33
35
|
end_eval
|
34
36
|
end
|
35
37
|
|
@@ -157,13 +159,16 @@ module ActiveRecord
|
|
157
159
|
# * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
|
158
160
|
# * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
|
159
161
|
#
|
160
|
-
# By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
|
161
|
-
# as boolean. If you wish to disable this emulation (which was the default
|
162
|
-
# behavior in versions 0.13.1 and earlier) you can add the following line
|
163
|
-
# to your environment.rb file:
|
164
|
-
#
|
165
|
-
# ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
|
166
162
|
class MysqlAdapter < AbstractAdapter
|
163
|
+
|
164
|
+
##
|
165
|
+
# :singleton-method:
|
166
|
+
# By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
|
167
|
+
# as boolean. If you wish to disable this emulation (which was the default
|
168
|
+
# behavior in versions 0.13.1 and earlier) you can add the following line
|
169
|
+
# to your environment.rb file:
|
170
|
+
#
|
171
|
+
# ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
|
167
172
|
cattr_accessor :emulate_booleans
|
168
173
|
self.emulate_booleans = true
|
169
174
|
|
@@ -206,6 +211,10 @@ module ActiveRecord
|
|
206
211
|
def supports_migrations? #:nodoc:
|
207
212
|
true
|
208
213
|
end
|
214
|
+
|
215
|
+
def supports_savepoints? #:nodoc:
|
216
|
+
true
|
217
|
+
end
|
209
218
|
|
210
219
|
def native_database_types #:nodoc:
|
211
220
|
NATIVE_DATABASE_TYPES
|
@@ -306,6 +315,7 @@ module ActiveRecord
|
|
306
315
|
rows
|
307
316
|
end
|
308
317
|
|
318
|
+
# Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you're done using it.
|
309
319
|
def execute(sql, name = nil) #:nodoc:
|
310
320
|
log(sql, name) { @connection.query(sql) }
|
311
321
|
rescue ActiveRecord::StatementInvalid => exception
|
@@ -344,6 +354,17 @@ module ActiveRecord
|
|
344
354
|
# Transactions aren't supported
|
345
355
|
end
|
346
356
|
|
357
|
+
def create_savepoint
|
358
|
+
execute("SAVEPOINT #{current_savepoint_name}")
|
359
|
+
end
|
360
|
+
|
361
|
+
def rollback_to_savepoint
|
362
|
+
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
363
|
+
end
|
364
|
+
|
365
|
+
def release_savepoint
|
366
|
+
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
367
|
+
end
|
347
368
|
|
348
369
|
def add_limit_offset!(sql, options) #:nodoc:
|
349
370
|
if limit = options[:limit]
|
@@ -412,7 +433,9 @@ module ActiveRecord
|
|
412
433
|
|
413
434
|
def tables(name = nil) #:nodoc:
|
414
435
|
tables = []
|
415
|
-
execute("SHOW TABLES", name)
|
436
|
+
result = execute("SHOW TABLES", name)
|
437
|
+
result.each { |field| tables << field[0] }
|
438
|
+
result.free
|
416
439
|
tables
|
417
440
|
end
|
418
441
|
|
@@ -423,7 +446,8 @@ module ActiveRecord
|
|
423
446
|
def indexes(table_name, name = nil)#:nodoc:
|
424
447
|
indexes = []
|
425
448
|
current_index = nil
|
426
|
-
execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
|
449
|
+
result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
|
450
|
+
result.each do |row|
|
427
451
|
if current_index != row[2]
|
428
452
|
next if row[2] == "PRIMARY" # skip the primary key
|
429
453
|
current_index = row[2]
|
@@ -432,13 +456,16 @@ module ActiveRecord
|
|
432
456
|
|
433
457
|
indexes.last.columns << row[4]
|
434
458
|
end
|
459
|
+
result.free
|
435
460
|
indexes
|
436
461
|
end
|
437
462
|
|
438
463
|
def columns(table_name, name = nil)#:nodoc:
|
439
464
|
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
|
440
465
|
columns = []
|
441
|
-
execute(sql, name)
|
466
|
+
result = execute(sql, name)
|
467
|
+
result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
|
468
|
+
result.free
|
442
469
|
columns
|
443
470
|
end
|
444
471
|
|
@@ -519,9 +546,11 @@ module ActiveRecord
|
|
519
546
|
# Returns a table's primary key and belonging sequence.
|
520
547
|
def pk_and_sequence_for(table) #:nodoc:
|
521
548
|
keys = []
|
522
|
-
execute("describe #{quote_table_name(table)}")
|
549
|
+
result = execute("describe #{quote_table_name(table)}")
|
550
|
+
result.each_hash do |h|
|
523
551
|
keys << h["Field"]if h["Key"] == "PRI"
|
524
552
|
end
|
553
|
+
result.free
|
525
554
|
keys.length == 1 ? [keys.first, nil] : nil
|
526
555
|
end
|
527
556
|
|
@@ -272,6 +272,10 @@ module ActiveRecord
|
|
272
272
|
def supports_ddl_transactions?
|
273
273
|
true
|
274
274
|
end
|
275
|
+
|
276
|
+
def supports_savepoints?
|
277
|
+
true
|
278
|
+
end
|
275
279
|
|
276
280
|
# Returns the configured supported identifier length supported by PostgreSQL,
|
277
281
|
# or report the default of 63 on PostgreSQL 7.x.
|
@@ -528,45 +532,26 @@ module ActiveRecord
|
|
528
532
|
def rollback_db_transaction
|
529
533
|
execute "ROLLBACK"
|
530
534
|
end
|
535
|
+
|
536
|
+
if defined?(PGconn::PQTRANS_IDLE)
|
537
|
+
# The ruby-pg driver supports inspecting the transaction status,
|
538
|
+
# while the ruby-postgres driver does not.
|
539
|
+
def outside_transaction?
|
540
|
+
@connection.transaction_status == PGconn::PQTRANS_IDLE
|
541
|
+
end
|
542
|
+
end
|
531
543
|
|
532
|
-
|
533
|
-
|
534
|
-
PQTRANS_IDLE = defined?(PGconn::PQTRANS_IDLE) ? PGconn::PQTRANS_IDLE : 0
|
535
|
-
|
536
|
-
# Check whether a transaction is active.
|
537
|
-
def transaction_active?
|
538
|
-
@connection.transaction_status != PQTRANS_IDLE
|
544
|
+
def create_savepoint
|
545
|
+
execute("SAVEPOINT #{current_savepoint_name}")
|
539
546
|
end
|
540
547
|
|
541
|
-
|
542
|
-
|
543
|
-
transaction_open = false
|
544
|
-
begin
|
545
|
-
if block_given?
|
546
|
-
if start_db_transaction
|
547
|
-
begin_db_transaction
|
548
|
-
transaction_open = true
|
549
|
-
end
|
550
|
-
yield
|
551
|
-
end
|
552
|
-
rescue Exception => database_transaction_rollback
|
553
|
-
if transaction_open && transaction_active?
|
554
|
-
transaction_open = false
|
555
|
-
rollback_db_transaction
|
556
|
-
end
|
557
|
-
raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
|
558
|
-
end
|
559
|
-
ensure
|
560
|
-
if transaction_open && transaction_active?
|
561
|
-
begin
|
562
|
-
commit_db_transaction
|
563
|
-
rescue Exception => database_transaction_rollback
|
564
|
-
rollback_db_transaction
|
565
|
-
raise
|
566
|
-
end
|
567
|
-
end
|
548
|
+
def rollback_to_savepoint
|
549
|
+
execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
|
568
550
|
end
|
569
551
|
|
552
|
+
def release_savepoint
|
553
|
+
execute("RELEASE SAVEPOINT #{current_savepoint_name}")
|
554
|
+
end
|
570
555
|
|
571
556
|
# SCHEMA STATEMENTS ========================================
|
572
557
|
|
@@ -950,13 +935,13 @@ module ActiveRecord
|
|
950
935
|
# should know about this but can't detect it there, so deal with it here.
|
951
936
|
money_precision = (postgresql_version >= 80300) ? 19 : 10
|
952
937
|
PostgreSQLColumn.module_eval(<<-end_eval)
|
953
|
-
def extract_precision(sql_type)
|
954
|
-
if sql_type =~ /^money$/
|
955
|
-
#{money_precision}
|
956
|
-
else
|
957
|
-
super
|
958
|
-
end
|
959
|
-
end
|
938
|
+
def extract_precision(sql_type) # def extract_precision(sql_type)
|
939
|
+
if sql_type =~ /^money$/ # if sql_type =~ /^money$/
|
940
|
+
#{money_precision} # 19
|
941
|
+
else # else
|
942
|
+
super # super
|
943
|
+
end # end
|
944
|
+
end # end
|
960
945
|
end_eval
|
961
946
|
|
962
947
|
configure_connection
|
@@ -17,9 +17,9 @@ module ActiveRecord
|
|
17
17
|
|
18
18
|
# "Downgrade" deprecated sqlite API
|
19
19
|
if SQLite.const_defined?(:Version)
|
20
|
-
ConnectionAdapters::SQLite2Adapter.new(db, logger)
|
20
|
+
ConnectionAdapters::SQLite2Adapter.new(db, logger, config)
|
21
21
|
else
|
22
|
-
ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger)
|
22
|
+
ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger, config)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -72,10 +72,31 @@ module ActiveRecord
|
|
72
72
|
#
|
73
73
|
# * <tt>:database</tt> - Path to the database file.
|
74
74
|
class SQLiteAdapter < AbstractAdapter
|
75
|
+
class Version
|
76
|
+
include Comparable
|
77
|
+
|
78
|
+
def initialize(version_string)
|
79
|
+
@version = version_string.split('.').map(&:to_i)
|
80
|
+
end
|
81
|
+
|
82
|
+
def <=>(version_string)
|
83
|
+
@version <=> version_string.split('.').map(&:to_i)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def initialize(connection, logger, config)
|
88
|
+
super(connection, logger)
|
89
|
+
@config = config
|
90
|
+
end
|
91
|
+
|
75
92
|
def adapter_name #:nodoc:
|
76
93
|
'SQLite'
|
77
94
|
end
|
78
95
|
|
96
|
+
def supports_ddl_transactions?
|
97
|
+
sqlite_version >= '2.0.0'
|
98
|
+
end
|
99
|
+
|
79
100
|
def supports_migrations? #:nodoc:
|
80
101
|
true
|
81
102
|
end
|
@@ -83,6 +104,10 @@ module ActiveRecord
|
|
83
104
|
def requires_reloading?
|
84
105
|
true
|
85
106
|
end
|
107
|
+
|
108
|
+
def supports_add_column?
|
109
|
+
sqlite_version >= '3.1.6'
|
110
|
+
end
|
86
111
|
|
87
112
|
def disconnect!
|
88
113
|
super
|
@@ -164,7 +189,6 @@ module ActiveRecord
|
|
164
189
|
catch_schema_changes { @connection.rollback }
|
165
190
|
end
|
166
191
|
|
167
|
-
|
168
192
|
# SELECT ... FOR UPDATE is redundant since the table is locked.
|
169
193
|
def add_lock!(sql, options) #:nodoc:
|
170
194
|
sql
|
@@ -213,14 +237,20 @@ module ActiveRecord
|
|
213
237
|
execute "ALTER TABLE #{name} RENAME TO #{new_name}"
|
214
238
|
end
|
215
239
|
|
240
|
+
# See: http://www.sqlite.org/lang_altertable.html
|
241
|
+
# SQLite has an additional restriction on the ALTER TABLE statement
|
242
|
+
def valid_alter_table_options( type, options)
|
243
|
+
type.to_sym != :primary_key
|
244
|
+
end
|
245
|
+
|
216
246
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
217
|
-
if
|
218
|
-
|
247
|
+
if supports_add_column? && valid_alter_table_options( type, options )
|
248
|
+
super(table_name, column_name, type, options)
|
249
|
+
else
|
250
|
+
alter_table(table_name) do |definition|
|
251
|
+
definition.column(column_name, type, options)
|
252
|
+
end
|
219
253
|
end
|
220
|
-
|
221
|
-
super(table_name, column_name, type, options)
|
222
|
-
# See last paragraph on http://www.sqlite.org/lang_altertable.html
|
223
|
-
execute "VACUUM"
|
224
254
|
end
|
225
255
|
|
226
256
|
def remove_column(table_name, *column_names) #:nodoc:
|
@@ -306,7 +336,7 @@ module ActiveRecord
|
|
306
336
|
end
|
307
337
|
|
308
338
|
def copy_table(from, to, options = {}) #:nodoc:
|
309
|
-
options = options.merge(:id => !columns(from).detect{|c| c.name == 'id'}.nil?)
|
339
|
+
options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
|
310
340
|
create_table(to, options) do |definition|
|
311
341
|
@definition = definition
|
312
342
|
columns(from).each do |column|
|
@@ -380,7 +410,7 @@ module ActiveRecord
|
|
380
410
|
end
|
381
411
|
|
382
412
|
def sqlite_version
|
383
|
-
@sqlite_version ||= select_value('select sqlite_version(*)')
|
413
|
+
@sqlite_version ||= SQLiteAdapter::Version.new(select_value('select sqlite_version(*)'))
|
384
414
|
end
|
385
415
|
|
386
416
|
def default_primary_key_type
|
@@ -393,19 +423,9 @@ module ActiveRecord
|
|
393
423
|
end
|
394
424
|
|
395
425
|
class SQLite2Adapter < SQLiteAdapter # :nodoc:
|
396
|
-
def supports_count_distinct? #:nodoc:
|
397
|
-
false
|
398
|
-
end
|
399
|
-
|
400
426
|
def rename_table(name, new_name)
|
401
427
|
move_table(name, new_name)
|
402
428
|
end
|
403
|
-
|
404
|
-
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
405
|
-
alter_table(table_name) do |definition|
|
406
|
-
definition.column(column_name, type, options)
|
407
|
-
end
|
408
|
-
end
|
409
429
|
end
|
410
430
|
|
411
431
|
class DeprecatedSQLiteAdapter < SQLite2Adapter # :nodoc:
|