activerecord 3.1.0 → 3.1.1.rc1
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 +36 -1
- data/lib/active_record.rb +0 -7
- data/lib/active_record/associations/alias_tracker.rb +12 -6
- data/lib/active_record/associations/belongs_to_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +7 -3
- data/lib/active_record/associations/collection_proxy.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +1 -1
- data/lib/active_record/connection_adapters/mysql_adapter.rb +36 -4
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +47 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +57 -5
- data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
- data/lib/active_record/fixtures.rb +370 -374
- data/lib/active_record/migration/command_recorder.rb +1 -1
- data/lib/active_record/railties/databases.rake +7 -5
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/relation/batches.rb +1 -1
- data/lib/active_record/relation/calculations.rb +1 -0
- data/lib/active_record/relation/finder_methods.rb +9 -3
- data/lib/active_record/relation/query_methods.rb +2 -7
- data/lib/active_record/version.rb +2 -2
- metadata +24 -16
data/CHANGELOG
CHANGED
@@ -1,4 +1,39 @@
|
|
1
|
-
*Rails 3.1.
|
1
|
+
*Rails 3.1.1 (unreleased)*
|
2
|
+
|
3
|
+
* LRU cache in mysql and sqlite are now per-process caches.
|
4
|
+
|
5
|
+
* lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache
|
6
|
+
keys are per process id.
|
7
|
+
* lib/active_record/connection_adapters/sqlite_adapter.rb: ditto
|
8
|
+
|
9
|
+
[Aaron Patterson]
|
10
|
+
|
11
|
+
* Database adapters use a statement pool for limiting the number of open
|
12
|
+
prepared statments on the database. The limit defaults to 1000, but can
|
13
|
+
be adjusted in your database config by changing 'statement_limit'.
|
14
|
+
|
15
|
+
* Fix clash between using 'preload', 'joins' or 'eager_load' in a default scope and including the
|
16
|
+
default scoped model in a nested through association. (GH #2834.) [Jon Leighton]
|
17
|
+
|
18
|
+
* Ensure we are not comparing a string with a symbol in HasManyAssociation#inverse_updates_counter_cache?.
|
19
|
+
Fixes GH #2755, where a counter cache could be decremented twice as far as it was supposed to be.
|
20
|
+
|
21
|
+
[Jon Leighton]
|
22
|
+
|
23
|
+
* Don't send any queries to the database when the foreign key of a belongs_to is nil. Fixes
|
24
|
+
GH #2828. [Georg Friedrich]
|
25
|
+
|
26
|
+
* Fixed find_in_batches method to not include order from default_scope. See GH #2832 [Arun Agrawal]
|
27
|
+
|
28
|
+
* Don't compute table name for abstract classes. Fixes problem with setting the primary key
|
29
|
+
in an abstract class. See GH #2791. [Akira Matsuda]
|
30
|
+
|
31
|
+
* Psych errors with poor yaml formatting are proxied. Fixes GH #2645 and
|
32
|
+
GH #2731
|
33
|
+
|
34
|
+
* Use the LIMIT word with the methods #last and #first. Fixes GH #2783 [Damien Mathieu]
|
35
|
+
|
36
|
+
*Rails 3.1.0 (August 30, 2011)*
|
2
37
|
|
3
38
|
* Add a proxy_association method to association proxies, which can be called by association
|
4
39
|
extensions to access information about the association. This replaces proxy_owner etc with
|
data/lib/active_record.rb
CHANGED
@@ -21,13 +21,6 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
|
24
|
-
|
25
|
-
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
|
26
|
-
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
|
27
|
-
|
28
|
-
activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__)
|
29
|
-
$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
|
30
|
-
|
31
24
|
require 'active_support'
|
32
25
|
require 'active_support/i18n'
|
33
26
|
require 'active_model'
|
@@ -53,12 +53,18 @@ module ActiveRecord
|
|
53
53
|
# quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
|
54
54
|
quoted_name = connection.quote_table_name(name).downcase
|
55
55
|
|
56
|
-
table_joins.map
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
counts = table_joins.map do |join|
|
57
|
+
if join.is_a?(Arel::Nodes::StringJoin)
|
58
|
+
# Table names + table aliases
|
59
|
+
join.left.downcase.scan(
|
60
|
+
/join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
|
61
|
+
).size
|
62
|
+
else
|
63
|
+
join.left.table_name == name ? 1 : 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
counts.sum
|
62
68
|
end
|
63
69
|
|
64
70
|
def truncate(name)
|
@@ -78,10 +78,14 @@ module ActiveRecord
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def find(*args)
|
81
|
-
if
|
82
|
-
|
81
|
+
if block_given?
|
82
|
+
load_target.find(*args) { |*block_args| yield(*block_args) }
|
83
83
|
else
|
84
|
-
|
84
|
+
if options[:finder_sql]
|
85
|
+
find_by_scan(*args)
|
86
|
+
else
|
87
|
+
scoped.find(*args)
|
88
|
+
end
|
85
89
|
end
|
86
90
|
end
|
87
91
|
|
@@ -78,7 +78,7 @@ module ActiveRecord
|
|
78
78
|
def method_missing(method, *args, &block)
|
79
79
|
match = DynamicFinderMatch.match(method)
|
80
80
|
if match && match.instantiator?
|
81
|
-
|
81
|
+
send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r|
|
82
82
|
proxy_association.send :set_owner_attributes, r
|
83
83
|
proxy_association.send :add_to_target, r
|
84
84
|
yield(r) if block_given?
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
16
16
|
chain[1..-1].each do |reflection|
|
17
17
|
scope = scope.merge(
|
18
18
|
reflection.klass.scoped.with_default_scope.
|
19
|
-
except(:select, :create_with, :includes)
|
19
|
+
except(:select, :create_with, :includes, :preload, :joins, :eager_load)
|
20
20
|
)
|
21
21
|
end
|
22
22
|
scope
|
data/lib/active_record/base.rb
CHANGED
@@ -421,7 +421,7 @@ module ActiveRecord
|
|
421
421
|
# can be used as an argument for establish_connection, for easily
|
422
422
|
# re-establishing the connection.
|
423
423
|
def remove_connection(klass)
|
424
|
-
pool = @connection_pools
|
424
|
+
pool = @connection_pools.delete(klass.name)
|
425
425
|
return nil unless pool
|
426
426
|
|
427
427
|
pool.automatic_reconnect = false
|
@@ -2,6 +2,7 @@ require 'active_record/connection_adapters/abstract_adapter'
|
|
2
2
|
require 'active_support/core_ext/kernel/requires'
|
3
3
|
require 'active_support/core_ext/object/blank'
|
4
4
|
require 'set'
|
5
|
+
require 'active_record/connection_adapters/statement_pool'
|
5
6
|
|
6
7
|
gem 'mysql', '~> 2.8.1'
|
7
8
|
require 'mysql'
|
@@ -184,11 +185,45 @@ module ActiveRecord
|
|
184
185
|
:boolean => { :name => "tinyint", :limit => 1 }
|
185
186
|
}
|
186
187
|
|
188
|
+
class StatementPool < ConnectionAdapters::StatementPool
|
189
|
+
def initialize(connection, max = 1000)
|
190
|
+
super
|
191
|
+
@cache = Hash.new { |h,pid| h[pid] = {} }
|
192
|
+
end
|
193
|
+
|
194
|
+
def each(&block); cache.each(&block); end
|
195
|
+
def key?(key); cache.key?(key); end
|
196
|
+
def [](key); cache[key]; end
|
197
|
+
def length; cache.length; end
|
198
|
+
def delete(key); cache.delete(key); end
|
199
|
+
|
200
|
+
def []=(sql, key)
|
201
|
+
while @max <= cache.size
|
202
|
+
cache.shift.last[:stmt].close
|
203
|
+
end
|
204
|
+
cache[sql] = key
|
205
|
+
end
|
206
|
+
|
207
|
+
def clear
|
208
|
+
cache.values.each do |hash|
|
209
|
+
hash[:stmt].close
|
210
|
+
end
|
211
|
+
cache.clear
|
212
|
+
end
|
213
|
+
|
214
|
+
private
|
215
|
+
def cache
|
216
|
+
@cache[$$]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
187
220
|
def initialize(connection, logger, connection_options, config)
|
188
221
|
super(connection, logger)
|
189
222
|
@connection_options, @config = connection_options, config
|
190
223
|
@quoted_column_names, @quoted_table_names = {}, {}
|
191
224
|
@statements = {}
|
225
|
+
@statements = StatementPool.new(@connection,
|
226
|
+
config.fetch(:statement_limit) { 1000 })
|
192
227
|
@client_encoding = nil
|
193
228
|
connect
|
194
229
|
end
|
@@ -334,9 +369,6 @@ module ActiveRecord
|
|
334
369
|
|
335
370
|
# Clears the prepared statements cache.
|
336
371
|
def clear_cache!
|
337
|
-
@statements.values.each do |cache|
|
338
|
-
cache[:stmt].close
|
339
|
-
end
|
340
372
|
@statements.clear
|
341
373
|
end
|
342
374
|
|
@@ -755,7 +787,7 @@ module ActiveRecord
|
|
755
787
|
def quoted_columns_for_index(column_names, options = {})
|
756
788
|
length = options[:length] if options.is_a?(Hash)
|
757
789
|
|
758
|
-
|
790
|
+
case length
|
759
791
|
when Hash
|
760
792
|
column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
|
761
793
|
when Fixnum
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
2
|
require 'active_support/core_ext/kernel/requires'
|
3
3
|
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'active_record/connection_adapters/statement_pool'
|
4
5
|
|
5
6
|
# Make sure we're using pg high enough for PGResult#values
|
6
7
|
gem 'pg', '~> 0.11'
|
@@ -247,6 +248,47 @@ module ActiveRecord
|
|
247
248
|
true
|
248
249
|
end
|
249
250
|
|
251
|
+
class StatementPool < ConnectionAdapters::StatementPool
|
252
|
+
def initialize(connection, max)
|
253
|
+
super
|
254
|
+
@counter = 0
|
255
|
+
@cache = Hash.new { |h,pid| h[pid] = {} }
|
256
|
+
end
|
257
|
+
|
258
|
+
def each(&block); cache.each(&block); end
|
259
|
+
def key?(key); cache.key?(key); end
|
260
|
+
def [](key); cache[key]; end
|
261
|
+
def length; cache.length; end
|
262
|
+
|
263
|
+
def next_key
|
264
|
+
"a#{@counter + 1}"
|
265
|
+
end
|
266
|
+
|
267
|
+
def []=(sql, key)
|
268
|
+
while @max <= cache.size
|
269
|
+
dealloc(cache.shift.last)
|
270
|
+
end
|
271
|
+
@counter += 1
|
272
|
+
cache[sql] = key
|
273
|
+
end
|
274
|
+
|
275
|
+
def clear
|
276
|
+
cache.each_value do |stmt_key|
|
277
|
+
dealloc stmt_key
|
278
|
+
end
|
279
|
+
cache.clear
|
280
|
+
end
|
281
|
+
|
282
|
+
private
|
283
|
+
def cache
|
284
|
+
@cache[$$]
|
285
|
+
end
|
286
|
+
|
287
|
+
def dealloc(key)
|
288
|
+
@connection.query "DEALLOCATE #{key}"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
250
292
|
# Initializes and connects a PostgreSQL adapter.
|
251
293
|
def initialize(connection, logger, connection_parameters, config)
|
252
294
|
super(connection, logger)
|
@@ -255,9 +297,10 @@ module ActiveRecord
|
|
255
297
|
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
|
256
298
|
@local_tz = nil
|
257
299
|
@table_alias_length = nil
|
258
|
-
@statements = {}
|
259
300
|
|
260
301
|
connect
|
302
|
+
@statements = StatementPool.new @connection,
|
303
|
+
config.fetch(:statement_limit) { 1000 }
|
261
304
|
|
262
305
|
if postgresql_version < 80200
|
263
306
|
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
|
@@ -272,9 +315,6 @@ module ActiveRecord
|
|
272
315
|
|
273
316
|
# Clears the prepared statements cache.
|
274
317
|
def clear_cache!
|
275
|
-
@statements.each_value do |value|
|
276
|
-
@connection.query "DEALLOCATE #{value}"
|
277
|
-
end
|
278
318
|
@statements.clear
|
279
319
|
end
|
280
320
|
|
@@ -672,6 +712,7 @@ module ActiveRecord
|
|
672
712
|
|
673
713
|
def table_exists?(name)
|
674
714
|
schema, table = extract_schema_and_table(name.to_s)
|
715
|
+
return false unless table # Abstract classes is having nil table name
|
675
716
|
|
676
717
|
binds = [[nil, table.gsub(/(^"|"$)/,'')]]
|
677
718
|
binds << [nil, schema] if schema
|
@@ -680,7 +721,7 @@ module ActiveRecord
|
|
680
721
|
SELECT COUNT(*)
|
681
722
|
FROM pg_tables
|
682
723
|
WHERE tablename = $1
|
683
|
-
|
724
|
+
AND schemaname = #{schema ? "$2" : "ANY (current_schemas(false))"}
|
684
725
|
SQL
|
685
726
|
end
|
686
727
|
|
@@ -964,7 +1005,7 @@ module ActiveRecord
|
|
964
1005
|
|
965
1006
|
def exec_cache(sql, binds)
|
966
1007
|
unless @statements.key? sql
|
967
|
-
nextkey =
|
1008
|
+
nextkey = @statements.next_key
|
968
1009
|
@connection.prepare nextkey, sql
|
969
1010
|
@statements[sql] = nextkey
|
970
1011
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_adapter'
|
2
2
|
require 'active_support/core_ext/kernel/requires'
|
3
|
+
require 'active_record/connection_adapters/statement_pool'
|
4
|
+
require 'active_support/core_ext/string/encoding'
|
3
5
|
|
4
6
|
module ActiveRecord
|
5
7
|
module ConnectionAdapters #:nodoc:
|
@@ -48,9 +50,45 @@ module ActiveRecord
|
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
53
|
+
class StatementPool < ConnectionAdapters::StatementPool
|
54
|
+
def initialize(connection, max)
|
55
|
+
super
|
56
|
+
@cache = Hash.new { |h,pid| h[pid] = {} }
|
57
|
+
end
|
58
|
+
|
59
|
+
def each(&block); cache.each(&block); end
|
60
|
+
def key?(key); cache.key?(key); end
|
61
|
+
def [](key); cache[key]; end
|
62
|
+
def length; cache.length; end
|
63
|
+
|
64
|
+
def []=(sql, key)
|
65
|
+
while @max <= cache.size
|
66
|
+
dealloc(cache.shift.last[:stmt])
|
67
|
+
end
|
68
|
+
cache[sql] = key
|
69
|
+
end
|
70
|
+
|
71
|
+
def clear
|
72
|
+
cache.values.each do |hash|
|
73
|
+
dealloc hash[:stmt]
|
74
|
+
end
|
75
|
+
cache.clear
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def cache
|
80
|
+
@cache[$$]
|
81
|
+
end
|
82
|
+
|
83
|
+
def dealloc(stmt)
|
84
|
+
stmt.close unless stmt.closed?
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
51
88
|
def initialize(connection, logger, config)
|
52
89
|
super(connection, logger)
|
53
|
-
@statements =
|
90
|
+
@statements = StatementPool.new(@connection,
|
91
|
+
config.fetch(:statement_limit) { 1000 })
|
54
92
|
@config = config
|
55
93
|
end
|
56
94
|
|
@@ -107,7 +145,6 @@ module ActiveRecord
|
|
107
145
|
|
108
146
|
# Clears the prepared statements cache.
|
109
147
|
def clear_cache!
|
110
|
-
@statements.values.each { |hash| hash[:stmt].close }
|
111
148
|
@statements.clear
|
112
149
|
end
|
113
150
|
|
@@ -159,10 +196,25 @@ module ActiveRecord
|
|
159
196
|
end
|
160
197
|
end
|
161
198
|
|
162
|
-
|
163
|
-
|
199
|
+
if "<3".encoding_aware?
|
200
|
+
def type_cast(value, column) # :nodoc:
|
201
|
+
return value.to_f if BigDecimal === value
|
202
|
+
return super unless String === value
|
203
|
+
return super unless column && value
|
164
204
|
|
165
|
-
|
205
|
+
value = super
|
206
|
+
if column.type == :string && value.encoding == Encoding::ASCII_8BIT
|
207
|
+
@logger.error "Binary data inserted for `string` type on column `#{column.name}`"
|
208
|
+
value.encode! 'utf-8'
|
209
|
+
end
|
210
|
+
value
|
211
|
+
end
|
212
|
+
else
|
213
|
+
def type_cast(value, column) # :nodoc:
|
214
|
+
return super unless BigDecimal === value
|
215
|
+
|
216
|
+
value.to_f
|
217
|
+
end
|
166
218
|
end
|
167
219
|
|
168
220
|
# DATABASE STATEMENTS ======================================
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class StatementPool
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(connection, max = 1000)
|
7
|
+
@connection = connection
|
8
|
+
@max = max
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
def key?(key)
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](key)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
def length
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def []=(sql, key)
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete(key)
|
36
|
+
raise NotImplementedError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|