activerecord 4.0.0 → 4.0.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +326 -3
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +18 -0
- data/lib/active_record/associations/association.rb +11 -3
- data/lib/active_record/associations/builder/belongs_to.rb +4 -2
- data/lib/active_record/associations/collection_association.rb +8 -8
- data/lib/active_record/associations/collection_proxy.rb +2 -4
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_one_association.rb +3 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +2 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -1
- data/lib/active_record/associations/preloader.rb +3 -2
- data/lib/active_record/attribute_methods/read.rb +15 -9
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +2 -0
- data/lib/active_record/attribute_methods.rb +26 -2
- data/lib/active_record/autosave_association.rb +8 -8
- data/lib/active_record/callbacks.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +12 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +16 -2
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +3 -1
- data/lib/active_record/connection_adapters/column.rb +12 -11
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
- data/lib/active_record/connection_adapters/mysql_adapter.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/cast.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +7 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +19 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +8 -5
- data/lib/active_record/core.rb +18 -18
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +4 -1
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/model_schema.rb +27 -17
- data/lib/active_record/null_relation.rb +1 -5
- data/lib/active_record/persistence.rb +16 -5
- data/lib/active_record/railtie.rb +1 -0
- data/lib/active_record/railties/databases.rake +4 -1
- data/lib/active_record/reflection.rb +1 -1
- data/lib/active_record/relation/batches.rb +2 -2
- data/lib/active_record/relation/calculations.rb +1 -0
- data/lib/active_record/relation/finder_methods.rb +10 -10
- data/lib/active_record/relation/merger.rb +31 -28
- data/lib/active_record/relation/query_methods.rb +20 -11
- data/lib/active_record/relation.rb +1 -1
- data/lib/active_record/result.rb +15 -1
- data/lib/active_record/sanitization.rb +13 -3
- data/lib/active_record/schema_dumper.rb +5 -1
- data/lib/active_record/tasks/database_tasks.rb +2 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +1 -1
- data/lib/active_record/transactions.rb +5 -5
- data/lib/active_record/validations/uniqueness.rb +2 -2
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +1 -0
- metadata +8 -7
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/enumerable'
|
2
|
+
require 'mutex_m'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
# = Active Record Attribute Methods
|
@@ -7,6 +8,7 @@ module ActiveRecord
|
|
7
8
|
include ActiveModel::AttributeMethods
|
8
9
|
|
9
10
|
included do
|
11
|
+
initialize_generated_modules
|
10
12
|
include Read
|
11
13
|
include Write
|
12
14
|
include BeforeTypeCast
|
@@ -18,12 +20,34 @@ module ActiveRecord
|
|
18
20
|
end
|
19
21
|
|
20
22
|
module ClassMethods
|
23
|
+
def inherited(child_class) #:nodoc:
|
24
|
+
child_class.initialize_generated_modules
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize_generated_modules # :nodoc:
|
29
|
+
@generated_attribute_methods = Module.new {
|
30
|
+
extend Mutex_m
|
31
|
+
|
32
|
+
const_set :AttrNames, Module.new {
|
33
|
+
def self.set_name_cache(name, value)
|
34
|
+
const_name = "ATTR_#{name}"
|
35
|
+
unless const_defined? const_name
|
36
|
+
const_set const_name, value.dup.freeze
|
37
|
+
end
|
38
|
+
end
|
39
|
+
}
|
40
|
+
}
|
41
|
+
@attribute_methods_generated = false
|
42
|
+
include @generated_attribute_methods
|
43
|
+
end
|
44
|
+
|
21
45
|
# Generates all the attribute related methods for columns in the database
|
22
46
|
# accessors, mutators and query methods.
|
23
47
|
def define_attribute_methods # :nodoc:
|
24
48
|
# Use a mutex; we don't want two thread simultaneously trying to define
|
25
49
|
# attribute methods.
|
26
|
-
|
50
|
+
generated_attribute_methods.synchronize do
|
27
51
|
return if attribute_methods_generated?
|
28
52
|
superclass.define_attribute_methods unless self == base_class
|
29
53
|
super(column_names)
|
@@ -32,7 +56,7 @@ module ActiveRecord
|
|
32
56
|
end
|
33
57
|
|
34
58
|
def attribute_methods_generated? # :nodoc:
|
35
|
-
@attribute_methods_generated
|
59
|
+
@attribute_methods_generated
|
36
60
|
end
|
37
61
|
|
38
62
|
def undefine_attribute_methods # :nodoc:
|
@@ -335,15 +335,19 @@ module ActiveRecord
|
|
335
335
|
autosave = reflection.options[:autosave]
|
336
336
|
|
337
337
|
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
|
338
|
-
|
338
|
+
|
339
|
+
if autosave
|
340
|
+
records_to_destroy = records.select(&:marked_for_destruction?)
|
341
|
+
records_to_destroy.each { |record| association.destroy(record) }
|
342
|
+
records -= records_to_destroy
|
343
|
+
end
|
344
|
+
|
339
345
|
records.each do |record|
|
340
346
|
next if record.destroyed?
|
341
347
|
|
342
348
|
saved = true
|
343
349
|
|
344
|
-
if autosave && record.
|
345
|
-
records_to_destroy << record
|
346
|
-
elsif autosave != false && (@new_record_before_save || record.new_record?)
|
350
|
+
if autosave != false && (@new_record_before_save || record.new_record?)
|
347
351
|
if autosave
|
348
352
|
saved = association.insert_record(record, false)
|
349
353
|
else
|
@@ -355,10 +359,6 @@ module ActiveRecord
|
|
355
359
|
|
356
360
|
raise ActiveRecord::Rollback unless saved
|
357
361
|
end
|
358
|
-
|
359
|
-
records_to_destroy.each do |record|
|
360
|
-
association.destroy(record)
|
361
|
-
end
|
362
362
|
end
|
363
363
|
|
364
364
|
# reconstruct the scope now that we know the owner's id
|
@@ -23,11 +23,14 @@ module ActiveRecord
|
|
23
23
|
# Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
|
24
24
|
# <tt>after_rollback</tt>.
|
25
25
|
#
|
26
|
+
# Additionally, an <tt>after_touch</tt> callback is triggered whenever an
|
27
|
+
# object is touched.
|
28
|
+
#
|
26
29
|
# Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
|
27
30
|
# is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
|
28
31
|
# are instantiated as well.
|
29
32
|
#
|
30
|
-
#
|
33
|
+
# There are nineteen callbacks in total, which give you immense power to react and prepare for each state in the
|
31
34
|
# Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
|
32
35
|
# except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
|
33
36
|
#
|
@@ -377,14 +377,14 @@ module ActiveRecord
|
|
377
377
|
update_sql(sql, name)
|
378
378
|
end
|
379
379
|
|
380
|
-
|
381
|
-
|
382
|
-
|
380
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
381
|
+
[sql, binds]
|
382
|
+
end
|
383
383
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
384
|
+
def last_inserted_id(result)
|
385
|
+
row = result.rows.first
|
386
|
+
row && row.first
|
387
|
+
end
|
388
388
|
end
|
389
389
|
end
|
390
390
|
end
|
@@ -706,12 +706,21 @@ module ActiveRecord
|
|
706
706
|
end
|
707
707
|
|
708
708
|
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
709
|
-
# Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
|
710
709
|
#
|
711
|
-
# distinct("posts.id", "posts.created_at desc")
|
710
|
+
# distinct("posts.id", ["posts.created_at desc"])
|
712
711
|
#
|
713
712
|
def distinct(columns, order_by)
|
714
|
-
"
|
713
|
+
ActiveSupport::Deprecation.warn("#distinct is deprecated and shall be removed from future releases.")
|
714
|
+
"DISTINCT #{columns_for_distinct(columns, order_by)}"
|
715
|
+
end
|
716
|
+
|
717
|
+
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
718
|
+
# Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
|
719
|
+
# require the order columns appear in the SELECT.
|
720
|
+
#
|
721
|
+
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
722
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
723
|
+
columns
|
715
724
|
end
|
716
725
|
|
717
726
|
# Adds timestamps (+created_at+ and +updated_at+) columns to the named table.
|
@@ -99,6 +99,7 @@ module ActiveRecord
|
|
99
99
|
@query_cache_enabled = false
|
100
100
|
@schema_cache = SchemaCache.new self
|
101
101
|
@visitor = nil
|
102
|
+
@prepared_statements = false
|
102
103
|
end
|
103
104
|
|
104
105
|
def valid_type?(type)
|
@@ -197,10 +198,11 @@ module ActiveRecord
|
|
197
198
|
end
|
198
199
|
|
199
200
|
def unprepared_statement
|
200
|
-
|
201
|
+
old_prepared_statements, @prepared_statements = @prepared_statements, false
|
202
|
+
old_visitor, @visitor = @visitor, unprepared_visitor
|
201
203
|
yield
|
202
204
|
ensure
|
203
|
-
@visitor =
|
205
|
+
@visitor, @prepared_statements = old_visitor, old_prepared_statements
|
204
206
|
end
|
205
207
|
|
206
208
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
@@ -280,6 +282,14 @@ module ActiveRecord
|
|
280
282
|
false
|
281
283
|
end
|
282
284
|
|
285
|
+
# This is meant to be implemented by the adapters that support extensions
|
286
|
+
def disable_extension(name)
|
287
|
+
end
|
288
|
+
|
289
|
+
# This is meant to be implemented by the adapters that support extensions
|
290
|
+
def enable_extension(name)
|
291
|
+
end
|
292
|
+
|
283
293
|
# A list of extensions, to be filled in by adapters that support them. At
|
284
294
|
# the moment only postgresql does.
|
285
295
|
def extensions
|
@@ -435,6 +445,10 @@ module ActiveRecord
|
|
435
445
|
# override in derived class
|
436
446
|
ActiveRecord::StatementInvalid.new(message, exception)
|
437
447
|
end
|
448
|
+
|
449
|
+
def without_prepared_statement?(binds)
|
450
|
+
!@prepared_statements || binds.empty?
|
451
|
+
end
|
438
452
|
end
|
439
453
|
end
|
440
454
|
end
|
@@ -165,6 +165,7 @@ module ActiveRecord
|
|
165
165
|
@quoted_column_names, @quoted_table_names = {}, {}
|
166
166
|
|
167
167
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
168
|
+
@prepared_statements = true
|
168
169
|
@visitor = Arel::Visitors::MySQL.new self
|
169
170
|
else
|
170
171
|
@visitor = unprepared_visitor
|
@@ -460,7 +461,8 @@ module ActiveRecord
|
|
460
461
|
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
|
461
462
|
execute_and_free(sql, 'SCHEMA') do |result|
|
462
463
|
each_hash(result).map do |field|
|
463
|
-
|
464
|
+
field_name = set_field_encoding(field[:Field])
|
465
|
+
new_column(field_name, field[:Default], field[:Type], field[:Null] == "YES", field[:Collation], field[:Extra])
|
464
466
|
end
|
465
467
|
end
|
466
468
|
end
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
13
13
|
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
14
14
|
end
|
15
15
|
|
16
|
-
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
|
16
|
+
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale, :default_function
|
17
17
|
attr_accessor :primary, :coder
|
18
18
|
|
19
19
|
alias :encoded? :coder
|
@@ -27,16 +27,17 @@ module ActiveRecord
|
|
27
27
|
# It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
|
28
28
|
# +null+ determines if this column allows +NULL+ values.
|
29
29
|
def initialize(name, default, sql_type = nil, null = true)
|
30
|
-
@name
|
31
|
-
@sql_type
|
32
|
-
@null
|
33
|
-
@limit
|
34
|
-
@precision
|
35
|
-
@scale
|
36
|
-
@type
|
37
|
-
@default
|
38
|
-
@
|
39
|
-
@
|
30
|
+
@name = name
|
31
|
+
@sql_type = sql_type
|
32
|
+
@null = null
|
33
|
+
@limit = extract_limit(sql_type)
|
34
|
+
@precision = extract_precision(sql_type)
|
35
|
+
@scale = extract_scale(sql_type)
|
36
|
+
@type = simplified_type(sql_type)
|
37
|
+
@default = extract_default(default)
|
38
|
+
@default_function = nil
|
39
|
+
@primary = nil
|
40
|
+
@coder = nil
|
40
41
|
end
|
41
42
|
|
42
43
|
# Returns +true+ if the column is either of type string or text.
|
@@ -213,9 +213,11 @@ module ActiveRecord
|
|
213
213
|
|
214
214
|
# Executes the SQL statement in the context of this connection.
|
215
215
|
def execute(sql, name = nil)
|
216
|
-
|
217
|
-
|
218
|
-
|
216
|
+
if @connection
|
217
|
+
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
218
|
+
# made since we established the connection
|
219
|
+
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
|
220
|
+
end
|
219
221
|
|
220
222
|
super
|
221
223
|
end
|
@@ -268,6 +270,10 @@ module ActiveRecord
|
|
268
270
|
def version
|
269
271
|
@version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
270
272
|
end
|
273
|
+
|
274
|
+
def set_field_encoding field_name
|
275
|
+
field_name
|
276
|
+
end
|
271
277
|
end
|
272
278
|
end
|
273
279
|
end
|
@@ -279,11 +279,7 @@ module ActiveRecord
|
|
279
279
|
end
|
280
280
|
|
281
281
|
def exec_query(sql, name = 'SQL', binds = [])
|
282
|
-
|
283
|
-
# always be empty, since the bind variables will have been already
|
284
|
-
# substituted and removed from binds by BindVisitor, so this will
|
285
|
-
# effectively disable prepared statement usage completely.
|
286
|
-
if binds.empty?
|
282
|
+
if without_prepared_statement?(binds)
|
287
283
|
result_set, affected_rows = exec_without_stmt(sql, name)
|
288
284
|
else
|
289
285
|
result_set, affected_rows = exec_stmt(sql, name, binds)
|
@@ -393,6 +389,14 @@ module ActiveRecord
|
|
393
389
|
TYPES[new] = TYPES[old]
|
394
390
|
end
|
395
391
|
|
392
|
+
def self.find_type(field)
|
393
|
+
if field.type == Mysql::Field::TYPE_TINY && field.length > 1
|
394
|
+
TYPES[Mysql::Field::TYPE_LONG]
|
395
|
+
else
|
396
|
+
TYPES.fetch(field.type) { Fields::Identity.new }
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
396
400
|
register_type Mysql::Field::TYPE_TINY, Fields::Boolean.new
|
397
401
|
register_type Mysql::Field::TYPE_LONG, Fields::Integer.new
|
398
402
|
alias_type Mysql::Field::TYPE_LONGLONG, Mysql::Field::TYPE_LONG
|
@@ -425,9 +429,7 @@ module ActiveRecord
|
|
425
429
|
if field.decimals > 0
|
426
430
|
types[field.name] = Fields::Decimal.new
|
427
431
|
else
|
428
|
-
types[field.name] = Fields
|
429
|
-
Fields::Identity.new
|
430
|
-
}
|
432
|
+
types[field.name] = Fields.find_type field
|
431
433
|
end
|
432
434
|
}
|
433
435
|
result_set = ActiveRecord::Result.new(types.keys, result.to_a, types)
|
@@ -501,12 +503,12 @@ module ActiveRecord
|
|
501
503
|
cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
|
502
504
|
field.name
|
503
505
|
}
|
506
|
+
metadata.free
|
504
507
|
end
|
505
508
|
|
506
509
|
result_set = ActiveRecord::Result.new(cols, stmt.to_a) if cols
|
507
510
|
affected_rows = stmt.affected_rows
|
508
511
|
|
509
|
-
stmt.result_metadata.free if cols
|
510
512
|
stmt.free_result
|
511
513
|
stmt.close if binds.empty?
|
512
514
|
|
@@ -553,6 +555,14 @@ module ActiveRecord
|
|
553
555
|
def version
|
554
556
|
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
555
557
|
end
|
558
|
+
|
559
|
+
def set_field_encoding field_name
|
560
|
+
field_name.force_encoding(client_encoding)
|
561
|
+
if internal_enc = Encoding.default_internal
|
562
|
+
field_name = field_name.encode!(internal_enc)
|
563
|
+
end
|
564
|
+
field_name
|
565
|
+
end
|
556
566
|
end
|
557
567
|
end
|
558
568
|
end
|
@@ -135,11 +135,12 @@ module ActiveRecord
|
|
135
135
|
|
136
136
|
def exec_query(sql, name = 'SQL', binds = [])
|
137
137
|
log(sql, name, binds) do
|
138
|
-
result = binds
|
139
|
-
|
138
|
+
result = without_prepared_statement?(binds) ? exec_no_cache(sql, binds) :
|
139
|
+
exec_cache(sql, binds)
|
140
140
|
|
141
141
|
types = {}
|
142
|
-
result.fields
|
142
|
+
fields = result.fields
|
143
|
+
fields.each_with_index do |fname, i|
|
143
144
|
ftype = result.ftype i
|
144
145
|
fmod = result.fmod i
|
145
146
|
types[fname] = OID::TYPE_MAP.fetch(ftype, fmod) { |oid, mod|
|
@@ -148,7 +149,7 @@ module ActiveRecord
|
|
148
149
|
}
|
149
150
|
end
|
150
151
|
|
151
|
-
ret = ActiveRecord::Result.new(
|
152
|
+
ret = ActiveRecord::Result.new(fields, result.values, types)
|
152
153
|
result.clear
|
153
154
|
return ret
|
154
155
|
end
|
@@ -156,8 +157,8 @@ module ActiveRecord
|
|
156
157
|
|
157
158
|
def exec_delete(sql, name = 'SQL', binds = [])
|
158
159
|
log(sql, name, binds) do
|
159
|
-
result = binds
|
160
|
-
|
160
|
+
result = without_prepared_statement?(binds) ? exec_no_cache(sql, binds) :
|
161
|
+
exec_cache(sql, binds)
|
161
162
|
affected = result.cmd_tuples
|
162
163
|
result.clear
|
163
164
|
affected
|
@@ -38,12 +38,17 @@ module ActiveRecord
|
|
38
38
|
class Money < Type
|
39
39
|
def type_cast(value)
|
40
40
|
return if value.nil?
|
41
|
+
return value unless String === value
|
41
42
|
|
42
43
|
# Because money output is formatted according to the locale, there are two
|
43
44
|
# cases to consider (note the decimal separators):
|
44
45
|
# (1) $12,345,678.12
|
45
46
|
# (2) $12.345.678,12
|
47
|
+
# Negative values are represented as follows:
|
48
|
+
# (3) -$2.55
|
49
|
+
# (4) ($2.55)
|
46
50
|
|
51
|
+
value.sub!(/^\((.+)\)$/, '-\1') # (4)
|
47
52
|
case value
|
48
53
|
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
|
49
54
|
value.gsub!(/[^-\d.]/, '')
|
@@ -30,6 +30,7 @@ module ActiveRecord
|
|
30
30
|
when Array
|
31
31
|
case sql_type
|
32
32
|
when 'point' then super(PostgreSQLColumn.point_to_string(value))
|
33
|
+
when 'json' then super(PostgreSQLColumn.json_to_string(value))
|
33
34
|
else
|
34
35
|
if column.array
|
35
36
|
"'#{PostgreSQLColumn.array_to_string(value, column, self).gsub(/'/, "''")}'"
|
@@ -98,6 +99,7 @@ module ActiveRecord
|
|
98
99
|
when Array
|
99
100
|
case column.sql_type
|
100
101
|
when 'point' then PostgreSQLColumn.point_to_string(value)
|
102
|
+
when 'json' then PostgreSQLColumn.json_to_string(value)
|
101
103
|
else
|
102
104
|
return super(value, column) unless column.array
|
103
105
|
PostgreSQLColumn.array_to_string(value, column, self)
|
@@ -321,6 +321,7 @@ module ActiveRecord
|
|
321
321
|
result = query(<<-end_sql, 'SCHEMA')[0]
|
322
322
|
SELECT attr.attname,
|
323
323
|
CASE
|
324
|
+
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
324
325
|
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
|
325
326
|
substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
|
326
327
|
strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
|
@@ -332,7 +333,7 @@ module ActiveRecord
|
|
332
333
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
333
334
|
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
334
335
|
AND cons.contype = 'p'
|
335
|
-
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval'
|
336
|
+
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
336
337
|
end_sql
|
337
338
|
end
|
338
339
|
|
@@ -383,8 +384,9 @@ module ActiveRecord
|
|
383
384
|
def change_column(table_name, column_name, type, options = {})
|
384
385
|
clear_cache!
|
385
386
|
quoted_table_name = quote_table_name(table_name)
|
386
|
-
|
387
|
-
|
387
|
+
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
|
388
|
+
sql_type << "[]" if options[:array]
|
389
|
+
execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
|
388
390
|
|
389
391
|
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
390
392
|
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
@@ -466,14 +468,9 @@ module ActiveRecord
|
|
466
468
|
end
|
467
469
|
end
|
468
470
|
|
469
|
-
# Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
470
|
-
#
|
471
471
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
472
472
|
# requires that the ORDER BY include the distinct column.
|
473
|
-
|
474
|
-
# distinct("posts.id", ["posts.created_at desc"])
|
475
|
-
# # => "DISTINCT posts.id, posts.created_at AS alias_0"
|
476
|
-
def distinct(columns, orders) #:nodoc:
|
473
|
+
def columns_for_distinct(columns, orders) #:nodoc:
|
477
474
|
order_columns = orders.map{ |s|
|
478
475
|
# Convert Arel node to string
|
479
476
|
s = s.to_sql unless s.is_a?(String)
|
@@ -481,7 +478,7 @@ module ActiveRecord
|
|
481
478
|
s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '')
|
482
479
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
483
480
|
|
484
|
-
[super].
|
481
|
+
[super, *order_columns].join(', ')
|
485
482
|
end
|
486
483
|
end
|
487
484
|
end
|
@@ -49,13 +49,17 @@ module ActiveRecord
|
|
49
49
|
# Instantiates a new PostgreSQL column definition in a table.
|
50
50
|
def initialize(name, default, oid_type, sql_type = nil, null = true)
|
51
51
|
@oid_type = oid_type
|
52
|
+
default_value = self.class.extract_value_from_default(default)
|
53
|
+
|
52
54
|
if sql_type =~ /\[\]$/
|
53
55
|
@array = true
|
54
|
-
super(name,
|
56
|
+
super(name, default_value, sql_type[0..sql_type.length - 3], null)
|
55
57
|
else
|
56
58
|
@array = false
|
57
|
-
super(name,
|
59
|
+
super(name, default_value, sql_type, null)
|
58
60
|
end
|
61
|
+
|
62
|
+
@default_function = default if has_default_function?(default_value, default)
|
59
63
|
end
|
60
64
|
|
61
65
|
# :stopdoc:
|
@@ -138,6 +142,10 @@ module ActiveRecord
|
|
138
142
|
|
139
143
|
private
|
140
144
|
|
145
|
+
def has_default_function?(default_value, default)
|
146
|
+
!default_value && (%r{\w+\(.*\)} === default)
|
147
|
+
end
|
148
|
+
|
141
149
|
def extract_limit(sql_type)
|
142
150
|
case sql_type
|
143
151
|
when /^bigint/i; 8
|
@@ -374,15 +382,11 @@ module ActiveRecord
|
|
374
382
|
self
|
375
383
|
end
|
376
384
|
|
377
|
-
def xml(options = {})
|
378
|
-
column(args[0], :text, options)
|
379
|
-
end
|
380
|
-
|
381
385
|
private
|
382
386
|
|
383
|
-
|
384
|
-
|
385
|
-
|
387
|
+
def create_column_definition(name, type)
|
388
|
+
ColumnDefinition.new name, type
|
389
|
+
end
|
386
390
|
end
|
387
391
|
|
388
392
|
class Table < ActiveRecord::ConnectionAdapters::Table
|
@@ -436,6 +440,7 @@ module ActiveRecord
|
|
436
440
|
def prepare_column_options(column, types)
|
437
441
|
spec = super
|
438
442
|
spec[:array] = 'true' if column.respond_to?(:array) && column.array
|
443
|
+
spec[:default] = "\"#{column.default_function}\"" if column.default_function
|
439
444
|
spec
|
440
445
|
end
|
441
446
|
|
@@ -528,6 +533,7 @@ module ActiveRecord
|
|
528
533
|
super(connection, logger)
|
529
534
|
|
530
535
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
536
|
+
@prepared_statements = true
|
531
537
|
@visitor = Arel::Visitors::PostgreSQL.new self
|
532
538
|
else
|
533
539
|
@visitor = unprepared_visitor
|
@@ -623,9 +629,9 @@ module ActiveRecord
|
|
623
629
|
true
|
624
630
|
end
|
625
631
|
|
626
|
-
# Returns true if pg > 9.
|
632
|
+
# Returns true if pg > 9.1
|
627
633
|
def supports_extensions?
|
628
|
-
postgresql_version >=
|
634
|
+
postgresql_version >= 90100
|
629
635
|
end
|
630
636
|
|
631
637
|
# Range datatypes weren't introduced until PostgreSQL 9.2
|
@@ -647,9 +653,9 @@ module ActiveRecord
|
|
647
653
|
|
648
654
|
def extension_enabled?(name)
|
649
655
|
if supports_extensions?
|
650
|
-
res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL)",
|
656
|
+
res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
|
651
657
|
'SCHEMA'
|
652
|
-
res.column_types['
|
658
|
+
res.column_types['enabled'].type_cast res.rows.first.first
|
653
659
|
end
|
654
660
|
end
|
655
661
|
|
@@ -17,12 +17,14 @@ module ActiveRecord
|
|
17
17
|
# Allow database path relative to Rails.root, but only if
|
18
18
|
# the database path is not the special path that tells
|
19
19
|
# Sqlite to build a database only in memory.
|
20
|
-
if
|
21
|
-
config[:database] = File.expand_path(config[:database], Rails.root)
|
20
|
+
if ':memory:' != config[:database]
|
21
|
+
config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
|
22
|
+
dirname = File.dirname(config[:database])
|
23
|
+
Dir.mkdir(dirname) unless File.directory?(dirname)
|
22
24
|
end
|
23
25
|
|
24
26
|
db = SQLite3::Database.new(
|
25
|
-
config[:database],
|
27
|
+
config[:database].to_s,
|
26
28
|
:results_as_hash => true
|
27
29
|
)
|
28
30
|
|
@@ -111,6 +113,7 @@ module ActiveRecord
|
|
111
113
|
@config = config
|
112
114
|
|
113
115
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
116
|
+
@prepared_statements = true
|
114
117
|
@visitor = Arel::Visitors::SQLite.new self
|
115
118
|
else
|
116
119
|
@visitor = unprepared_visitor
|
@@ -291,8 +294,8 @@ module ActiveRecord
|
|
291
294
|
def exec_query(sql, name = nil, binds = [])
|
292
295
|
log(sql, name, binds) do
|
293
296
|
|
294
|
-
# Don't cache statements
|
295
|
-
if binds
|
297
|
+
# Don't cache statements if they are not prepared
|
298
|
+
if without_prepared_statement?(binds)
|
296
299
|
stmt = @connection.prepare(sql)
|
297
300
|
cols = stmt.columns
|
298
301
|
records = stmt.to_a
|