activerecord 4.1.15 → 4.2.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.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2176
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/association_scope.rb +53 -21
  7. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  8. data/lib/active_record/associations/builder/association.rb +16 -5
  9. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  10. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  11. data/lib/active_record/associations/builder/has_one.rb +2 -2
  12. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  13. data/lib/active_record/associations/collection_association.rb +32 -44
  14. data/lib/active_record/associations/collection_proxy.rb +1 -10
  15. data/lib/active_record/associations/has_many_association.rb +60 -14
  16. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  17. data/lib/active_record/associations/has_one_association.rb +0 -1
  18. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  19. data/lib/active_record/associations/join_dependency.rb +7 -9
  20. data/lib/active_record/associations/preloader/association.rb +9 -5
  21. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/singular_association.rb +16 -1
  24. data/lib/active_record/associations/through_association.rb +6 -22
  25. data/lib/active_record/associations.rb +58 -33
  26. data/lib/active_record/attribute.rb +131 -0
  27. data/lib/active_record/attribute_assignment.rb +19 -11
  28. data/lib/active_record/attribute_decorators.rb +66 -0
  29. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  30. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  31. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  32. data/lib/active_record/attribute_methods/read.rb +14 -57
  33. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  35. data/lib/active_record/attribute_methods/write.rb +8 -23
  36. data/lib/active_record/attribute_methods.rb +53 -90
  37. data/lib/active_record/attribute_set/builder.rb +32 -0
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attributes.rb +122 -0
  40. data/lib/active_record/autosave_association.rb +11 -21
  41. data/lib/active_record/base.rb +9 -19
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  52. data/lib/active_record/connection_adapters/column.rb +13 -244
  53. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  55. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  56. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  59. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  60. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  85. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  86. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  87. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  88. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  89. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  90. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  91. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  93. data/lib/active_record/connection_handling.rb +1 -1
  94. data/lib/active_record/core.rb +119 -22
  95. data/lib/active_record/counter_cache.rb +60 -6
  96. data/lib/active_record/enum.rb +9 -10
  97. data/lib/active_record/errors.rb +27 -26
  98. data/lib/active_record/explain.rb +1 -1
  99. data/lib/active_record/fixtures.rb +52 -45
  100. data/lib/active_record/gem_version.rb +3 -3
  101. data/lib/active_record/inheritance.rb +33 -8
  102. data/lib/active_record/integration.rb +4 -4
  103. data/lib/active_record/locking/optimistic.rb +34 -16
  104. data/lib/active_record/migration/command_recorder.rb +19 -2
  105. data/lib/active_record/migration/join_table.rb +1 -1
  106. data/lib/active_record/migration.rb +22 -32
  107. data/lib/active_record/model_schema.rb +39 -48
  108. data/lib/active_record/nested_attributes.rb +8 -18
  109. data/lib/active_record/persistence.rb +39 -22
  110. data/lib/active_record/query_cache.rb +3 -3
  111. data/lib/active_record/querying.rb +1 -8
  112. data/lib/active_record/railtie.rb +17 -10
  113. data/lib/active_record/railties/databases.rake +47 -42
  114. data/lib/active_record/readonly_attributes.rb +0 -1
  115. data/lib/active_record/reflection.rb +225 -92
  116. data/lib/active_record/relation/batches.rb +0 -2
  117. data/lib/active_record/relation/calculations.rb +28 -32
  118. data/lib/active_record/relation/delegation.rb +1 -1
  119. data/lib/active_record/relation/finder_methods.rb +42 -20
  120. data/lib/active_record/relation/merger.rb +0 -1
  121. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  122. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/query_methods.rb +98 -62
  125. data/lib/active_record/relation/spawn_methods.rb +6 -7
  126. data/lib/active_record/relation.rb +35 -11
  127. data/lib/active_record/result.rb +16 -9
  128. data/lib/active_record/sanitization.rb +8 -1
  129. data/lib/active_record/schema.rb +0 -1
  130. data/lib/active_record/schema_dumper.rb +51 -9
  131. data/lib/active_record/schema_migration.rb +4 -0
  132. data/lib/active_record/scoping/default.rb +5 -4
  133. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  134. data/lib/active_record/statement_cache.rb +79 -5
  135. data/lib/active_record/store.rb +5 -5
  136. data/lib/active_record/tasks/database_tasks.rb +37 -5
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  138. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  139. data/lib/active_record/timestamp.rb +9 -7
  140. data/lib/active_record/transactions.rb +35 -21
  141. data/lib/active_record/type/binary.rb +40 -0
  142. data/lib/active_record/type/boolean.rb +19 -0
  143. data/lib/active_record/type/date.rb +46 -0
  144. data/lib/active_record/type/date_time.rb +43 -0
  145. data/lib/active_record/type/decimal.rb +40 -0
  146. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  147. data/lib/active_record/type/float.rb +19 -0
  148. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  149. data/lib/active_record/type/integer.rb +23 -0
  150. data/lib/active_record/type/mutable.rb +16 -0
  151. data/lib/active_record/type/numeric.rb +36 -0
  152. data/lib/active_record/type/serialized.rb +51 -0
  153. data/lib/active_record/type/string.rb +36 -0
  154. data/lib/active_record/type/text.rb +11 -0
  155. data/lib/active_record/type/time.rb +26 -0
  156. data/lib/active_record/type/time_value.rb +38 -0
  157. data/lib/active_record/type/type_map.rb +48 -0
  158. data/lib/active_record/type/value.rb +101 -0
  159. data/lib/active_record/type.rb +20 -0
  160. data/lib/active_record/validations/uniqueness.rb +9 -23
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record.rb +2 -1
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,12 +1,15 @@
1
1
  require 'active_record/connection_adapters/abstract_adapter'
2
2
  require 'active_record/connection_adapters/statement_pool'
3
+
4
+ require 'active_record/connection_adapters/postgresql/utils'
5
+ require 'active_record/connection_adapters/postgresql/column'
3
6
  require 'active_record/connection_adapters/postgresql/oid'
4
- require 'active_record/connection_adapters/postgresql/cast'
5
- require 'active_record/connection_adapters/postgresql/array_parser'
6
7
  require 'active_record/connection_adapters/postgresql/quoting'
8
+ require 'active_record/connection_adapters/postgresql/referential_integrity'
9
+ require 'active_record/connection_adapters/postgresql/schema_definitions'
7
10
  require 'active_record/connection_adapters/postgresql/schema_statements'
8
11
  require 'active_record/connection_adapters/postgresql/database_statements'
9
- require 'active_record/connection_adapters/postgresql/referential_integrity'
12
+
10
13
  require 'arel/visitors/bind_visitor'
11
14
 
12
15
  # Make sure we're using pg high enough for PGResult#values
@@ -43,222 +46,6 @@ module ActiveRecord
43
46
  end
44
47
 
45
48
  module ConnectionAdapters
46
- # PostgreSQL-specific extensions to column definitions in a table.
47
- class PostgreSQLColumn < Column #:nodoc:
48
- attr_accessor :array
49
-
50
- def initialize(name, default, oid_type, sql_type = nil, null = true)
51
- @oid_type = oid_type
52
- default_value = self.class.extract_value_from_default(default)
53
-
54
- if sql_type =~ /\[\]$/
55
- @array = true
56
- super(name, default_value, sql_type[0..sql_type.length - 3], null)
57
- else
58
- @array = false
59
- super(name, default_value, sql_type, null)
60
- end
61
-
62
- @default_function = default if has_default_function?(default_value, default)
63
- end
64
-
65
- def number?
66
- !array && super
67
- end
68
-
69
- def text?
70
- !array && super
71
- end
72
-
73
- # :stopdoc:
74
- class << self
75
- include ConnectionAdapters::PostgreSQLColumn::Cast
76
- include ConnectionAdapters::PostgreSQLColumn::ArrayParser
77
- attr_accessor :money_precision
78
- end
79
- # :startdoc:
80
-
81
- # Extracts the value from a PostgreSQL column default definition.
82
- def self.extract_value_from_default(default)
83
- # This is a performance optimization for Ruby 1.9.2 in development.
84
- # If the value is nil, we return nil straight away without checking
85
- # the regular expressions. If we check each regular expression,
86
- # Regexp#=== will call NilClass#to_str, which will trigger
87
- # method_missing (defined by whiny nil in ActiveSupport) which
88
- # makes this method very very slow.
89
- return default unless default
90
-
91
- case default
92
- when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
93
- $1
94
- # Numeric types
95
- when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
96
- $1
97
- # Character types
98
- when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
99
- $1.gsub(/''/, "'")
100
- # Binary data types
101
- when /\A'(.*)'::bytea\z/m
102
- $1
103
- # Date/time types
104
- when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
105
- $1
106
- when /\A'(.*)'::interval\z/
107
- $1
108
- # Boolean type
109
- when 'true'
110
- true
111
- when 'false'
112
- false
113
- # Geometric types
114
- when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
115
- $1
116
- # Network address types
117
- when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
118
- $1
119
- # Bit string types
120
- when /\AB'(.*)'::"?bit(?: varying)?"?\z/
121
- $1
122
- # XML type
123
- when /\A'(.*)'::xml\z/m
124
- $1
125
- # Arrays
126
- when /\A'(.*)'::"?\D+"?\[\]\z/
127
- $1
128
- # Hstore
129
- when /\A'(.*)'::hstore\z/
130
- $1
131
- # JSON
132
- when /\A'(.*)'::json\z/
133
- $1
134
- # Object identifier types
135
- when /\A-?\d+\z/
136
- $1
137
- else
138
- # Anything else is blank, some user type, or some function
139
- # and we can't know the value of that, so return nil.
140
- nil
141
- end
142
- end
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
-
152
- def type_cast(value)
153
- return if value.nil?
154
- return super if encoded?
155
-
156
- @oid_type.type_cast value
157
- end
158
-
159
- def accessor
160
- @oid_type.accessor
161
- end
162
-
163
- private
164
-
165
- def has_default_function?(default_value, default)
166
- !default_value && (%r{\w+\(.*\)} === default)
167
- end
168
-
169
- def extract_limit(sql_type)
170
- case sql_type
171
- when /^bigint/i; 8
172
- when /^smallint/i; 2
173
- when /^timestamp/i; nil
174
- else super
175
- end
176
- end
177
-
178
- # Extracts the scale from PostgreSQL-specific data types.
179
- def extract_scale(sql_type)
180
- # Money type has a fixed scale of 2.
181
- sql_type =~ /^money/ ? 2 : super
182
- end
183
-
184
- # Extracts the precision from PostgreSQL-specific data types.
185
- def extract_precision(sql_type)
186
- if sql_type == 'money'
187
- self.class.money_precision
188
- elsif sql_type =~ /timestamp/i
189
- $1.to_i if sql_type =~ /\((\d+)\)/
190
- else
191
- super
192
- end
193
- end
194
-
195
- # Maps PostgreSQL-specific data types to logical Rails types.
196
- def simplified_type(field_type)
197
- case field_type
198
- # Numeric and monetary types
199
- when /^(?:real|double precision)$/
200
- :float
201
- # Monetary types
202
- when 'money'
203
- :decimal
204
- when 'hstore'
205
- :hstore
206
- when 'ltree'
207
- :ltree
208
- # Network address types
209
- when 'inet'
210
- :inet
211
- when 'cidr'
212
- :cidr
213
- when 'macaddr'
214
- :macaddr
215
- # Character types
216
- when /^(?:character varying|bpchar)(?:\(\d+\))?$/
217
- :string
218
- # Binary data types
219
- when 'bytea'
220
- :binary
221
- # Date/time types
222
- when /^timestamp with(?:out)? time zone$/
223
- :datetime
224
- when /^interval(?:|\(\d+\))$/
225
- :string
226
- # Geometric types
227
- when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
228
- :string
229
- # Bit strings
230
- when /^bit(?: varying)?(?:\(\d+\))?$/
231
- :string
232
- # XML type
233
- when 'xml'
234
- :xml
235
- # tsvector type
236
- when 'tsvector'
237
- :tsvector
238
- # Arrays
239
- when /^\D+\[\]$/
240
- :string
241
- # Object identifier types
242
- when 'oid'
243
- :integer
244
- # UUID type
245
- when 'uuid'
246
- :uuid
247
- # JSON type
248
- when 'json'
249
- :json
250
- # Small and big integer types
251
- when /^(?:small|big)int$/
252
- :integer
253
- when /(num|date|tstz|ts|int4|int8)range$/
254
- field_type.to_sym
255
- # Pass through all types that are not specific to PostgreSQL.
256
- else
257
- super
258
- end
259
- end
260
- end
261
-
262
49
  # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
263
50
  #
264
51
  # Options:
@@ -287,142 +74,16 @@ module ActiveRecord
287
74
  # In addition, default connection parameters of libpq can be set per environment variables.
288
75
  # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
289
76
  class PostgreSQLAdapter < AbstractAdapter
290
- class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
291
- attr_accessor :array
292
- end
293
-
294
- module ColumnMethods
295
- def xml(*args)
296
- options = args.extract_options!
297
- column(args[0], 'xml', options)
298
- end
299
-
300
- def tsvector(*args)
301
- options = args.extract_options!
302
- column(args[0], 'tsvector', options)
303
- end
304
-
305
- def int4range(name, options = {})
306
- column(name, 'int4range', options)
307
- end
308
-
309
- def int8range(name, options = {})
310
- column(name, 'int8range', options)
311
- end
312
-
313
- def tsrange(name, options = {})
314
- column(name, 'tsrange', options)
315
- end
316
-
317
- def tstzrange(name, options = {})
318
- column(name, 'tstzrange', options)
319
- end
320
-
321
- def numrange(name, options = {})
322
- column(name, 'numrange', options)
323
- end
324
-
325
- def daterange(name, options = {})
326
- column(name, 'daterange', options)
327
- end
328
-
329
- def hstore(name, options = {})
330
- column(name, 'hstore', options)
331
- end
332
-
333
- def ltree(name, options = {})
334
- column(name, 'ltree', options)
335
- end
336
-
337
- def inet(name, options = {})
338
- column(name, 'inet', options)
339
- end
340
-
341
- def cidr(name, options = {})
342
- column(name, 'cidr', options)
343
- end
344
-
345
- def macaddr(name, options = {})
346
- column(name, 'macaddr', options)
347
- end
348
-
349
- def uuid(name, options = {})
350
- column(name, 'uuid', options)
351
- end
352
-
353
- def json(name, options = {})
354
- column(name, 'json', options)
355
- end
356
- end
357
-
358
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
359
- include ColumnMethods
360
-
361
- # Defines the primary key field.
362
- # Use of the native PostgreSQL UUID type is supported, and can be used
363
- # by defining your tables as such:
364
- #
365
- # create_table :stuffs, id: :uuid do |t|
366
- # t.string :content
367
- # t.timestamps
368
- # end
369
- #
370
- # By default, this will use the +uuid_generate_v4()+ function from the
371
- # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
372
- # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
373
- # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
374
- # set the +:default+ option to +nil+:
375
- #
376
- # create_table :stuffs, id: false do |t|
377
- # t.primary_key :id, :uuid, default: nil
378
- # t.uuid :foo_id
379
- # t.timestamps
380
- # end
381
- #
382
- # You may also pass a different UUID generation function from +uuid-ossp+
383
- # or another library.
384
- #
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.
389
- def primary_key(name, type = :primary_key, options = {})
390
- return super unless type == :uuid
391
- options[:default] = options.fetch(:default, 'uuid_generate_v4()')
392
- options[:primary_key] = true
393
- column name, type, options
394
- end
395
-
396
- def column(name, type = nil, options = {})
397
- super
398
- column = self[name]
399
- column.array = options[:array]
400
-
401
- self
402
- end
403
-
404
- private
405
-
406
- def create_column_definition(name, type)
407
- ColumnDefinition.new name, type
408
- end
409
- end
410
-
411
- class Table < ActiveRecord::ConnectionAdapters::Table
412
- include ColumnMethods
413
- end
414
-
415
77
  ADAPTER_NAME = 'PostgreSQL'
416
78
 
417
79
  NATIVE_DATABASE_TYPES = {
418
80
  primary_key: "serial primary key",
419
- string: { name: "character varying", limit: 255 },
81
+ string: { name: "character varying" },
420
82
  text: { name: "text" },
421
83
  integer: { name: "integer" },
422
84
  float: { name: "float" },
423
85
  decimal: { name: "decimal" },
424
86
  datetime: { name: "timestamp" },
425
- timestamp: { name: "timestamp" },
426
87
  time: { name: "time" },
427
88
  date: { name: "date" },
428
89
  daterange: { name: "daterange" },
@@ -441,13 +102,20 @@ module ActiveRecord
441
102
  macaddr: { name: "macaddr" },
442
103
  uuid: { name: "uuid" },
443
104
  json: { name: "json" },
444
- ltree: { name: "ltree" }
105
+ ltree: { name: "ltree" },
106
+ citext: { name: "citext" },
107
+ point: { name: "point" },
108
+ bit: { name: "bit" },
109
+ bit_varying: { name: "bit varying" },
110
+ money: { name: "money" },
445
111
  }
446
112
 
447
- include Quoting
448
- include ReferentialIntegrity
449
- include SchemaStatements
450
- include DatabaseStatements
113
+ OID = PostgreSQL::OID #:nodoc:
114
+
115
+ include PostgreSQL::Quoting
116
+ include PostgreSQL::ReferentialIntegrity
117
+ include PostgreSQL::SchemaStatements
118
+ include PostgreSQL::DatabaseStatements
451
119
  include Savepoints
452
120
 
453
121
  # Returns 'PostgreSQL' as adapter name for identification purposes.
@@ -455,9 +123,13 @@ module ActiveRecord
455
123
  ADAPTER_NAME
456
124
  end
457
125
 
126
+ def schema_creation # :nodoc:
127
+ PostgreSQL::SchemaCreation.new self
128
+ end
129
+
458
130
  # Adds `:array` option to the default set provided by the
459
131
  # AbstractAdapter
460
- def prepare_column_options(column, types)
132
+ def prepare_column_options(column, types) # :nodoc:
461
133
  spec = super
462
134
  spec[:array] = 'true' if column.respond_to?(:array) && column.array
463
135
  spec[:default] = "\"#{column.default_function}\"" if column.default_function
@@ -487,6 +159,10 @@ module ActiveRecord
487
159
  true
488
160
  end
489
161
 
162
+ def supports_foreign_keys?
163
+ true
164
+ end
165
+
490
166
  def index_algorithms
491
167
  { concurrently: 'CONCURRENTLY' }
492
168
  end
@@ -544,19 +220,15 @@ module ActiveRecord
544
220
  end
545
221
  end
546
222
 
547
- class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
548
- include Arel::Visitors::BindVisitor
549
- end
550
-
551
223
  # Initializes and connects a PostgreSQL adapter.
552
224
  def initialize(connection, logger, connection_parameters, config)
553
225
  super(connection, logger)
554
226
 
227
+ @visitor = Arel::Visitors::PostgreSQL.new self
555
228
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
556
229
  @prepared_statements = true
557
- @visitor = Arel::Visitors::PostgreSQL.new self
558
230
  else
559
- @visitor = unprepared_visitor
231
+ @prepared_statements = false
560
232
  end
561
233
 
562
234
  @connection_parameters, @config = connection_parameters, config
@@ -573,7 +245,7 @@ module ActiveRecord
573
245
  raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
574
246
  end
575
247
 
576
- @type_map = OID::TypeMap.new
248
+ @type_map = Type::HashLookupTypeMap.new
577
249
  initialize_type_map(type_map)
578
250
  @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
579
251
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
@@ -592,10 +264,6 @@ module ActiveRecord
592
264
  false
593
265
  end
594
266
 
595
- def active_threadsafe?
596
- @connection.connect_poll != PG::PGRES_POLLING_FAILED
597
- end
598
-
599
267
  # Close then reopen the connection.
600
268
  def reconnect!
601
269
  super
@@ -605,7 +273,12 @@ module ActiveRecord
605
273
 
606
274
  def reset!
607
275
  clear_cache!
608
- super
276
+ reset_transaction
277
+ unless @connection.transaction_status == ::PG::PQTRANS_IDLE
278
+ @connection.query 'ROLLBACK'
279
+ end
280
+ @connection.query 'DISCARD ALL'
281
+ configure_connection
609
282
  end
610
283
 
611
284
  # Disconnects from the database if already connected. Otherwise, this
@@ -637,10 +310,6 @@ module ActiveRecord
637
310
  self.client_min_messages = old
638
311
  end
639
312
 
640
- def supports_insert_with_returning?
641
- true
642
- end
643
-
644
313
  def supports_ddl_transactions?
645
314
  true
646
315
  end
@@ -659,6 +328,10 @@ module ActiveRecord
659
328
  postgresql_version >= 90200
660
329
  end
661
330
 
331
+ def supports_materialized_views?
332
+ postgresql_version >= 90300
333
+ end
334
+
662
335
  def enable_extension(name)
663
336
  exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
664
337
  reload_type_map
@@ -675,14 +348,13 @@ module ActiveRecord
675
348
  if supports_extensions?
676
349
  res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
677
350
  'SCHEMA'
678
- res.column_types['enabled'].type_cast res.rows.first.first
351
+ res.cast_values.first
679
352
  end
680
353
  end
681
354
 
682
355
  def extensions
683
356
  if supports_extensions?
684
- res = exec_query "SELECT extname from pg_extension", "SCHEMA"
685
- res.rows.map { |r| res.column_types['extname'].type_cast r.first }
357
+ exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
686
358
  else
687
359
  super
688
360
  end
@@ -699,25 +371,6 @@ module ActiveRecord
699
371
  exec_query "SET SESSION AUTHORIZATION #{user}"
700
372
  end
701
373
 
702
- module Utils
703
- extend self
704
-
705
- # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
706
- # +schema_name+ is nil if not specified in +name+.
707
- # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
708
- # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
709
- #
710
- # * <tt>table_name</tt>
711
- # * <tt>"table.name"</tt>
712
- # * <tt>schema_name.table_name</tt>
713
- # * <tt>schema_name."table.name"</tt>
714
- # * <tt>"schema.name"."table name"</tt>
715
- def extract_schema_and_table(name)
716
- table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
717
- [schema, table]
718
- end
719
- end
720
-
721
374
  def use_insert_returning?
722
375
  @use_insert_returning
723
376
  end
@@ -727,7 +380,12 @@ module ActiveRecord
727
380
  end
728
381
 
729
382
  def update_table_definition(table_name, base) #:nodoc:
730
- Table.new(table_name, base)
383
+ PostgreSQL::Table.new(table_name, base)
384
+ end
385
+
386
+ def lookup_cast_type(sql_type) # :nodoc:
387
+ oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
388
+ super(oid)
731
389
  end
732
390
 
733
391
  protected
@@ -756,68 +414,161 @@ module ActiveRecord
756
414
 
757
415
  private
758
416
 
759
- def type_map
760
- @type_map
761
- end
417
+ def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
418
+ if !type_map.key?(oid)
419
+ load_additional_types(type_map, [oid])
420
+ end
762
421
 
763
- def get_oid_type(oid, fmod, column_name)
764
- type_map.fetch(oid, fmod) {
422
+ type_map.fetch(oid, fmod, sql_type) {
765
423
  warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
766
- type_map[oid] = OID::Identity.new
424
+ Type::Value.new.tap do |cast_type|
425
+ type_map.register_type(oid, cast_type)
426
+ end
767
427
  }
768
428
  end
769
429
 
770
- def reload_type_map
771
- type_map.clear
772
- initialize_type_map(type_map)
773
- end
774
-
775
- def add_oid(row, records_by_oid, type_map)
776
- return type_map if type_map.key? row['type_elem'].to_i
430
+ def initialize_type_map(m) # :nodoc:
431
+ register_class_with_limit m, 'int2', OID::Integer
432
+ m.alias_type 'int4', 'int2'
433
+ m.alias_type 'int8', 'int2'
434
+ m.alias_type 'oid', 'int2'
435
+ m.register_type 'float4', OID::Float.new
436
+ m.alias_type 'float8', 'float4'
437
+ m.register_type 'text', Type::Text.new
438
+ register_class_with_limit m, 'varchar', Type::String
439
+ m.alias_type 'char', 'varchar'
440
+ m.alias_type 'name', 'varchar'
441
+ m.alias_type 'bpchar', 'varchar'
442
+ m.register_type 'bool', Type::Boolean.new
443
+ register_class_with_limit m, 'bit', OID::Bit
444
+ register_class_with_limit m, 'varbit', OID::BitVarying
445
+ m.alias_type 'timestamptz', 'timestamp'
446
+ m.register_type 'date', OID::Date.new
447
+ m.register_type 'time', OID::Time.new
448
+
449
+ m.register_type 'money', OID::Money.new
450
+ m.register_type 'bytea', OID::Bytea.new
451
+ m.register_type 'point', OID::Point.new
452
+ m.register_type 'hstore', OID::Hstore.new
453
+ m.register_type 'json', OID::Json.new
454
+ m.register_type 'jsonb', OID::Jsonb.new
455
+ m.register_type 'cidr', OID::Cidr.new
456
+ m.register_type 'inet', OID::Inet.new
457
+ m.register_type 'uuid', OID::Uuid.new
458
+ m.register_type 'xml', OID::Xml.new
459
+ m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
460
+ m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
461
+ m.register_type 'citext', OID::SpecializedString.new(:citext)
462
+ m.register_type 'ltree', OID::SpecializedString.new(:ltree)
463
+
464
+ # FIXME: why are we keeping these types as strings?
465
+ m.alias_type 'interval', 'varchar'
466
+ m.alias_type 'path', 'varchar'
467
+ m.alias_type 'line', 'varchar'
468
+ m.alias_type 'polygon', 'varchar'
469
+ m.alias_type 'circle', 'varchar'
470
+ m.alias_type 'lseg', 'varchar'
471
+ m.alias_type 'box', 'varchar'
472
+
473
+ m.register_type 'timestamp' do |_, _, sql_type|
474
+ precision = extract_precision(sql_type)
475
+ OID::DateTime.new(precision: precision)
476
+ end
777
477
 
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
478
+ m.register_type 'numeric' do |_, fmod, sql_type|
479
+ precision = extract_precision(sql_type)
480
+ scale = extract_scale(sql_type)
481
+
482
+ # The type for the numeric depends on the width of the field,
483
+ # so we'll do something special here.
484
+ #
485
+ # When dealing with decimal columns:
486
+ #
487
+ # places after decimal = fmod - 4 & 0xffff
488
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
489
+ if fmod && (fmod - 4 & 0xffff).zero?
490
+ # FIXME: Remove this class, and the second argument to
491
+ # lookups on PG
492
+ Type::DecimalWithoutScale.new(precision: precision)
493
+ else
494
+ OID::Decimal.new(precision: precision, scale: scale)
785
495
  end
786
-
787
- vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i]
788
496
  end
789
497
 
790
- type_map[row['oid'].to_i] = vector
791
- type_map
498
+ load_additional_types(m)
792
499
  end
793
500
 
794
- def initialize_type_map(type_map)
795
- result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
796
- leaves, nodes = result.partition { |row| row['typelem'] == '0' }
501
+ def extract_limit(sql_type) # :nodoc:
502
+ case sql_type
503
+ when /^bigint/i; 8
504
+ when /^smallint/i; 2
505
+ else super
506
+ end
507
+ end
797
508
 
798
- # populate the leaf nodes
799
- leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
800
- type_map[row['oid'].to_i] = OID::NAMES[row['typname']]
509
+ # Extracts the value from a PostgreSQL column default definition.
510
+ def extract_value_from_default(oid, default) # :nodoc:
511
+ case default
512
+ # Quoted types
513
+ when /\A[\(B]?'(.*)'::/m
514
+ $1.gsub(/''/, "'")
515
+ # Boolean types
516
+ when 'true', 'false'
517
+ default
518
+ # Numeric types
519
+ when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
520
+ $1
521
+ # Object identifier types
522
+ when /\A-?\d+\z/
523
+ $1
524
+ else
525
+ # Anything else is blank, some user type, or some function
526
+ # and we can't know the value of that, so return nil.
527
+ nil
801
528
  end
529
+ end
802
530
 
803
- records_by_oid = result.group_by { |row| row['oid'] }
531
+ def extract_default_function(default_value, default) # :nodoc:
532
+ default if has_default_function?(default_value, default)
533
+ end
804
534
 
805
- arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
535
+ def has_default_function?(default_value, default) # :nodoc:
536
+ !default_value && (%r{\w+\(.*\)} === default)
537
+ end
806
538
 
807
- # populate composite types
808
- nodes.each do |row|
809
- add_oid row, records_by_oid, type_map
539
+ def load_additional_types(type_map, oids = nil) # :nodoc:
540
+ if supports_ranges?
541
+ query = <<-SQL
542
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
543
+ FROM pg_type as t
544
+ LEFT JOIN pg_range as r ON oid = rngtypid
545
+ SQL
546
+ else
547
+ query = <<-SQL
548
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
549
+ FROM pg_type as t
550
+ SQL
810
551
  end
811
552
 
812
- # populate array types
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
553
+ if oids
554
+ query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
816
555
  end
556
+
557
+ initializer = OID::TypeMapInitializer.new(type_map)
558
+ records = execute(query, 'SCHEMA')
559
+ initializer.run(records)
817
560
  end
818
561
 
819
562
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
820
563
 
564
+ def execute_and_clear(sql, name, binds)
565
+ result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
566
+ exec_cache(sql, name, binds)
567
+ ret = yield result
568
+ result.clear
569
+ ret
570
+ end
571
+
821
572
  def exec_no_cache(sql, name, binds)
822
573
  log(sql, name, binds) { @connection.async_exec(sql, []) }
823
574
  end
@@ -877,11 +628,6 @@ module ActiveRecord
877
628
  @statements[sql_key]
878
629
  end
879
630
 
880
- # The internal PostgreSQL identifier of the money data type.
881
- MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
882
- # The internal PostgreSQL identifier of the BYTEA data type.
883
- BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
884
-
885
631
  # Connects to a PostgreSQL server and sets up the adapter depending on the
886
632
  # connected server's characteristics.
887
633
  def connect
@@ -890,14 +636,14 @@ module ActiveRecord
890
636
  # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
891
637
  # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
892
638
  # should know about this but can't detect it there, so deal with it here.
893
- PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
639
+ OID::Money.precision = (postgresql_version >= 80300) ? 19 : 10
894
640
 
895
641
  configure_connection
896
642
  rescue ::PG::Error => error
897
643
  if error.message.include?("does not exist")
898
- raise ActiveRecord::NoDatabaseError.new(error.message)
644
+ raise ActiveRecord::NoDatabaseError.new(error.message, error)
899
645
  else
900
- raise error
646
+ raise
901
647
  end
902
648
  end
903
649
 
@@ -972,7 +718,7 @@ module ActiveRecord
972
718
  # Query implementation notes:
973
719
  # - format_type includes the column size constraint, e.g. varchar(50)
974
720
  # - ::regclass is a function that gives the id for a table name
975
- def column_definitions(table_name) #:nodoc:
721
+ def column_definitions(table_name) # :nodoc:
976
722
  exec_query(<<-end_sql, 'SCHEMA').rows
977
723
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
978
724
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
@@ -984,23 +730,13 @@ module ActiveRecord
984
730
  end_sql
985
731
  end
986
732
 
987
- def extract_pg_identifier_from_name(name)
988
- match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
989
-
990
- if match_data
991
- rest = name[match_data[0].length, name.length]
992
- rest = rest[1, rest.length] if rest.start_with? "."
993
- [match_data[1], (rest.length > 0 ? rest : nil)]
994
- end
995
- end
996
-
997
- def extract_table_ref_from_insert_sql(sql)
733
+ def extract_table_ref_from_insert_sql(sql) # :nodoc:
998
734
  sql[/into\s+([^\(]*).*values\s*\(/im]
999
735
  $1.strip if $1
1000
736
  end
1001
737
 
1002
- def create_table_definition(name, temporary, options, as = nil)
1003
- TableDefinition.new native_database_types, name, temporary, options, as
738
+ def create_table_definition(name, temporary, options, as = nil) # :nodoc:
739
+ PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
1004
740
  end
1005
741
  end
1006
742
  end