activerecord 4.0.13 → 4.1.0.beta1
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 +745 -2700
- data/README.rdoc +2 -2
- data/examples/performance.rb +30 -18
- data/examples/simple.rb +4 -4
- data/lib/active_record.rb +2 -6
- data/lib/active_record/aggregations.rb +2 -1
- data/lib/active_record/association_relation.rb +0 -4
- data/lib/active_record/associations.rb +87 -43
- data/lib/active_record/associations/alias_tracker.rb +1 -3
- data/lib/active_record/associations/association.rb +8 -16
- data/lib/active_record/associations/association_scope.rb +5 -16
- data/lib/active_record/associations/belongs_to_association.rb +34 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +78 -54
- data/lib/active_record/associations/builder/belongs_to.rb +91 -58
- data/lib/active_record/associations/builder/collection_association.rb +47 -45
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
- data/lib/active_record/associations/builder/has_many.rb +2 -2
- 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 +68 -105
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/has_many_association.rb +11 -9
- data/lib/active_record/associations/has_many_through_association.rb +16 -12
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +204 -165
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
- 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_helper.rb +2 -11
- data/lib/active_record/associations/preloader.rb +89 -34
- data/lib/active_record/associations/preloader/association.rb +43 -25
- 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/singular_association.rb +6 -5
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +5 -2
- data/lib/active_record/attribute_methods.rb +45 -40
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
- data/lib/active_record/attribute_methods/dirty.rb +8 -22
- data/lib/active_record/attribute_methods/primary_key.rb +1 -7
- data/lib/active_record/attribute_methods/read.rb +55 -28
- data/lib/active_record/attribute_methods/serialization.rb +12 -33
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
- data/lib/active_record/attribute_methods/write.rb +37 -12
- data/lib/active_record/autosave_association.rb +207 -207
- data/lib/active_record/base.rb +5 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
- data/lib/active_record/connection_adapters/column.rb +1 -35
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
- data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
- data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
- data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
- data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
- data/lib/active_record/connection_handling.rb +2 -2
- data/lib/active_record/core.rb +22 -43
- data/lib/active_record/counter_cache.rb +7 -7
- data/lib/active_record/enum.rb +100 -0
- data/lib/active_record/errors.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +2 -1
- data/lib/active_record/fixtures.rb +171 -74
- data/lib/active_record/inheritance.rb +16 -22
- data/lib/active_record/integration.rb +52 -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 +5 -12
- data/lib/active_record/migration.rb +62 -46
- data/lib/active_record/migration/command_recorder.rb +7 -13
- data/lib/active_record/model_schema.rb +7 -14
- data/lib/active_record/nested_attributes.rb +10 -8
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +3 -3
- data/lib/active_record/persistence.rb +16 -34
- data/lib/active_record/querying.rb +14 -12
- data/lib/active_record/railtie.rb +0 -50
- data/lib/active_record/railties/databases.rake +12 -15
- data/lib/active_record/readonly_attributes.rb +0 -6
- data/lib/active_record/reflection.rb +189 -75
- data/lib/active_record/relation.rb +69 -94
- data/lib/active_record/relation/batches.rb +57 -23
- data/lib/active_record/relation/calculations.rb +36 -43
- data/lib/active_record/relation/delegation.rb +54 -39
- data/lib/active_record/relation/finder_methods.rb +107 -62
- data/lib/active_record/relation/merger.rb +7 -20
- data/lib/active_record/relation/predicate_builder.rb +57 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +110 -98
- data/lib/active_record/relation/spawn_methods.rb +1 -2
- data/lib/active_record/result.rb +45 -6
- data/lib/active_record/runtime_registry.rb +5 -0
- data/lib/active_record/sanitization.rb +6 -8
- data/lib/active_record/schema_dumper.rb +16 -5
- data/lib/active_record/schema_migration.rb +24 -25
- data/lib/active_record/scoping/default.rb +5 -18
- data/lib/active_record/scoping/named.rb +8 -29
- data/lib/active_record/store.rb +56 -28
- data/lib/active_record/tasks/database_tasks.rb +8 -4
- data/lib/active_record/timestamp.rb +4 -4
- data/lib/active_record/transactions.rb +8 -10
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -6
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +2 -8
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
- metadata +32 -45
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
- 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 -102
@@ -16,15 +16,14 @@ 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
|
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
|
-
|
27
|
-
"'#{escaped}'::#{sql_type}"
|
26
|
+
"'#{PostgreSQLColumn.range_to_string(value)}'::#{sql_type}"
|
28
27
|
else
|
29
28
|
super
|
30
29
|
end
|
@@ -71,8 +70,8 @@ module ActiveRecord
|
|
71
70
|
when 'xml' then "xml '#{quote_string(value)}'"
|
72
71
|
when /^bit/
|
73
72
|
case value
|
74
|
-
when
|
75
|
-
when
|
73
|
+
when /^[01]*$/ then "B'#{value}'" # Bit-string notation
|
74
|
+
when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
|
76
75
|
end
|
77
76
|
else
|
78
77
|
super
|
@@ -87,8 +86,11 @@ module ActiveRecord
|
|
87
86
|
|
88
87
|
case value
|
89
88
|
when Range
|
90
|
-
|
91
|
-
|
89
|
+
if /range$/ =~ column.sql_type
|
90
|
+
PostgreSQLColumn.range_to_string(value)
|
91
|
+
else
|
92
|
+
super(value, column)
|
93
|
+
end
|
92
94
|
when NilClass
|
93
95
|
if column.array && array_member
|
94
96
|
'NULL'
|
@@ -102,21 +104,33 @@ module ActiveRecord
|
|
102
104
|
when 'point' then PostgreSQLColumn.point_to_string(value)
|
103
105
|
when 'json' then PostgreSQLColumn.json_to_string(value)
|
104
106
|
else
|
105
|
-
|
106
|
-
|
107
|
+
if column.array
|
108
|
+
PostgreSQLColumn.array_to_string(value, column, self)
|
109
|
+
else
|
110
|
+
super(value, column)
|
111
|
+
end
|
107
112
|
end
|
108
113
|
when String
|
109
|
-
|
110
|
-
|
114
|
+
if 'bytea' == column.sql_type
|
115
|
+
# Return a bind param hash with format as binary.
|
116
|
+
# See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
|
117
|
+
# for more information
|
118
|
+
{ value: value, format: 1 }
|
119
|
+
else
|
120
|
+
super(value, column)
|
121
|
+
end
|
111
122
|
when Hash
|
112
123
|
case column.sql_type
|
113
|
-
when 'hstore' then PostgreSQLColumn.hstore_to_string(value
|
124
|
+
when 'hstore' then PostgreSQLColumn.hstore_to_string(value)
|
114
125
|
when 'json' then PostgreSQLColumn.json_to_string(value)
|
115
126
|
else super(value, column)
|
116
127
|
end
|
117
128
|
when IPAddr
|
118
|
-
|
119
|
-
|
129
|
+
if %w(inet cidr).include? column.sql_type
|
130
|
+
PostgreSQLColumn.cidr_to_string(value)
|
131
|
+
else
|
132
|
+
super(value, column)
|
133
|
+
end
|
120
134
|
else
|
121
135
|
super(value, column)
|
122
136
|
end
|
@@ -168,15 +182,6 @@ module ActiveRecord
|
|
168
182
|
end
|
169
183
|
result
|
170
184
|
end
|
171
|
-
|
172
|
-
# Does not quote function default values for UUID columns
|
173
|
-
def quote_default_value(value, column) #:nodoc:
|
174
|
-
if column.type == :uuid && value =~ /\(\)/
|
175
|
-
value
|
176
|
-
else
|
177
|
-
quote(value, column)
|
178
|
-
end
|
179
|
-
end
|
180
185
|
end
|
181
186
|
end
|
182
187
|
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
|
#
|
@@ -126,19 +126,6 @@ module ActiveRecord
|
|
126
126
|
SQL
|
127
127
|
end
|
128
128
|
|
129
|
-
def index_name_exists?(table_name, index_name, default)
|
130
|
-
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
131
|
-
SELECT COUNT(*)
|
132
|
-
FROM pg_class t
|
133
|
-
INNER JOIN pg_index d ON t.oid = d.indrelid
|
134
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
135
|
-
WHERE i.relkind = 'i'
|
136
|
-
AND i.relname = '#{index_name}'
|
137
|
-
AND t.relname = '#{table_name}'
|
138
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
|
139
|
-
SQL
|
140
|
-
end
|
141
|
-
|
142
129
|
# Returns an array of indexes for the given table.
|
143
130
|
def indexes(table_name, name = nil)
|
144
131
|
result = query(<<-SQL, 'SCHEMA')
|
@@ -185,15 +172,13 @@ module ActiveRecord
|
|
185
172
|
def columns(table_name)
|
186
173
|
# Limit, precision, and scale are all handled by the superclass.
|
187
174
|
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
|
188
|
-
oid =
|
175
|
+
oid = type_map.fetch(oid.to_i, fmod.to_i) {
|
176
|
+
OID::Identity.new
|
177
|
+
}
|
189
178
|
PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
|
190
179
|
end
|
191
180
|
end
|
192
181
|
|
193
|
-
def column_for(table_name, column_name) #:nodoc:
|
194
|
-
columns(table_name).detect { |c| c.name == column_name.to_s }
|
195
|
-
end
|
196
|
-
|
197
182
|
# Returns the current database name.
|
198
183
|
def current_database
|
199
184
|
query('select current_database()', 'SCHEMA')[0][0]
|
@@ -382,10 +367,7 @@ module ActiveRecord
|
|
382
367
|
pk, seq = pk_and_sequence_for(new_name)
|
383
368
|
if seq == "#{table_name}_#{pk}_seq"
|
384
369
|
new_seq = "#{new_name}_#{pk}_seq"
|
385
|
-
idx = "#{table_name}_pkey"
|
386
|
-
new_idx = "#{new_name}_pkey"
|
387
370
|
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)}"
|
389
371
|
end
|
390
372
|
|
391
373
|
rename_table_indexes(table_name, new_name)
|
@@ -413,16 +395,13 @@ module ActiveRecord
|
|
413
395
|
# Changes the default value of a table column.
|
414
396
|
def change_column_default(table_name, column_name, default)
|
415
397
|
clear_cache!
|
416
|
-
|
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
|
398
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
|
419
399
|
end
|
420
400
|
|
421
401
|
def change_column_null(table_name, column_name, null, default = nil)
|
422
402
|
clear_cache!
|
423
403
|
unless null || default.nil?
|
424
|
-
|
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
|
404
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
426
405
|
end
|
427
406
|
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
428
407
|
end
|
@@ -492,12 +471,11 @@ module ActiveRecord
|
|
492
471
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
493
472
|
# requires that the ORDER BY include the distinct column.
|
494
473
|
def columns_for_distinct(columns, orders) #:nodoc:
|
495
|
-
order_columns = orders.map{ |s|
|
474
|
+
order_columns = orders.reject(&:blank?).map{ |s|
|
496
475
|
# Convert Arel node to string
|
497
476
|
s = s.to_sql unless s.is_a?(String)
|
498
477
|
# Remove any ASC/DESC modifiers
|
499
|
-
s.gsub(/\s+(
|
500
|
-
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
|
478
|
+
s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '')
|
501
479
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
502
480
|
|
503
481
|
[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)
|
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
46
46
|
# PostgreSQL-specific extensions to column definitions in a table.
|
47
47
|
class PostgreSQLColumn < Column #:nodoc:
|
48
48
|
attr_accessor :array
|
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
52
|
default_value = self.class.extract_value_from_default(default)
|
@@ -62,14 +62,6 @@ module ActiveRecord
|
|
62
62
|
@default_function = default if has_default_function?(default_value, default)
|
63
63
|
end
|
64
64
|
|
65
|
-
def number?
|
66
|
-
!array && super
|
67
|
-
end
|
68
|
-
|
69
|
-
def text?
|
70
|
-
!array && super
|
71
|
-
end
|
72
|
-
|
73
65
|
# :stopdoc:
|
74
66
|
class << self
|
75
67
|
include ConnectionAdapters::PostgreSQLColumn::Cast
|
@@ -96,7 +88,7 @@ module ActiveRecord
|
|
96
88
|
$1
|
97
89
|
# Character types
|
98
90
|
when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
|
99
|
-
$1
|
91
|
+
$1.gsub(/''/, "'")
|
100
92
|
# Binary data types
|
101
93
|
when /\A'(.*)'::bytea\z/m
|
102
94
|
$1
|
@@ -141,6 +133,14 @@ module ActiveRecord
|
|
141
133
|
end
|
142
134
|
end
|
143
135
|
|
136
|
+
def type_cast_for_write(value)
|
137
|
+
if @oid_type.respond_to?(:type_cast_for_write)
|
138
|
+
@oid_type.type_cast_for_write(value)
|
139
|
+
else
|
140
|
+
super
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
144
|
def type_cast(value)
|
145
145
|
return if value.nil?
|
146
146
|
return super if encoded?
|
@@ -148,6 +148,10 @@ module ActiveRecord
|
|
148
148
|
@oid_type.type_cast value
|
149
149
|
end
|
150
150
|
|
151
|
+
def accessor
|
152
|
+
@oid_type.accessor
|
153
|
+
end
|
154
|
+
|
151
155
|
private
|
152
156
|
|
153
157
|
def has_default_function?(default_value, default)
|
@@ -356,10 +360,10 @@ module ActiveRecord
|
|
356
360
|
# end
|
357
361
|
#
|
358
362
|
# By default, this will use the +uuid_generate_v4()+ function from the
|
359
|
-
# +uuid-ossp+ extension, which MUST be enabled on your
|
363
|
+
# +uuid-ossp+ extension, which MUST be enabled on your database. To enable
|
360
364
|
# 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
|
365
|
+
# migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
|
366
|
+
# set the +:default+ option to +nil+:
|
363
367
|
#
|
364
368
|
# create_table :stuffs, id: false do |t|
|
365
369
|
# t.primary_key :id, :uuid, default: nil
|
@@ -370,11 +374,10 @@ module ActiveRecord
|
|
370
374
|
# You may also pass a different UUID generation function from +uuid-ossp+
|
371
375
|
# or another library.
|
372
376
|
#
|
373
|
-
# Note that setting the UUID primary key default value to +nil+
|
374
|
-
#
|
375
|
-
#
|
376
|
-
#
|
377
|
-
# for instance.
|
377
|
+
# Note that setting the UUID primary key default value to +nil+ will
|
378
|
+
# require you to assure that you always provide a UUID value before saving
|
379
|
+
# a record (as primary keys cannot be +nil+). This might be done via the
|
380
|
+
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
|
378
381
|
def primary_key(name, type = :primary_key, options = {})
|
379
382
|
return super unless type == :uuid
|
380
383
|
options[:default] = options.fetch(:default, 'uuid_generate_v4()')
|
@@ -437,6 +440,7 @@ module ActiveRecord
|
|
437
440
|
include ReferentialIntegrity
|
438
441
|
include SchemaStatements
|
439
442
|
include DatabaseStatements
|
443
|
+
include Savepoints
|
440
444
|
|
441
445
|
# Returns 'PostgreSQL' as adapter name for identification purposes.
|
442
446
|
def adapter_name
|
@@ -561,7 +565,8 @@ module ActiveRecord
|
|
561
565
|
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
|
562
566
|
end
|
563
567
|
|
564
|
-
|
568
|
+
@type_map = OID::TypeMap.new
|
569
|
+
initialize_type_map(type_map)
|
565
570
|
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
|
566
571
|
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
|
567
572
|
end
|
@@ -573,16 +578,11 @@ module ActiveRecord
|
|
573
578
|
|
574
579
|
# Is this connection alive and ready for queries?
|
575
580
|
def active?
|
576
|
-
@connection.
|
577
|
-
true
|
581
|
+
@connection.connect_poll != PG::PGRES_POLLING_FAILED
|
578
582
|
rescue PGError
|
579
583
|
false
|
580
584
|
end
|
581
585
|
|
582
|
-
def active_threadsafe?
|
583
|
-
@connection.connect_poll != PG::PGRES_POLLING_FAILED
|
584
|
-
end
|
585
|
-
|
586
586
|
# Close then reopen the connection.
|
587
587
|
def reconnect!
|
588
588
|
super
|
@@ -632,12 +632,6 @@ module ActiveRecord
|
|
632
632
|
true
|
633
633
|
end
|
634
634
|
|
635
|
-
# Returns true, since this connection adapter supports savepoints.
|
636
|
-
def supports_savepoints?
|
637
|
-
true
|
638
|
-
end
|
639
|
-
|
640
|
-
# Returns true.
|
641
635
|
def supports_explain?
|
642
636
|
true
|
643
637
|
end
|
@@ -719,10 +713,6 @@ module ActiveRecord
|
|
719
713
|
!native_database_types[type].nil?
|
720
714
|
end
|
721
715
|
|
722
|
-
def update_table_definition(table_name, base) #:nodoc:
|
723
|
-
Table.new(table_name, base)
|
724
|
-
end
|
725
|
-
|
726
716
|
protected
|
727
717
|
|
728
718
|
# Returns the version of the connected PostgreSQL server.
|
@@ -749,72 +739,85 @@ module ActiveRecord
|
|
749
739
|
|
750
740
|
private
|
751
741
|
|
752
|
-
def
|
753
|
-
|
754
|
-
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
755
|
-
OID::TYPE_MAP[oid] = OID::Identity.new
|
756
|
-
}
|
742
|
+
def type_map
|
743
|
+
@type_map
|
757
744
|
end
|
758
745
|
|
759
746
|
def reload_type_map
|
760
|
-
|
761
|
-
initialize_type_map
|
747
|
+
type_map.clear
|
748
|
+
initialize_type_map(type_map)
|
749
|
+
end
|
750
|
+
|
751
|
+
def add_oid(row, records_by_oid, type_map)
|
752
|
+
return type_map if type_map.key? row['type_elem'].to_i
|
753
|
+
|
754
|
+
if OID.registered_type? row['typname']
|
755
|
+
# this composite type is explicitly registered
|
756
|
+
vector = OID::NAMES[row['typname']]
|
757
|
+
else
|
758
|
+
# use the default for composite types
|
759
|
+
unless type_map.key? row['typelem'].to_i
|
760
|
+
add_oid records_by_oid[row['typelem']], records_by_oid, type_map
|
761
|
+
end
|
762
|
+
|
763
|
+
vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i]
|
764
|
+
end
|
765
|
+
|
766
|
+
type_map[row['oid'].to_i] = vector
|
767
|
+
type_map
|
762
768
|
end
|
763
769
|
|
764
|
-
def initialize_type_map
|
770
|
+
def initialize_type_map(type_map)
|
765
771
|
result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
|
766
772
|
leaves, nodes = result.partition { |row| row['typelem'] == '0' }
|
767
773
|
|
768
774
|
# populate the leaf nodes
|
769
775
|
leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
|
770
|
-
|
776
|
+
type_map[row['oid'].to_i] = OID::NAMES[row['typname']]
|
771
777
|
end
|
772
778
|
|
779
|
+
records_by_oid = result.group_by { |row| row['oid'] }
|
780
|
+
|
773
781
|
arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
|
774
782
|
|
775
783
|
# populate composite types
|
776
|
-
nodes.
|
777
|
-
|
778
|
-
# this composite type is explicitly registered
|
779
|
-
vector = OID::NAMES[row['typname']]
|
780
|
-
else
|
781
|
-
# use the default for composite types
|
782
|
-
vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
|
783
|
-
end
|
784
|
-
|
785
|
-
OID::TYPE_MAP[row['oid'].to_i] = vector
|
784
|
+
nodes.each do |row|
|
785
|
+
add_oid row, records_by_oid, type_map
|
786
786
|
end
|
787
787
|
|
788
788
|
# populate array types
|
789
|
-
arrays.find_all { |row|
|
790
|
-
array = OID::Array.new
|
791
|
-
|
789
|
+
arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
|
790
|
+
array = OID::Array.new type_map[row['typelem'].to_i]
|
791
|
+
type_map[row['oid'].to_i] = array
|
792
792
|
end
|
793
793
|
end
|
794
794
|
|
795
|
-
FEATURE_NOT_SUPPORTED = "0A000"
|
795
|
+
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
796
796
|
|
797
|
-
def exec_no_cache(sql, binds)
|
798
|
-
@connection.async_exec(sql
|
797
|
+
def exec_no_cache(sql, name, binds)
|
798
|
+
log(sql, name, binds) { @connection.async_exec(sql) }
|
799
799
|
end
|
800
800
|
|
801
|
-
def exec_cache(sql, binds)
|
802
|
-
stmt_key = prepare_statement
|
801
|
+
def exec_cache(sql, name, binds)
|
802
|
+
stmt_key = prepare_statement(sql)
|
803
|
+
type_casted_binds = binds.map { |col, val|
|
804
|
+
[col, type_cast(val, col)]
|
805
|
+
}
|
806
|
+
|
807
|
+
log(sql, name, type_casted_binds, stmt_key) do
|
808
|
+
@connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val })
|
809
|
+
@connection.block
|
810
|
+
@connection.get_last_result
|
811
|
+
end
|
812
|
+
rescue ActiveRecord::StatementInvalid => e
|
813
|
+
pgerror = e.original_exception
|
803
814
|
|
804
|
-
# Clear the queue
|
805
|
-
@connection.get_last_result
|
806
|
-
@connection.send_query_prepared(stmt_key, binds.map { |col, val|
|
807
|
-
type_cast(val, col)
|
808
|
-
})
|
809
|
-
@connection.block
|
810
|
-
@connection.get_last_result
|
811
|
-
rescue PGError => e
|
812
815
|
# Get the PG code for the failure. Annoyingly, the code for
|
813
816
|
# prepared statements whose return value may have changed is
|
814
817
|
# FEATURE_NOT_SUPPORTED. Check here for more details:
|
815
818
|
# http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
816
819
|
begin
|
817
|
-
code =
|
820
|
+
code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
|
818
821
|
rescue
|
819
822
|
raise e
|
820
823
|
end
|
@@ -839,6 +842,8 @@ module ActiveRecord
|
|
839
842
|
unless @statements.key? sql_key
|
840
843
|
nextkey = @statements.next_key
|
841
844
|
@connection.prepare nextkey, sql
|
845
|
+
# Clear the queue
|
846
|
+
@connection.get_last_result
|
842
847
|
@statements[sql_key] = nextkey
|
843
848
|
end
|
844
849
|
@statements[sql_key]
|
@@ -915,6 +920,14 @@ module ActiveRecord
|
|
915
920
|
exec_query(sql, name, binds)
|
916
921
|
end
|
917
922
|
|
923
|
+
def select_raw(sql, name = nil)
|
924
|
+
res = execute(sql, name)
|
925
|
+
results = result_as_array(res)
|
926
|
+
fields = res.fields
|
927
|
+
res.clear
|
928
|
+
return fields, results
|
929
|
+
end
|
930
|
+
|
918
931
|
# Returns the list of a table's column names, data types, and default values.
|
919
932
|
#
|
920
933
|
# The underlying query is roughly:
|
@@ -960,8 +973,12 @@ module ActiveRecord
|
|
960
973
|
$1.strip if $1
|
961
974
|
end
|
962
975
|
|
963
|
-
def create_table_definition(name, temporary, options)
|
964
|
-
TableDefinition.new native_database_types, name, temporary, options
|
976
|
+
def create_table_definition(name, temporary, options, as = nil)
|
977
|
+
TableDefinition.new native_database_types, name, temporary, options, as
|
978
|
+
end
|
979
|
+
|
980
|
+
def update_table_definition(table_name, base)
|
981
|
+
Table.new(table_name, base)
|
965
982
|
end
|
966
983
|
end
|
967
984
|
end
|