activerecord 4.0.4 → 4.1.16
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 +1632 -1797
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/examples/performance.rb +30 -18
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +2 -1
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +49 -29
- data/lib/active_record/associations/association.rb +9 -17
- data/lib/active_record/associations/association_scope.rb +59 -49
- data/lib/active_record/associations/belongs_to_association.rb +34 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +6 -1
- data/lib/active_record/associations/builder/association.rb +84 -54
- data/lib/active_record/associations/builder/belongs_to.rb +90 -58
- data/lib/active_record/associations/builder/collection_association.rb +47 -45
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +119 -25
- data/lib/active_record/associations/builder/has_many.rb +3 -3
- data/lib/active_record/associations/builder/has_one.rb +5 -7
- data/lib/active_record/associations/builder/singular_association.rb +6 -7
- data/lib/active_record/associations/collection_association.rb +121 -111
- data/lib/active_record/associations/collection_proxy.rb +73 -18
- data/lib/active_record/associations/has_many_association.rb +14 -11
- data/lib/active_record/associations/has_many_through_association.rb +33 -6
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +46 -104
- data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
- data/lib/active_record/associations/join_dependency.rb +208 -168
- data/lib/active_record/associations/preloader/association.rb +69 -27
- data/lib/active_record/associations/preloader/collection_association.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +58 -26
- data/lib/active_record/associations/preloader.rb +63 -49
- data/lib/active_record/associations/singular_association.rb +6 -5
- data/lib/active_record/associations/through_association.rb +30 -9
- data/lib/active_record/associations.rb +116 -42
- data/lib/active_record/attribute_assignment.rb +6 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
- data/lib/active_record/attribute_methods/dirty.rb +35 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -1
- data/lib/active_record/attribute_methods/read.rb +56 -29
- data/lib/active_record/attribute_methods/serialization.rb +44 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +13 -1
- data/lib/active_record/attribute_methods/write.rb +59 -26
- data/lib/active_record/attribute_methods.rb +82 -43
- data/lib/active_record/autosave_association.rb +209 -194
- data/lib/active_record/base.rb +6 -2
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +5 -10
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -24
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -13
- data/lib/active_record/connection_adapters/abstract/quoting.rb +6 -3
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +90 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +45 -70
- data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -96
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +74 -66
- data/lib/active_record/connection_adapters/column.rb +1 -35
- data/lib/active_record/connection_adapters/connection_specification.rb +231 -43
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -5
- data/lib/active_record/connection_adapters/mysql_adapter.rb +24 -17
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +22 -15
- data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -4
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -44
- data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -14
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +37 -12
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +20 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +98 -52
- data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -60
- data/lib/active_record/connection_handling.rb +39 -5
- data/lib/active_record/core.rb +38 -54
- data/lib/active_record/counter_cache.rb +9 -10
- data/lib/active_record/dynamic_matchers.rb +6 -2
- data/lib/active_record/enum.rb +199 -0
- data/lib/active_record/errors.rb +22 -5
- data/lib/active_record/fixture_set/file.rb +2 -1
- data/lib/active_record/fixtures.rb +173 -76
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +23 -9
- data/lib/active_record/integration.rb +54 -1
- data/lib/active_record/locking/optimistic.rb +7 -2
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +6 -13
- data/lib/active_record/migration/command_recorder.rb +8 -2
- data/lib/active_record/migration.rb +91 -56
- data/lib/active_record/model_schema.rb +7 -14
- data/lib/active_record/nested_attributes.rb +25 -13
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +26 -6
- data/lib/active_record/persistence.rb +23 -29
- data/lib/active_record/querying.rb +15 -12
- data/lib/active_record/railtie.rb +12 -61
- data/lib/active_record/railties/databases.rake +37 -56
- data/lib/active_record/readonly_attributes.rb +0 -6
- data/lib/active_record/reflection.rb +230 -79
- data/lib/active_record/relation/batches.rb +74 -24
- data/lib/active_record/relation/calculations.rb +52 -48
- data/lib/active_record/relation/delegation.rb +54 -39
- data/lib/active_record/relation/finder_methods.rb +210 -67
- data/lib/active_record/relation/merger.rb +15 -12
- data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder.rb +81 -40
- data/lib/active_record/relation/query_methods.rb +185 -108
- data/lib/active_record/relation/spawn_methods.rb +8 -5
- data/lib/active_record/relation.rb +79 -84
- data/lib/active_record/result.rb +45 -6
- data/lib/active_record/runtime_registry.rb +5 -0
- data/lib/active_record/sanitization.rb +4 -4
- data/lib/active_record/schema_dumper.rb +18 -6
- data/lib/active_record/schema_migration.rb +31 -18
- data/lib/active_record/scoping/default.rb +5 -18
- data/lib/active_record/scoping/named.rb +14 -29
- data/lib/active_record/scoping.rb +5 -0
- data/lib/active_record/store.rb +67 -18
- data/lib/active_record/tasks/database_tasks.rb +66 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -10
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +6 -6
- data/lib/active_record/transactions.rb +10 -12
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +19 -9
- data/lib/active_record/version.rb +4 -7
- data/lib/active_record.rb +5 -7
- data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
- data/lib/rails/generators/active_record.rb +2 -8
- metadata +18 -30
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
- data/lib/active_record/associations/join_helper.rb +0 -45
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
- data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
- data/lib/active_record/test_case.rb +0 -96
@@ -6,10 +6,6 @@ module ActiveRecord
|
|
6
6
|
module OID
|
7
7
|
class Type
|
8
8
|
def type; end
|
9
|
-
|
10
|
-
def type_cast_for_write(value)
|
11
|
-
value
|
12
|
-
end
|
13
9
|
end
|
14
10
|
|
15
11
|
class Identity < Type
|
@@ -31,6 +27,9 @@ module ActiveRecord
|
|
31
27
|
class Bytea < Type
|
32
28
|
def type_cast(value)
|
33
29
|
return if value.nil?
|
30
|
+
# This is a flawed heuristic, but it avoids truncation;
|
31
|
+
# we really shouldn’t be calling this with already-unescaped values
|
32
|
+
return value if value.dup.force_encoding("BINARY") =~ /\x00/
|
34
33
|
PGconn.unescape_bytea value
|
35
34
|
end
|
36
35
|
end
|
@@ -141,7 +140,7 @@ module ActiveRecord
|
|
141
140
|
case @subtype
|
142
141
|
when :date
|
143
142
|
from = ConnectionAdapters::Column.value_to_date(extracted[:from])
|
144
|
-
from
|
143
|
+
from += 1.day if extracted[:exclude_start]
|
145
144
|
to = ConnectionAdapters::Column.value_to_date(extracted[:to])
|
146
145
|
when :decimal
|
147
146
|
from = BigDecimal.new(extracted[:from].to_s)
|
@@ -152,7 +151,7 @@ module ActiveRecord
|
|
152
151
|
to = ConnectionAdapters::Column.string_to_time(extracted[:to])
|
153
152
|
when :integer
|
154
153
|
from = to_integer(extracted[:from]) rescue value ? 1 : 0
|
155
|
-
from
|
154
|
+
from += 1 if extracted[:exclude_start]
|
156
155
|
to = to_integer(extracted[:to]) rescue value ? 1 : 0
|
157
156
|
else
|
158
157
|
return value
|
@@ -214,9 +213,14 @@ module ActiveRecord
|
|
214
213
|
|
215
214
|
class Float < Type
|
216
215
|
def type_cast(value)
|
217
|
-
|
218
|
-
|
219
|
-
|
216
|
+
case value
|
217
|
+
when nil; nil
|
218
|
+
when 'Infinity'; ::Float::INFINITY
|
219
|
+
when '-Infinity'; -::Float::INFINITY
|
220
|
+
when 'NaN'; ::Float::NAN
|
221
|
+
else
|
222
|
+
value.to_f
|
223
|
+
end
|
220
224
|
end
|
221
225
|
end
|
222
226
|
|
@@ -229,11 +233,22 @@ module ActiveRecord
|
|
229
233
|
end
|
230
234
|
|
231
235
|
class Hstore < Type
|
236
|
+
def type_cast_for_write(value)
|
237
|
+
# roundtrip to ensure uniform uniform types
|
238
|
+
# TODO: This is not an efficient solution.
|
239
|
+
stringified = ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value)
|
240
|
+
type_cast(stringified)
|
241
|
+
end
|
242
|
+
|
232
243
|
def type_cast(value)
|
233
244
|
return if value.nil?
|
234
245
|
|
235
246
|
ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
|
236
247
|
end
|
248
|
+
|
249
|
+
def accessor
|
250
|
+
ActiveRecord::Store::StringKeyedHashAccessor
|
251
|
+
end
|
237
252
|
end
|
238
253
|
|
239
254
|
class Cidr < Type
|
@@ -245,11 +260,22 @@ module ActiveRecord
|
|
245
260
|
end
|
246
261
|
|
247
262
|
class Json < Type
|
263
|
+
def type_cast_for_write(value)
|
264
|
+
# roundtrip to ensure uniform uniform types
|
265
|
+
# TODO: This is not an efficient solution.
|
266
|
+
stringified = ConnectionAdapters::PostgreSQLColumn.json_to_string(value)
|
267
|
+
type_cast(stringified)
|
268
|
+
end
|
269
|
+
|
248
270
|
def type_cast(value)
|
249
271
|
return if value.nil?
|
250
272
|
|
251
273
|
ConnectionAdapters::PostgreSQLColumn.string_to_json value
|
252
274
|
end
|
275
|
+
|
276
|
+
def accessor
|
277
|
+
ActiveRecord::Store::StringKeyedHashAccessor
|
278
|
+
end
|
253
279
|
end
|
254
280
|
|
255
281
|
class TypeMap
|
@@ -289,17 +315,15 @@ module ActiveRecord
|
|
289
315
|
end
|
290
316
|
end
|
291
317
|
|
292
|
-
|
293
|
-
|
294
|
-
# When the PG adapter connects, the pg_type table is queried. The
|
318
|
+
# When the PG adapter connects, the pg_type table is queried. The
|
295
319
|
# key of this hash maps to the `typname` column from the table.
|
296
|
-
#
|
320
|
+
# type_map is then dynamically built with oids as the key and type
|
297
321
|
# objects as values.
|
298
322
|
NAMES = Hash.new { |h,k| # :nodoc:
|
299
323
|
h[k] = OID::Identity.new
|
300
324
|
}
|
301
325
|
|
302
|
-
# Register an OID type named +name+ with a
|
326
|
+
# Register an OID type named +name+ with a typecasting object in
|
303
327
|
# +type+. +name+ should correspond to the `typname` column in
|
304
328
|
# the `pg_type` table.
|
305
329
|
def self.register_type(name, type)
|
@@ -16,14 +16,15 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
# Quotes PostgreSQL-specific data types for SQL input.
|
18
18
|
def quote(value, column = nil) #:nodoc:
|
19
|
-
return super unless column
|
19
|
+
return super unless column && column.type
|
20
20
|
|
21
21
|
sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
|
22
22
|
|
23
23
|
case value
|
24
24
|
when Range
|
25
25
|
if /range$/ =~ sql_type
|
26
|
-
|
26
|
+
escaped = quote_string(PostgreSQLColumn.range_to_string(value))
|
27
|
+
"'#{escaped}'::#{sql_type}"
|
27
28
|
else
|
28
29
|
super
|
29
30
|
end
|
@@ -70,8 +71,8 @@ module ActiveRecord
|
|
70
71
|
when 'xml' then "xml '#{quote_string(value)}'"
|
71
72
|
when /^bit/
|
72
73
|
case value
|
73
|
-
when
|
74
|
-
when
|
74
|
+
when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
|
75
|
+
when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
|
75
76
|
end
|
76
77
|
else
|
77
78
|
super
|
@@ -86,8 +87,11 @@ module ActiveRecord
|
|
86
87
|
|
87
88
|
case value
|
88
89
|
when Range
|
89
|
-
|
90
|
-
|
90
|
+
if /range$/ =~ column.sql_type
|
91
|
+
PostgreSQLColumn.range_to_string(value)
|
92
|
+
else
|
93
|
+
super(value, column)
|
94
|
+
end
|
91
95
|
when NilClass
|
92
96
|
if column.array && array_member
|
93
97
|
'NULL'
|
@@ -101,12 +105,21 @@ module ActiveRecord
|
|
101
105
|
when 'point' then PostgreSQLColumn.point_to_string(value)
|
102
106
|
when 'json' then PostgreSQLColumn.json_to_string(value)
|
103
107
|
else
|
104
|
-
|
105
|
-
|
108
|
+
if column.array
|
109
|
+
PostgreSQLColumn.array_to_string(value, column, self)
|
110
|
+
else
|
111
|
+
super(value, column)
|
112
|
+
end
|
106
113
|
end
|
107
114
|
when String
|
108
|
-
|
109
|
-
|
115
|
+
if 'bytea' == column.sql_type
|
116
|
+
# Return a bind param hash with format as binary.
|
117
|
+
# See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
|
118
|
+
# for more information
|
119
|
+
{ value: value, format: 1 }
|
120
|
+
else
|
121
|
+
super(value, column)
|
122
|
+
end
|
110
123
|
when Hash
|
111
124
|
case column.sql_type
|
112
125
|
when 'hstore' then PostgreSQLColumn.hstore_to_string(value, array_member)
|
@@ -114,8 +127,11 @@ module ActiveRecord
|
|
114
127
|
else super(value, column)
|
115
128
|
end
|
116
129
|
when IPAddr
|
117
|
-
|
118
|
-
|
130
|
+
if %w(inet cidr).include? column.sql_type
|
131
|
+
PostgreSQLColumn.cidr_to_string(value)
|
132
|
+
else
|
133
|
+
super(value, column)
|
134
|
+
end
|
119
135
|
else
|
120
136
|
super(value, column)
|
121
137
|
end
|
@@ -167,6 +183,15 @@ module ActiveRecord
|
|
167
183
|
end
|
168
184
|
result
|
169
185
|
end
|
186
|
+
|
187
|
+
# Does not quote function default values for UUID columns
|
188
|
+
def quote_default_value(value, column) #:nodoc:
|
189
|
+
if column.type == :uuid && value =~ /\(\)/
|
190
|
+
value
|
191
|
+
else
|
192
|
+
quote(value, column)
|
193
|
+
end
|
194
|
+
end
|
170
195
|
end
|
171
196
|
end
|
172
197
|
end
|
@@ -12,7 +12,7 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
def visit_ColumnDefinition(o)
|
14
14
|
sql = super
|
15
|
-
if o.primary_key? && o.type
|
15
|
+
if o.primary_key? && o.type != :primary_key
|
16
16
|
sql << " PRIMARY KEY "
|
17
17
|
add_column_options!(sql, column_options(o))
|
18
18
|
end
|
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
46
46
|
end
|
47
47
|
|
48
48
|
# Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
|
49
|
-
# <tt>:encoding</tt
|
49
|
+
# <tt>:encoding</tt> (defaults to utf8), <tt>:collation</tt>, <tt>:ctype</tt>,
|
50
50
|
# <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
|
51
51
|
# <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
|
52
52
|
#
|
@@ -56,8 +56,8 @@ module ActiveRecord
|
|
56
56
|
def create_database(name, options = {})
|
57
57
|
options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
|
58
58
|
|
59
|
-
option_string = options.
|
60
|
-
case key
|
59
|
+
option_string = options.inject("") do |memo, (key, value)|
|
60
|
+
memo += case key
|
61
61
|
when :owner
|
62
62
|
" OWNER = \"#{value}\""
|
63
63
|
when :template
|
@@ -185,13 +185,15 @@ module ActiveRecord
|
|
185
185
|
def columns(table_name)
|
186
186
|
# Limit, precision, and scale are all handled by the superclass.
|
187
187
|
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
|
188
|
-
oid =
|
189
|
-
OID::Identity.new
|
190
|
-
}
|
188
|
+
oid = get_oid_type(oid.to_i, fmod.to_i, column_name)
|
191
189
|
PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
|
192
190
|
end
|
193
191
|
end
|
194
192
|
|
193
|
+
def column_for(table_name, column_name) #:nodoc:
|
194
|
+
columns(table_name).detect { |c| c.name == column_name.to_s }
|
195
|
+
end
|
196
|
+
|
195
197
|
# Returns the current database name.
|
196
198
|
def current_database
|
197
199
|
query('select current_database()', 'SCHEMA')[0][0]
|
@@ -380,7 +382,10 @@ module ActiveRecord
|
|
380
382
|
pk, seq = pk_and_sequence_for(new_name)
|
381
383
|
if seq == "#{table_name}_#{pk}_seq"
|
382
384
|
new_seq = "#{new_name}_#{pk}_seq"
|
385
|
+
idx = "#{table_name}_pkey"
|
386
|
+
new_idx = "#{new_name}_pkey"
|
383
387
|
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
|
388
|
+
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
384
389
|
end
|
385
390
|
|
386
391
|
rename_table_indexes(table_name, new_name)
|
@@ -408,13 +413,16 @@ module ActiveRecord
|
|
408
413
|
# Changes the default value of a table column.
|
409
414
|
def change_column_default(table_name, column_name, default)
|
410
415
|
clear_cache!
|
411
|
-
|
416
|
+
column = column_for(table_name, column_name)
|
417
|
+
|
418
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}" if column
|
412
419
|
end
|
413
420
|
|
414
421
|
def change_column_null(table_name, column_name, null, default = nil)
|
415
422
|
clear_cache!
|
416
423
|
unless null || default.nil?
|
417
|
-
|
424
|
+
column = column_for(table_name, column_name)
|
425
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
|
418
426
|
end
|
419
427
|
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
420
428
|
end
|
@@ -484,11 +492,12 @@ module ActiveRecord
|
|
484
492
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
485
493
|
# requires that the ORDER BY include the distinct column.
|
486
494
|
def columns_for_distinct(columns, orders) #:nodoc:
|
487
|
-
order_columns = orders.map{ |s|
|
495
|
+
order_columns = orders.reject(&:blank?).map{ |s|
|
488
496
|
# Convert Arel node to string
|
489
497
|
s = s.to_sql unless s.is_a?(String)
|
490
498
|
# Remove any ASC/DESC modifiers
|
491
|
-
s.gsub(/\s+(ASC|DESC)\
|
499
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, '')
|
500
|
+
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
|
492
501
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
493
502
|
|
494
503
|
[super, *order_columns].join(', ')
|
@@ -20,8 +20,8 @@ module ActiveRecord
|
|
20
20
|
VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
|
21
21
|
:client_encoding, :options, :application_name, :fallback_application_name,
|
22
22
|
:keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
|
23
|
-
:tty, :sslmode, :requiressl, :
|
24
|
-
:requirepeer, :krbsrvname, :gsslib, :service]
|
23
|
+
:tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
|
24
|
+
:sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
|
25
25
|
|
26
26
|
# Establishes a connection to the database that's used by all Active Record objects
|
27
27
|
def postgresql_connection(config)
|
@@ -96,7 +96,7 @@ module ActiveRecord
|
|
96
96
|
$1
|
97
97
|
# Character types
|
98
98
|
when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
|
99
|
-
$1
|
99
|
+
$1.gsub(/''/, "'")
|
100
100
|
# Binary data types
|
101
101
|
when /\A'(.*)'::bytea\z/m
|
102
102
|
$1
|
@@ -141,6 +141,14 @@ module ActiveRecord
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
+
def type_cast_for_write(value)
|
145
|
+
if @oid_type.respond_to?(:type_cast_for_write)
|
146
|
+
@oid_type.type_cast_for_write(value)
|
147
|
+
else
|
148
|
+
super
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
144
152
|
def type_cast(value)
|
145
153
|
return if value.nil?
|
146
154
|
return super if encoded?
|
@@ -148,6 +156,10 @@ module ActiveRecord
|
|
148
156
|
@oid_type.type_cast value
|
149
157
|
end
|
150
158
|
|
159
|
+
def accessor
|
160
|
+
@oid_type.accessor
|
161
|
+
end
|
162
|
+
|
151
163
|
private
|
152
164
|
|
153
165
|
def has_default_function?(default_value, default)
|
@@ -356,10 +368,10 @@ module ActiveRecord
|
|
356
368
|
# end
|
357
369
|
#
|
358
370
|
# By default, this will use the +uuid_generate_v4()+ function from the
|
359
|
-
# +uuid-ossp+ extension, which MUST be enabled on your
|
371
|
+
# +uuid-ossp+ extension, which MUST be enabled on your database. To enable
|
360
372
|
# the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
|
361
|
-
# migrations To use a UUID primary key without +uuid-ossp+ enabled, you can
|
362
|
-
# set the +:default+ option to nil
|
373
|
+
# migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
|
374
|
+
# set the +:default+ option to +nil+:
|
363
375
|
#
|
364
376
|
# create_table :stuffs, id: false do |t|
|
365
377
|
# t.primary_key :id, :uuid, default: nil
|
@@ -370,11 +382,10 @@ module ActiveRecord
|
|
370
382
|
# You may also pass a different UUID generation function from +uuid-ossp+
|
371
383
|
# or another library.
|
372
384
|
#
|
373
|
-
# Note that setting the UUID primary key default value to +nil+
|
374
|
-
#
|
375
|
-
#
|
376
|
-
#
|
377
|
-
# for instance.
|
385
|
+
# Note that setting the UUID primary key default value to +nil+ will
|
386
|
+
# require you to assure that you always provide a UUID value before saving
|
387
|
+
# a record (as primary keys cannot be +nil+). This might be done via the
|
388
|
+
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
|
378
389
|
def primary_key(name, type = :primary_key, options = {})
|
379
390
|
return super unless type == :uuid
|
380
391
|
options[:default] = options.fetch(:default, 'uuid_generate_v4()')
|
@@ -437,6 +448,7 @@ module ActiveRecord
|
|
437
448
|
include ReferentialIntegrity
|
438
449
|
include SchemaStatements
|
439
450
|
include DatabaseStatements
|
451
|
+
include Savepoints
|
440
452
|
|
441
453
|
# Returns 'PostgreSQL' as adapter name for identification purposes.
|
442
454
|
def adapter_name
|
@@ -561,7 +573,8 @@ module ActiveRecord
|
|
561
573
|
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
|
562
574
|
end
|
563
575
|
|
564
|
-
|
576
|
+
@type_map = OID::TypeMap.new
|
577
|
+
initialize_type_map(type_map)
|
565
578
|
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
|
566
579
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
567
580
|
end
|
@@ -632,12 +645,6 @@ module ActiveRecord
|
|
632
645
|
true
|
633
646
|
end
|
634
647
|
|
635
|
-
# Returns true, since this connection adapter supports savepoints.
|
636
|
-
def supports_savepoints?
|
637
|
-
true
|
638
|
-
end
|
639
|
-
|
640
|
-
# Returns true.
|
641
648
|
def supports_explain?
|
642
649
|
true
|
643
650
|
end
|
@@ -749,65 +756,92 @@ module ActiveRecord
|
|
749
756
|
|
750
757
|
private
|
751
758
|
|
759
|
+
def type_map
|
760
|
+
@type_map
|
761
|
+
end
|
762
|
+
|
763
|
+
def get_oid_type(oid, fmod, column_name)
|
764
|
+
type_map.fetch(oid, fmod) {
|
765
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
766
|
+
type_map[oid] = OID::Identity.new
|
767
|
+
}
|
768
|
+
end
|
769
|
+
|
752
770
|
def reload_type_map
|
753
|
-
|
754
|
-
initialize_type_map
|
771
|
+
type_map.clear
|
772
|
+
initialize_type_map(type_map)
|
755
773
|
end
|
756
774
|
|
757
|
-
def
|
775
|
+
def add_oid(row, records_by_oid, type_map)
|
776
|
+
return type_map if type_map.key? row['type_elem'].to_i
|
777
|
+
|
778
|
+
if OID.registered_type? row['typname']
|
779
|
+
# this composite type is explicitly registered
|
780
|
+
vector = OID::NAMES[row['typname']]
|
781
|
+
else
|
782
|
+
# use the default for composite types
|
783
|
+
unless type_map.key? row['typelem'].to_i
|
784
|
+
add_oid records_by_oid[row['typelem']], records_by_oid, type_map
|
785
|
+
end
|
786
|
+
|
787
|
+
vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i]
|
788
|
+
end
|
789
|
+
|
790
|
+
type_map[row['oid'].to_i] = vector
|
791
|
+
type_map
|
792
|
+
end
|
793
|
+
|
794
|
+
def initialize_type_map(type_map)
|
758
795
|
result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
|
759
796
|
leaves, nodes = result.partition { |row| row['typelem'] == '0' }
|
760
797
|
|
761
798
|
# populate the leaf nodes
|
762
799
|
leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
|
763
|
-
|
800
|
+
type_map[row['oid'].to_i] = OID::NAMES[row['typname']]
|
764
801
|
end
|
765
802
|
|
803
|
+
records_by_oid = result.group_by { |row| row['oid'] }
|
804
|
+
|
766
805
|
arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
|
767
806
|
|
768
807
|
# populate composite types
|
769
|
-
nodes.
|
770
|
-
|
771
|
-
# this composite type is explicitly registered
|
772
|
-
vector = OID::NAMES[row['typname']]
|
773
|
-
else
|
774
|
-
# use the default for composite types
|
775
|
-
vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
|
776
|
-
end
|
777
|
-
|
778
|
-
OID::TYPE_MAP[row['oid'].to_i] = vector
|
808
|
+
nodes.each do |row|
|
809
|
+
add_oid row, records_by_oid, type_map
|
779
810
|
end
|
780
811
|
|
781
812
|
# populate array types
|
782
|
-
arrays.find_all { |row|
|
783
|
-
array = OID::Array.new
|
784
|
-
|
813
|
+
arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
|
814
|
+
array = OID::Array.new type_map[row['typelem'].to_i]
|
815
|
+
type_map[row['oid'].to_i] = array
|
785
816
|
end
|
786
817
|
end
|
787
818
|
|
788
819
|
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
|
789
820
|
|
790
|
-
def exec_no_cache(sql, binds)
|
791
|
-
@connection.async_exec(sql)
|
821
|
+
def exec_no_cache(sql, name, binds)
|
822
|
+
log(sql, name, binds) { @connection.async_exec(sql, []) }
|
792
823
|
end
|
793
824
|
|
794
|
-
def exec_cache(sql, binds)
|
795
|
-
stmt_key = prepare_statement
|
825
|
+
def exec_cache(sql, name, binds)
|
826
|
+
stmt_key = prepare_statement(sql)
|
827
|
+
type_casted_binds = binds.map { |col, val|
|
828
|
+
[col, type_cast(val, col)]
|
829
|
+
}
|
830
|
+
|
831
|
+
log(sql, name, type_casted_binds, stmt_key) do
|
832
|
+
@connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val })
|
833
|
+
@connection.block
|
834
|
+
@connection.get_last_result
|
835
|
+
end
|
836
|
+
rescue ActiveRecord::StatementInvalid => e
|
837
|
+
pgerror = e.original_exception
|
796
838
|
|
797
|
-
# Clear the queue
|
798
|
-
@connection.get_last_result
|
799
|
-
@connection.send_query_prepared(stmt_key, binds.map { |col, val|
|
800
|
-
type_cast(val, col)
|
801
|
-
})
|
802
|
-
@connection.block
|
803
|
-
@connection.get_last_result
|
804
|
-
rescue PGError => e
|
805
839
|
# Get the PG code for the failure. Annoyingly, the code for
|
806
840
|
# prepared statements whose return value may have changed is
|
807
841
|
# FEATURE_NOT_SUPPORTED. Check here for more details:
|
808
842
|
# http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
809
843
|
begin
|
810
|
-
code =
|
844
|
+
code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
|
811
845
|
rescue
|
812
846
|
raise e
|
813
847
|
end
|
@@ -831,7 +865,13 @@ module ActiveRecord
|
|
831
865
|
sql_key = sql_key(sql)
|
832
866
|
unless @statements.key? sql_key
|
833
867
|
nextkey = @statements.next_key
|
834
|
-
|
868
|
+
begin
|
869
|
+
@connection.prepare nextkey, sql
|
870
|
+
rescue => e
|
871
|
+
raise translate_exception_class(e, sql)
|
872
|
+
end
|
873
|
+
# Clear the queue
|
874
|
+
@connection.get_last_result
|
835
875
|
@statements[sql_key] = nextkey
|
836
876
|
end
|
837
877
|
@statements[sql_key]
|
@@ -853,6 +893,12 @@ module ActiveRecord
|
|
853
893
|
PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
|
854
894
|
|
855
895
|
configure_connection
|
896
|
+
rescue ::PG::Error => error
|
897
|
+
if error.message.include?("does not exist")
|
898
|
+
raise ActiveRecord::NoDatabaseError.new(error.message)
|
899
|
+
else
|
900
|
+
raise error
|
901
|
+
end
|
856
902
|
end
|
857
903
|
|
858
904
|
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
|
@@ -949,12 +995,12 @@ module ActiveRecord
|
|
949
995
|
end
|
950
996
|
|
951
997
|
def extract_table_ref_from_insert_sql(sql)
|
952
|
-
sql[/into\s+([^\(]*).*values\s*\(/
|
998
|
+
sql[/into\s+([^\(]*).*values\s*\(/im]
|
953
999
|
$1.strip if $1
|
954
1000
|
end
|
955
1001
|
|
956
|
-
def create_table_definition(name, temporary, options)
|
957
|
-
TableDefinition.new native_database_types, name, temporary, options
|
1002
|
+
def create_table_definition(name, temporary, options, as = nil)
|
1003
|
+
TableDefinition.new native_database_types, name, temporary, options, as
|
958
1004
|
end
|
959
1005
|
end
|
960
1006
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'active_support/deprecation/reporting'
|
2
1
|
|
3
2
|
module ActiveRecord
|
4
3
|
module ConnectionAdapters
|
@@ -16,13 +15,8 @@ module ActiveRecord
|
|
16
15
|
prepare_default_proc
|
17
16
|
end
|
18
17
|
|
19
|
-
def primary_keys(table_name
|
20
|
-
|
21
|
-
@primary_keys[table_name]
|
22
|
-
else
|
23
|
-
ActiveSupport::Deprecation.warn('call primary_keys with a table name!')
|
24
|
-
@primary_keys.dup
|
25
|
-
end
|
18
|
+
def primary_keys(table_name)
|
19
|
+
@primary_keys[table_name]
|
26
20
|
end
|
27
21
|
|
28
22
|
# A cached lookup for table existence.
|
@@ -41,34 +35,19 @@ module ActiveRecord
|
|
41
35
|
end
|
42
36
|
end
|
43
37
|
|
44
|
-
def tables(name
|
45
|
-
|
46
|
-
@tables[name]
|
47
|
-
else
|
48
|
-
ActiveSupport::Deprecation.warn('call tables with a name!')
|
49
|
-
@tables.dup
|
50
|
-
end
|
38
|
+
def tables(name)
|
39
|
+
@tables[name]
|
51
40
|
end
|
52
41
|
|
53
42
|
# Get the columns for a table
|
54
|
-
def columns(table
|
55
|
-
|
56
|
-
@columns[table]
|
57
|
-
else
|
58
|
-
ActiveSupport::Deprecation.warn('call columns with a table name!')
|
59
|
-
@columns.dup
|
60
|
-
end
|
43
|
+
def columns(table)
|
44
|
+
@columns[table]
|
61
45
|
end
|
62
46
|
|
63
47
|
# Get the columns for a table as a hash, key is the column name
|
64
48
|
# value is the column object.
|
65
|
-
def columns_hash(table
|
66
|
-
|
67
|
-
@columns_hash[table]
|
68
|
-
else
|
69
|
-
ActiveSupport::Deprecation.warn('call columns_hash with a table name!')
|
70
|
-
@columns_hash.dup
|
71
|
-
end
|
49
|
+
def columns_hash(table)
|
50
|
+
@columns_hash[table]
|
72
51
|
end
|
73
52
|
|
74
53
|
# Clears out internal caches
|