activerecord 4.1.0 → 4.2.0

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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +776 -1330
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/association_relation.rb +4 -0
  6. data/lib/active_record/associations/alias_tracker.rb +14 -13
  7. data/lib/active_record/associations/association.rb +2 -2
  8. data/lib/active_record/associations/association_scope.rb +83 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
  13. data/lib/active_record/associations/builder/has_many.rb +1 -1
  14. data/lib/active_record/associations/builder/has_one.rb +2 -2
  15. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  16. data/lib/active_record/associations/collection_association.rb +66 -29
  17. data/lib/active_record/associations/collection_proxy.rb +22 -26
  18. data/lib/active_record/associations/has_many_association.rb +65 -18
  19. data/lib/active_record/associations/has_many_through_association.rb +55 -27
  20. data/lib/active_record/associations/has_one_association.rb +0 -1
  21. data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
  22. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  23. data/lib/active_record/associations/join_dependency.rb +20 -12
  24. data/lib/active_record/associations/preloader/association.rb +34 -11
  25. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  26. data/lib/active_record/associations/preloader.rb +49 -59
  27. data/lib/active_record/associations/singular_association.rb +25 -4
  28. data/lib/active_record/associations/through_association.rb +23 -14
  29. data/lib/active_record/associations.rb +171 -42
  30. data/lib/active_record/attribute.rb +149 -0
  31. data/lib/active_record/attribute_assignment.rb +18 -10
  32. data/lib/active_record/attribute_decorators.rb +66 -0
  33. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  34. data/lib/active_record/attribute_methods/dirty.rb +98 -44
  35. data/lib/active_record/attribute_methods/primary_key.rb +14 -8
  36. data/lib/active_record/attribute_methods/query.rb +1 -1
  37. data/lib/active_record/attribute_methods/read.rb +22 -59
  38. data/lib/active_record/attribute_methods/serialization.rb +37 -147
  39. data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
  40. data/lib/active_record/attribute_methods/write.rb +14 -21
  41. data/lib/active_record/attribute_methods.rb +67 -94
  42. data/lib/active_record/attribute_set/builder.rb +86 -0
  43. data/lib/active_record/attribute_set.rb +77 -0
  44. data/lib/active_record/attributes.rb +139 -0
  45. data/lib/active_record/autosave_association.rb +45 -38
  46. data/lib/active_record/base.rb +10 -20
  47. data/lib/active_record/callbacks.rb +7 -7
  48. data/lib/active_record/coders/json.rb +13 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
  60. data/lib/active_record/connection_adapters/column.rb +28 -239
  61. data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
  62. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
  63. data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
  64. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  65. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  66. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
  67. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  96. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
  97. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  98. data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
  99. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  100. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
  101. data/lib/active_record/connection_handling.rb +3 -3
  102. data/lib/active_record/core.rb +143 -32
  103. data/lib/active_record/counter_cache.rb +60 -7
  104. data/lib/active_record/enum.rb +10 -11
  105. data/lib/active_record/errors.rb +49 -27
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixtures.rb +56 -70
  108. data/lib/active_record/gem_version.rb +2 -2
  109. data/lib/active_record/inheritance.rb +35 -10
  110. data/lib/active_record/integration.rb +4 -4
  111. data/lib/active_record/locking/optimistic.rb +35 -17
  112. data/lib/active_record/log_subscriber.rb +1 -1
  113. data/lib/active_record/migration/command_recorder.rb +19 -2
  114. data/lib/active_record/migration/join_table.rb +1 -1
  115. data/lib/active_record/migration.rb +52 -49
  116. data/lib/active_record/model_schema.rb +49 -57
  117. data/lib/active_record/nested_attributes.rb +7 -7
  118. data/lib/active_record/null_relation.rb +19 -5
  119. data/lib/active_record/persistence.rb +50 -31
  120. data/lib/active_record/query_cache.rb +3 -3
  121. data/lib/active_record/querying.rb +10 -7
  122. data/lib/active_record/railtie.rb +14 -11
  123. data/lib/active_record/railties/databases.rake +56 -54
  124. data/lib/active_record/readonly_attributes.rb +0 -1
  125. data/lib/active_record/reflection.rb +286 -102
  126. data/lib/active_record/relation/batches.rb +0 -1
  127. data/lib/active_record/relation/calculations.rb +39 -31
  128. data/lib/active_record/relation/delegation.rb +2 -2
  129. data/lib/active_record/relation/finder_methods.rb +80 -36
  130. data/lib/active_record/relation/merger.rb +25 -30
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
  132. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  133. data/lib/active_record/relation/predicate_builder.rb +11 -10
  134. data/lib/active_record/relation/query_methods.rb +141 -55
  135. data/lib/active_record/relation/spawn_methods.rb +3 -0
  136. data/lib/active_record/relation.rb +69 -30
  137. data/lib/active_record/result.rb +18 -7
  138. data/lib/active_record/sanitization.rb +12 -2
  139. data/lib/active_record/schema.rb +0 -1
  140. data/lib/active_record/schema_dumper.rb +58 -26
  141. data/lib/active_record/schema_migration.rb +11 -0
  142. data/lib/active_record/scoping/default.rb +8 -7
  143. data/lib/active_record/scoping/named.rb +4 -0
  144. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  145. data/lib/active_record/statement_cache.rb +95 -10
  146. data/lib/active_record/store.rb +19 -10
  147. data/lib/active_record/tasks/database_tasks.rb +73 -7
  148. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
  149. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  151. data/lib/active_record/timestamp.rb +11 -9
  152. data/lib/active_record/transactions.rb +37 -21
  153. data/lib/active_record/type/big_integer.rb +13 -0
  154. data/lib/active_record/type/binary.rb +50 -0
  155. data/lib/active_record/type/boolean.rb +30 -0
  156. data/lib/active_record/type/date.rb +46 -0
  157. data/lib/active_record/type/date_time.rb +43 -0
  158. data/lib/active_record/type/decimal.rb +40 -0
  159. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  160. data/lib/active_record/type/decorator.rb +14 -0
  161. data/lib/active_record/type/float.rb +19 -0
  162. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  163. data/lib/active_record/type/integer.rb +55 -0
  164. data/lib/active_record/type/mutable.rb +16 -0
  165. data/lib/active_record/type/numeric.rb +36 -0
  166. data/lib/active_record/type/serialized.rb +56 -0
  167. data/lib/active_record/type/string.rb +36 -0
  168. data/lib/active_record/type/text.rb +11 -0
  169. data/lib/active_record/type/time.rb +26 -0
  170. data/lib/active_record/type/time_value.rb +38 -0
  171. data/lib/active_record/type/type_map.rb +64 -0
  172. data/lib/active_record/type/unsigned_integer.rb +15 -0
  173. data/lib/active_record/type/value.rb +101 -0
  174. data/lib/active_record/type.rb +23 -0
  175. data/lib/active_record/validations/associated.rb +5 -3
  176. data/lib/active_record/validations/presence.rb +6 -4
  177. data/lib/active_record/validations/uniqueness.rb +11 -17
  178. data/lib/active_record/validations.rb +25 -19
  179. data/lib/active_record.rb +3 -0
  180. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  181. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
  182. data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +65 -10
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,16 +1,19 @@
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
13
- gem 'pg', '~> 0.11'
16
+ gem 'pg', '~> 0.15'
14
17
  require 'pg'
15
18
 
16
19
  require 'ipaddr'
@@ -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,17 @@ 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
- ADAPTER_NAME = 'PostgreSQL'
77
+ ADAPTER_NAME = 'PostgreSQL'.freeze
416
78
 
417
79
  NATIVE_DATABASE_TYPES = {
418
80
  primary_key: "serial primary key",
419
- string: { name: "character varying", limit: 255 },
81
+ bigserial: "bigserial",
82
+ string: { name: "character varying" },
420
83
  text: { name: "text" },
421
84
  integer: { name: "integer" },
422
85
  float: { name: "float" },
423
86
  decimal: { name: "decimal" },
424
87
  datetime: { name: "timestamp" },
425
- timestamp: { name: "timestamp" },
426
88
  time: { name: "time" },
427
89
  date: { name: "date" },
428
90
  daterange: { name: "daterange" },
@@ -433,6 +95,7 @@ module ActiveRecord
433
95
  int8range: { name: "int8range" },
434
96
  binary: { name: "bytea" },
435
97
  boolean: { name: "boolean" },
98
+ bigint: { name: "bigint" },
436
99
  xml: { name: "xml" },
437
100
  tsvector: { name: "tsvector" },
438
101
  hstore: { name: "hstore" },
@@ -441,30 +104,37 @@ module ActiveRecord
441
104
  macaddr: { name: "macaddr" },
442
105
  uuid: { name: "uuid" },
443
106
  json: { name: "json" },
444
- ltree: { name: "ltree" }
107
+ jsonb: { name: "jsonb" },
108
+ ltree: { name: "ltree" },
109
+ citext: { name: "citext" },
110
+ point: { name: "point" },
111
+ bit: { name: "bit" },
112
+ bit_varying: { name: "bit varying" },
113
+ money: { name: "money" },
445
114
  }
446
115
 
447
- include Quoting
448
- include ReferentialIntegrity
449
- include SchemaStatements
450
- include DatabaseStatements
116
+ OID = PostgreSQL::OID #:nodoc:
117
+
118
+ include PostgreSQL::Quoting
119
+ include PostgreSQL::ReferentialIntegrity
120
+ include PostgreSQL::SchemaStatements
121
+ include PostgreSQL::DatabaseStatements
451
122
  include Savepoints
452
123
 
453
- # Returns 'PostgreSQL' as adapter name for identification purposes.
454
- def adapter_name
455
- ADAPTER_NAME
124
+ def schema_creation # :nodoc:
125
+ PostgreSQL::SchemaCreation.new self
456
126
  end
457
127
 
458
- # Adds `:array` option to the default set provided by the
128
+ # Adds +:array+ option to the default set provided by the
459
129
  # AbstractAdapter
460
- def prepare_column_options(column, types)
130
+ def prepare_column_options(column, types) # :nodoc:
461
131
  spec = super
462
132
  spec[:array] = 'true' if column.respond_to?(:array) && column.array
463
133
  spec[:default] = "\"#{column.default_function}\"" if column.default_function
464
134
  spec
465
135
  end
466
136
 
467
- # Adds `:array` as a valid migration key
137
+ # Adds +:array+ as a valid migration key
468
138
  def migration_keys
469
139
  super + [:array]
470
140
  end
@@ -487,6 +157,14 @@ module ActiveRecord
487
157
  true
488
158
  end
489
159
 
160
+ def supports_foreign_keys?
161
+ true
162
+ end
163
+
164
+ def supports_views?
165
+ true
166
+ end
167
+
490
168
  def index_algorithms
491
169
  { concurrently: 'CONCURRENTLY' }
492
170
  end
@@ -544,19 +222,15 @@ module ActiveRecord
544
222
  end
545
223
  end
546
224
 
547
- class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
548
- include Arel::Visitors::BindVisitor
549
- end
550
-
551
225
  # Initializes and connects a PostgreSQL adapter.
552
226
  def initialize(connection, logger, connection_parameters, config)
553
227
  super(connection, logger)
554
228
 
229
+ @visitor = Arel::Visitors::PostgreSQL.new self
555
230
  if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
556
231
  @prepared_statements = true
557
- @visitor = Arel::Visitors::PostgreSQL.new self
558
232
  else
559
- @visitor = unprepared_visitor
233
+ @prepared_statements = false
560
234
  end
561
235
 
562
236
  @connection_parameters, @config = connection_parameters, config
@@ -573,7 +247,7 @@ module ActiveRecord
573
247
  raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
574
248
  end
575
249
 
576
- @type_map = OID::TypeMap.new
250
+ @type_map = Type::HashLookupTypeMap.new
577
251
  initialize_type_map(type_map)
578
252
  @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
579
253
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
@@ -584,6 +258,10 @@ module ActiveRecord
584
258
  @statements.clear
585
259
  end
586
260
 
261
+ def truncate(table_name, name = nil)
262
+ exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
263
+ end
264
+
587
265
  # Is this connection alive and ready for queries?
588
266
  def active?
589
267
  @connection.query 'SELECT 1'
@@ -592,10 +270,6 @@ module ActiveRecord
592
270
  false
593
271
  end
594
272
 
595
- def active_threadsafe?
596
- @connection.connect_poll != PG::PGRES_POLLING_FAILED
597
- end
598
-
599
273
  # Close then reopen the connection.
600
274
  def reconnect!
601
275
  super
@@ -605,7 +279,12 @@ module ActiveRecord
605
279
 
606
280
  def reset!
607
281
  clear_cache!
608
- super
282
+ reset_transaction
283
+ unless @connection.transaction_status == ::PG::PQTRANS_IDLE
284
+ @connection.query 'ROLLBACK'
285
+ end
286
+ @connection.query 'DISCARD ALL'
287
+ configure_connection
609
288
  end
610
289
 
611
290
  # Disconnects from the database if already connected. Otherwise, this
@@ -637,10 +316,6 @@ module ActiveRecord
637
316
  self.client_min_messages = old
638
317
  end
639
318
 
640
- def supports_insert_with_returning?
641
- true
642
- end
643
-
644
319
  def supports_ddl_transactions?
645
320
  true
646
321
  end
@@ -659,6 +334,10 @@ module ActiveRecord
659
334
  postgresql_version >= 90200
660
335
  end
661
336
 
337
+ def supports_materialized_views?
338
+ postgresql_version >= 90300
339
+ end
340
+
662
341
  def enable_extension(name)
663
342
  exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
664
343
  reload_type_map
@@ -675,14 +354,13 @@ module ActiveRecord
675
354
  if supports_extensions?
676
355
  res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
677
356
  'SCHEMA'
678
- res.column_types['enabled'].type_cast res.rows.first.first
357
+ res.cast_values.first
679
358
  end
680
359
  end
681
360
 
682
361
  def extensions
683
362
  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 }
363
+ exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
686
364
  else
687
365
  super
688
366
  end
@@ -699,25 +377,6 @@ module ActiveRecord
699
377
  exec_query "SET SESSION AUTHORIZATION #{user}"
700
378
  end
701
379
 
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
380
  def use_insert_returning?
722
381
  @use_insert_returning
723
382
  end
@@ -727,9 +386,24 @@ module ActiveRecord
727
386
  end
728
387
 
729
388
  def update_table_definition(table_name, base) #:nodoc:
730
- Table.new(table_name, base)
389
+ PostgreSQL::Table.new(table_name, base)
390
+ end
391
+
392
+ def lookup_cast_type(sql_type) # :nodoc:
393
+ oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first['oid'].to_i
394
+ super(oid)
395
+ end
396
+
397
+ def column_name_for_operation(operation, node) # :nodoc:
398
+ OPERATION_ALIASES.fetch(operation) { operation.downcase }
731
399
  end
732
400
 
401
+ OPERATION_ALIASES = { # :nodoc:
402
+ "maximum" => "max",
403
+ "minimum" => "min",
404
+ "average" => "avg",
405
+ }
406
+
733
407
  protected
734
408
 
735
409
  # Returns the version of the connected PostgreSQL server.
@@ -756,63 +430,166 @@ module ActiveRecord
756
430
 
757
431
  private
758
432
 
759
- def type_map
760
- @type_map
761
- end
433
+ def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc:
434
+ if !type_map.key?(oid)
435
+ load_additional_types(type_map, [oid])
436
+ end
762
437
 
763
- def reload_type_map
764
- type_map.clear
765
- initialize_type_map(type_map)
438
+ type_map.fetch(oid, fmod, sql_type) {
439
+ warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
440
+ Type::Value.new.tap do |cast_type|
441
+ type_map.register_type(oid, cast_type)
442
+ end
443
+ }
766
444
  end
767
445
 
768
- def add_oid(row, records_by_oid, type_map)
769
- return type_map if type_map.key? row['type_elem'].to_i
446
+ def initialize_type_map(m) # :nodoc:
447
+ register_class_with_limit m, 'int2', OID::Integer
448
+ m.alias_type 'int4', 'int2'
449
+ m.alias_type 'int8', 'int2'
450
+ m.alias_type 'oid', 'int2'
451
+ m.register_type 'float4', OID::Float.new
452
+ m.alias_type 'float8', 'float4'
453
+ m.register_type 'text', Type::Text.new
454
+ register_class_with_limit m, 'varchar', Type::String
455
+ m.alias_type 'char', 'varchar'
456
+ m.alias_type 'name', 'varchar'
457
+ m.alias_type 'bpchar', 'varchar'
458
+ m.register_type 'bool', Type::Boolean.new
459
+ register_class_with_limit m, 'bit', OID::Bit
460
+ register_class_with_limit m, 'varbit', OID::BitVarying
461
+ m.alias_type 'timestamptz', 'timestamp'
462
+ m.register_type 'date', OID::Date.new
463
+ m.register_type 'time', OID::Time.new
464
+
465
+ m.register_type 'money', OID::Money.new
466
+ m.register_type 'bytea', OID::Bytea.new
467
+ m.register_type 'point', OID::Point.new
468
+ m.register_type 'hstore', OID::Hstore.new
469
+ m.register_type 'json', OID::Json.new
470
+ m.register_type 'jsonb', OID::Jsonb.new
471
+ m.register_type 'cidr', OID::Cidr.new
472
+ m.register_type 'inet', OID::Inet.new
473
+ m.register_type 'uuid', OID::Uuid.new
474
+ m.register_type 'xml', OID::Xml.new
475
+ m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
476
+ m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
477
+ m.register_type 'citext', OID::SpecializedString.new(:citext)
478
+ m.register_type 'ltree', OID::SpecializedString.new(:ltree)
479
+
480
+ # FIXME: why are we keeping these types as strings?
481
+ m.alias_type 'interval', 'varchar'
482
+ m.alias_type 'path', 'varchar'
483
+ m.alias_type 'line', 'varchar'
484
+ m.alias_type 'polygon', 'varchar'
485
+ m.alias_type 'circle', 'varchar'
486
+ m.alias_type 'lseg', 'varchar'
487
+ m.alias_type 'box', 'varchar'
488
+
489
+ m.register_type 'timestamp' do |_, _, sql_type|
490
+ precision = extract_precision(sql_type)
491
+ OID::DateTime.new(precision: precision)
492
+ end
770
493
 
771
- if OID.registered_type? row['typname']
772
- # this composite type is explicitly registered
773
- vector = OID::NAMES[row['typname']]
774
- else
775
- # use the default for composite types
776
- unless type_map.key? row['typelem'].to_i
777
- add_oid records_by_oid[row['typelem']], records_by_oid, type_map
494
+ m.register_type 'numeric' do |_, fmod, sql_type|
495
+ precision = extract_precision(sql_type)
496
+ scale = extract_scale(sql_type)
497
+
498
+ # The type for the numeric depends on the width of the field,
499
+ # so we'll do something special here.
500
+ #
501
+ # When dealing with decimal columns:
502
+ #
503
+ # places after decimal = fmod - 4 & 0xffff
504
+ # places before decimal = (fmod - 4) >> 16 & 0xffff
505
+ if fmod && (fmod - 4 & 0xffff).zero?
506
+ # FIXME: Remove this class, and the second argument to
507
+ # lookups on PG
508
+ Type::DecimalWithoutScale.new(precision: precision)
509
+ else
510
+ OID::Decimal.new(precision: precision, scale: scale)
778
511
  end
779
-
780
- vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i]
781
512
  end
782
513
 
783
- type_map[row['oid'].to_i] = vector
784
- type_map
514
+ load_additional_types(m)
785
515
  end
786
516
 
787
- def initialize_type_map(type_map)
788
- result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
789
- leaves, nodes = result.partition { |row| row['typelem'] == '0' }
517
+ def extract_limit(sql_type) # :nodoc:
518
+ case sql_type
519
+ when /^bigint/i, /^int8/i
520
+ 8
521
+ when /^smallint/i
522
+ 2
523
+ else
524
+ super
525
+ end
526
+ end
790
527
 
791
- # populate the leaf nodes
792
- leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
793
- type_map[row['oid'].to_i] = OID::NAMES[row['typname']]
528
+ # Extracts the value from a PostgreSQL column default definition.
529
+ def extract_value_from_default(oid, default) # :nodoc:
530
+ case default
531
+ # Quoted types
532
+ when /\A[\(B]?'(.*)'::/m
533
+ $1.gsub(/''/, "'")
534
+ # Boolean types
535
+ when 'true', 'false'
536
+ default
537
+ # Numeric types
538
+ when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
539
+ $1
540
+ # Object identifier types
541
+ when /\A-?\d+\z/
542
+ $1
543
+ else
544
+ # Anything else is blank, some user type, or some function
545
+ # and we can't know the value of that, so return nil.
546
+ nil
794
547
  end
548
+ end
795
549
 
796
- records_by_oid = result.group_by { |row| row['oid'] }
550
+ def extract_default_function(default_value, default) # :nodoc:
551
+ default if has_default_function?(default_value, default)
552
+ end
797
553
 
798
- arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
554
+ def has_default_function?(default_value, default) # :nodoc:
555
+ !default_value && (%r{\w+\(.*\)} === default)
556
+ end
799
557
 
800
- # populate composite types
801
- nodes.each do |row|
802
- add_oid row, records_by_oid, type_map
558
+ def load_additional_types(type_map, oids = nil) # :nodoc:
559
+ if supports_ranges?
560
+ query = <<-SQL
561
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
562
+ FROM pg_type as t
563
+ LEFT JOIN pg_range as r ON oid = rngtypid
564
+ SQL
565
+ else
566
+ query = <<-SQL
567
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
568
+ FROM pg_type as t
569
+ SQL
803
570
  end
804
571
 
805
- # populate array types
806
- arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
807
- array = OID::Array.new type_map[row['typelem'].to_i]
808
- type_map[row['oid'].to_i] = array
572
+ if oids
573
+ query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
809
574
  end
575
+
576
+ initializer = OID::TypeMapInitializer.new(type_map)
577
+ records = execute(query, 'SCHEMA')
578
+ initializer.run(records)
810
579
  end
811
580
 
812
581
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
813
582
 
583
+ def execute_and_clear(sql, name, binds)
584
+ result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
585
+ exec_cache(sql, name, binds)
586
+ ret = yield result
587
+ result.clear
588
+ ret
589
+ end
590
+
814
591
  def exec_no_cache(sql, name, binds)
815
- log(sql, name, binds) { @connection.async_exec(sql) }
592
+ log(sql, name, binds) { @connection.async_exec(sql, []) }
816
593
  end
817
594
 
818
595
  def exec_cache(sql, name, binds)
@@ -822,9 +599,7 @@ module ActiveRecord
822
599
  }
823
600
 
824
601
  log(sql, name, type_casted_binds, stmt_key) do
825
- @connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val })
826
- @connection.block
827
- @connection.get_last_result
602
+ @connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val })
828
603
  end
829
604
  rescue ActiveRecord::StatementInvalid => e
830
605
  pgerror = e.original_exception
@@ -870,11 +645,6 @@ module ActiveRecord
870
645
  @statements[sql_key]
871
646
  end
872
647
 
873
- # The internal PostgreSQL identifier of the money data type.
874
- MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
875
- # The internal PostgreSQL identifier of the BYTEA data type.
876
- BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
877
-
878
648
  # Connects to a PostgreSQL server and sets up the adapter depending on the
879
649
  # connected server's characteristics.
880
650
  def connect
@@ -883,14 +653,14 @@ module ActiveRecord
883
653
  # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
884
654
  # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
885
655
  # should know about this but can't detect it there, so deal with it here.
886
- PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
656
+ OID::Money.precision = (postgresql_version >= 80300) ? 19 : 10
887
657
 
888
658
  configure_connection
889
659
  rescue ::PG::Error => error
890
660
  if error.message.include?("does not exist")
891
- raise ActiveRecord::NoDatabaseError.new(error.message)
661
+ raise ActiveRecord::NoDatabaseError.new(error.message, error)
892
662
  else
893
- raise error
663
+ raise
894
664
  end
895
665
  end
896
666
 
@@ -921,9 +691,9 @@ module ActiveRecord
921
691
  variables.map do |k, v|
922
692
  if v == ':default' || v == :default
923
693
  # Sets the value to the global or compile default
924
- execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA')
694
+ execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA')
925
695
  elsif !v.nil?
926
- execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
696
+ execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA')
927
697
  end
928
698
  end
929
699
  end
@@ -941,12 +711,6 @@ module ActiveRecord
941
711
  exec_query("SELECT currval('#{sequence_name}')", 'SQL')
942
712
  end
943
713
 
944
- # Executes a SELECT query and returns the results, performing any data type
945
- # conversions that are required to be performed here instead of in PostgreSQLColumn.
946
- def select(sql, name = nil, binds = [])
947
- exec_query(sql, name, binds)
948
- end
949
-
950
714
  # Returns the list of a table's column names, data types, and default values.
951
715
  #
952
716
  # The underlying query is roughly:
@@ -965,7 +729,7 @@ module ActiveRecord
965
729
  # Query implementation notes:
966
730
  # - format_type includes the column size constraint, e.g. varchar(50)
967
731
  # - ::regclass is a function that gives the id for a table name
968
- def column_definitions(table_name) #:nodoc:
732
+ def column_definitions(table_name) # :nodoc:
969
733
  exec_query(<<-end_sql, 'SCHEMA').rows
970
734
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
971
735
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
@@ -977,23 +741,13 @@ module ActiveRecord
977
741
  end_sql
978
742
  end
979
743
 
980
- def extract_pg_identifier_from_name(name)
981
- match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
982
-
983
- if match_data
984
- rest = name[match_data[0].length, name.length]
985
- rest = rest[1, rest.length] if rest.start_with? "."
986
- [match_data[1], (rest.length > 0 ? rest : nil)]
987
- end
988
- end
989
-
990
- def extract_table_ref_from_insert_sql(sql)
744
+ def extract_table_ref_from_insert_sql(sql) # :nodoc:
991
745
  sql[/into\s+([^\(]*).*values\s*\(/im]
992
746
  $1.strip if $1
993
747
  end
994
748
 
995
- def create_table_definition(name, temporary, options, as = nil)
996
- TableDefinition.new native_database_types, name, temporary, options, as
749
+ def create_table_definition(name, temporary, options, as = nil) # :nodoc:
750
+ PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
997
751
  end
998
752
  end
999
753
  end