activerecord 5.2.6 → 6.0.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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +609 -622
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +52 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/association.rb +14 -18
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  13. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  15. data/lib/active_record/associations/builder/has_many.rb +2 -0
  16. data/lib/active_record/associations/builder/has_one.rb +35 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  18. data/lib/active_record/associations/collection_association.rb +6 -21
  19. data/lib/active_record/associations/collection_proxy.rb +12 -15
  20. data/lib/active_record/associations/foreign_association.rb +7 -0
  21. data/lib/active_record/associations/has_many_association.rb +2 -10
  22. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  23. data/lib/active_record/associations/has_one_association.rb +28 -30
  24. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  25. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  26. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/preloader/association.rb +38 -36
  29. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  30. data/lib/active_record/associations/preloader.rb +40 -32
  31. data/lib/active_record/associations/singular_association.rb +2 -16
  32. data/lib/active_record/associations.rb +19 -14
  33. data/lib/active_record/attribute_assignment.rb +7 -10
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  35. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  36. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  37. data/lib/active_record/attribute_methods/query.rb +2 -3
  38. data/lib/active_record/attribute_methods/read.rb +15 -53
  39. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  41. data/lib/active_record/attribute_methods/write.rb +17 -24
  42. data/lib/active_record/attribute_methods.rb +28 -100
  43. data/lib/active_record/attributes.rb +13 -0
  44. data/lib/active_record/autosave_association.rb +5 -9
  45. data/lib/active_record/base.rb +2 -3
  46. data/lib/active_record/callbacks.rb +5 -19
  47. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +94 -16
  48. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  49. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  50. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
  51. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  59. data/lib/active_record/connection_adapters/column.rb +17 -13
  60. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  61. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  64. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  65. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  68. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  72. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  77. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  80. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  81. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  84. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  85. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  87. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  88. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  89. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  90. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  91. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
  93. data/lib/active_record/connection_handling.rb +149 -27
  94. data/lib/active_record/core.rb +100 -60
  95. data/lib/active_record/counter_cache.rb +4 -29
  96. data/lib/active_record/database_configurations/database_config.rb +37 -0
  97. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  98. data/lib/active_record/database_configurations/url_config.rb +79 -0
  99. data/lib/active_record/database_configurations.rb +233 -0
  100. data/lib/active_record/dynamic_matchers.rb +1 -1
  101. data/lib/active_record/enum.rb +37 -7
  102. data/lib/active_record/errors.rb +15 -7
  103. data/lib/active_record/explain.rb +1 -1
  104. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  105. data/lib/active_record/fixture_set/render_context.rb +17 -0
  106. data/lib/active_record/fixture_set/table_row.rb +153 -0
  107. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  108. data/lib/active_record/fixtures.rb +145 -472
  109. data/lib/active_record/gem_version.rb +3 -3
  110. data/lib/active_record/inheritance.rb +13 -3
  111. data/lib/active_record/insert_all.rb +179 -0
  112. data/lib/active_record/integration.rb +68 -16
  113. data/lib/active_record/internal_metadata.rb +10 -2
  114. data/lib/active_record/locking/optimistic.rb +5 -6
  115. data/lib/active_record/locking/pessimistic.rb +3 -3
  116. data/lib/active_record/log_subscriber.rb +7 -26
  117. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  118. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  119. data/lib/active_record/middleware/database_selector.rb +75 -0
  120. data/lib/active_record/migration/command_recorder.rb +50 -6
  121. data/lib/active_record/migration/compatibility.rb +76 -49
  122. data/lib/active_record/migration.rb +100 -81
  123. data/lib/active_record/model_schema.rb +30 -9
  124. data/lib/active_record/nested_attributes.rb +2 -2
  125. data/lib/active_record/no_touching.rb +7 -0
  126. data/lib/active_record/persistence.rb +228 -24
  127. data/lib/active_record/query_cache.rb +11 -4
  128. data/lib/active_record/querying.rb +32 -20
  129. data/lib/active_record/railtie.rb +80 -43
  130. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  131. data/lib/active_record/railties/controller_runtime.rb +30 -35
  132. data/lib/active_record/railties/databases.rake +196 -46
  133. data/lib/active_record/reflection.rb +32 -30
  134. data/lib/active_record/relation/batches.rb +13 -10
  135. data/lib/active_record/relation/calculations.rb +53 -47
  136. data/lib/active_record/relation/delegation.rb +26 -43
  137. data/lib/active_record/relation/finder_methods.rb +13 -26
  138. data/lib/active_record/relation/merger.rb +11 -20
  139. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  140. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  141. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  142. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  143. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  145. data/lib/active_record/relation/predicate_builder.rb +4 -6
  146. data/lib/active_record/relation/query_attribute.rb +13 -8
  147. data/lib/active_record/relation/query_methods.rb +189 -63
  148. data/lib/active_record/relation/spawn_methods.rb +1 -1
  149. data/lib/active_record/relation/where_clause.rb +14 -10
  150. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  151. data/lib/active_record/relation.rb +310 -80
  152. data/lib/active_record/result.rb +30 -11
  153. data/lib/active_record/sanitization.rb +32 -40
  154. data/lib/active_record/schema.rb +2 -11
  155. data/lib/active_record/schema_dumper.rb +22 -7
  156. data/lib/active_record/schema_migration.rb +5 -1
  157. data/lib/active_record/scoping/default.rb +4 -5
  158. data/lib/active_record/scoping/named.rb +19 -15
  159. data/lib/active_record/scoping.rb +8 -8
  160. data/lib/active_record/statement_cache.rb +30 -3
  161. data/lib/active_record/store.rb +87 -8
  162. data/lib/active_record/table_metadata.rb +10 -17
  163. data/lib/active_record/tasks/database_tasks.rb +194 -25
  164. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  165. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  166. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  167. data/lib/active_record/test_databases.rb +23 -0
  168. data/lib/active_record/test_fixtures.rb +224 -0
  169. data/lib/active_record/timestamp.rb +39 -25
  170. data/lib/active_record/touch_later.rb +4 -2
  171. data/lib/active_record/transactions.rb +57 -66
  172. data/lib/active_record/translation.rb +1 -1
  173. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type_caster/connection.rb +15 -14
  176. data/lib/active_record/type_caster/map.rb +1 -4
  177. data/lib/active_record/validations/uniqueness.rb +15 -27
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record.rb +9 -2
  180. data/lib/arel/alias_predication.rb +9 -0
  181. data/lib/arel/attributes/attribute.rb +37 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/collectors/bind.rb +24 -0
  184. data/lib/arel/collectors/composite.rb +31 -0
  185. data/lib/arel/collectors/plain_string.rb +20 -0
  186. data/lib/arel/collectors/sql_string.rb +20 -0
  187. data/lib/arel/collectors/substitute_binds.rb +28 -0
  188. data/lib/arel/crud.rb +42 -0
  189. data/lib/arel/delete_manager.rb +18 -0
  190. data/lib/arel/errors.rb +9 -0
  191. data/lib/arel/expressions.rb +29 -0
  192. data/lib/arel/factory_methods.rb +49 -0
  193. data/lib/arel/insert_manager.rb +49 -0
  194. data/lib/arel/math.rb +45 -0
  195. data/lib/arel/nodes/and.rb +32 -0
  196. data/lib/arel/nodes/ascending.rb +23 -0
  197. data/lib/arel/nodes/binary.rb +52 -0
  198. data/lib/arel/nodes/bind_param.rb +36 -0
  199. data/lib/arel/nodes/case.rb +55 -0
  200. data/lib/arel/nodes/casted.rb +50 -0
  201. data/lib/arel/nodes/comment.rb +29 -0
  202. data/lib/arel/nodes/count.rb +12 -0
  203. data/lib/arel/nodes/delete_statement.rb +45 -0
  204. data/lib/arel/nodes/descending.rb +23 -0
  205. data/lib/arel/nodes/equality.rb +18 -0
  206. data/lib/arel/nodes/extract.rb +24 -0
  207. data/lib/arel/nodes/false.rb +16 -0
  208. data/lib/arel/nodes/full_outer_join.rb +8 -0
  209. data/lib/arel/nodes/function.rb +44 -0
  210. data/lib/arel/nodes/grouping.rb +8 -0
  211. data/lib/arel/nodes/in.rb +8 -0
  212. data/lib/arel/nodes/infix_operation.rb +80 -0
  213. data/lib/arel/nodes/inner_join.rb +8 -0
  214. data/lib/arel/nodes/insert_statement.rb +37 -0
  215. data/lib/arel/nodes/join_source.rb +20 -0
  216. data/lib/arel/nodes/matches.rb +18 -0
  217. data/lib/arel/nodes/named_function.rb +23 -0
  218. data/lib/arel/nodes/node.rb +50 -0
  219. data/lib/arel/nodes/node_expression.rb +13 -0
  220. data/lib/arel/nodes/outer_join.rb +8 -0
  221. data/lib/arel/nodes/over.rb +15 -0
  222. data/lib/arel/nodes/regexp.rb +16 -0
  223. data/lib/arel/nodes/right_outer_join.rb +8 -0
  224. data/lib/arel/nodes/select_core.rb +67 -0
  225. data/lib/arel/nodes/select_statement.rb +41 -0
  226. data/lib/arel/nodes/sql_literal.rb +16 -0
  227. data/lib/arel/nodes/string_join.rb +11 -0
  228. data/lib/arel/nodes/table_alias.rb +27 -0
  229. data/lib/arel/nodes/terminal.rb +16 -0
  230. data/lib/arel/nodes/true.rb +16 -0
  231. data/lib/arel/nodes/unary.rb +45 -0
  232. data/lib/arel/nodes/unary_operation.rb +20 -0
  233. data/lib/arel/nodes/unqualified_column.rb +22 -0
  234. data/lib/arel/nodes/update_statement.rb +41 -0
  235. data/lib/arel/nodes/values_list.rb +9 -0
  236. data/lib/arel/nodes/window.rb +126 -0
  237. data/lib/arel/nodes/with.rb +11 -0
  238. data/lib/arel/nodes.rb +68 -0
  239. data/lib/arel/order_predications.rb +13 -0
  240. data/lib/arel/predications.rb +257 -0
  241. data/lib/arel/select_manager.rb +271 -0
  242. data/lib/arel/table.rb +110 -0
  243. data/lib/arel/tree_manager.rb +72 -0
  244. data/lib/arel/update_manager.rb +34 -0
  245. data/lib/arel/visitors/depth_first.rb +204 -0
  246. data/lib/arel/visitors/dot.rb +297 -0
  247. data/lib/arel/visitors/ibm_db.rb +34 -0
  248. data/lib/arel/visitors/informix.rb +62 -0
  249. data/lib/arel/visitors/mssql.rb +157 -0
  250. data/lib/arel/visitors/mysql.rb +83 -0
  251. data/lib/arel/visitors/oracle.rb +159 -0
  252. data/lib/arel/visitors/oracle12.rb +66 -0
  253. data/lib/arel/visitors/postgresql.rb +110 -0
  254. data/lib/arel/visitors/sqlite.rb +39 -0
  255. data/lib/arel/visitors/to_sql.rb +889 -0
  256. data/lib/arel/visitors/visitor.rb +46 -0
  257. data/lib/arel/visitors/where_sql.rb +23 -0
  258. data/lib/arel/visitors.rb +20 -0
  259. data/lib/arel/window_predications.rb +9 -0
  260. data/lib/arel.rb +51 -0
  261. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  262. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  263. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  264. data/lib/rails/generators/active_record/migration.rb +14 -1
  265. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  266. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  267. metadata +108 -26
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -5,7 +5,7 @@ gem "pg", ">= 0.18", "< 2.0"
5
5
  require "pg"
6
6
 
7
7
  # Use async_exec instead of exec_params on pg versions before 1.1
8
- class ::PG::Connection
8
+ class ::PG::Connection # :nodoc:
9
9
  unless self.public_method_defined?(:async_exec_params)
10
10
  remove_method :exec_params
11
11
  alias exec_params async_exec
@@ -43,9 +43,14 @@ module ActiveRecord
43
43
  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
44
44
  conn_params.slice!(*valid_conn_param_keys)
45
45
 
46
- # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
47
- # so just pass a nil connection object for the time being.
48
- ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
46
+ conn = PG.connect(conn_params)
47
+ ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
48
+ rescue ::PG::Error => error
49
+ if error.message.include?(conn_params[:dbname])
50
+ raise ActiveRecord::NoDatabaseError
51
+ else
52
+ raise
53
+ end
49
54
  end
50
55
  end
51
56
 
@@ -78,7 +83,20 @@ module ActiveRecord
78
83
  # In addition, default connection parameters of libpq can be set per environment variables.
79
84
  # See https://www.postgresql.org/docs/current/static/libpq-envars.html .
80
85
  class PostgreSQLAdapter < AbstractAdapter
81
- ADAPTER_NAME = "PostgreSQL".freeze
86
+ ADAPTER_NAME = "PostgreSQL"
87
+
88
+ ##
89
+ # :singleton-method:
90
+ # PostgreSQL allows the creation of "unlogged" tables, which do not record
91
+ # data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
92
+ # but significantly increases the risk of data loss if the database
93
+ # crashes. As a result, this should not be used in production
94
+ # environments. If you would like all created tables to be unlogged in
95
+ # the test environment you can add the following line to your test.rb
96
+ # file:
97
+ #
98
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
99
+ class_attribute :create_unlogged_tables, default: false
82
100
 
83
101
  NATIVE_DATABASE_TYPES = {
84
102
  primary_key: "bigserial primary key",
@@ -167,7 +185,7 @@ module ActiveRecord
167
185
  end
168
186
 
169
187
  def supports_json?
170
- postgresql_version >= 90200
188
+ true
171
189
  end
172
190
 
173
191
  def supports_comments?
@@ -178,6 +196,17 @@ module ActiveRecord
178
196
  true
179
197
  end
180
198
 
199
+ def supports_insert_returning?
200
+ true
201
+ end
202
+
203
+ def supports_insert_on_conflict?
204
+ database_version >= 90500
205
+ end
206
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
207
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
208
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
209
+
181
210
  def index_algorithms
182
211
  { concurrently: "CONCURRENTLY" }
183
212
  end
@@ -220,15 +249,8 @@ module ActiveRecord
220
249
  @local_tz = nil
221
250
  @max_identifier_length = nil
222
251
 
223
- connect
252
+ configure_connection
224
253
  add_pg_encoders
225
- @statements = StatementPool.new @connection,
226
- self.class.type_cast_config_to_integer(config[:statement_limit])
227
-
228
- if postgresql_version < 90100
229
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
230
- end
231
-
232
254
  add_pg_decoders
233
255
 
234
256
  @type_map = Type::HashLookupTypeMap.new
@@ -237,15 +259,10 @@ module ActiveRecord
237
259
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
238
260
  end
239
261
 
240
- # Clears the prepared statements cache.
241
- def clear_cache!
242
- @lock.synchronize do
243
- @statements.clear
244
- end
245
- end
246
-
247
- def truncate(table_name, name = nil)
248
- exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
262
+ def self.database_exists?(config)
263
+ !!ActiveRecord::Base.postgresql_connection(config)
264
+ rescue ActiveRecord::NoDatabaseError
265
+ false
249
266
  end
250
267
 
251
268
  # Is this connection alive and ready for queries?
@@ -264,6 +281,8 @@ module ActiveRecord
264
281
  super
265
282
  @connection.reset
266
283
  configure_connection
284
+ rescue PG::ConnectionBad
285
+ connect
267
286
  end
268
287
  end
269
288
 
@@ -289,6 +308,7 @@ module ActiveRecord
289
308
  end
290
309
 
291
310
  def discard! # :nodoc:
311
+ super
292
312
  @connection.socket_io.reopen(IO::NULL) rescue nil
293
313
  @connection = nil
294
314
  end
@@ -318,20 +338,31 @@ module ActiveRecord
318
338
  end
319
339
 
320
340
  def supports_ranges?
321
- # Range datatypes weren't introduced until PostgreSQL 9.2
322
- postgresql_version >= 90200
341
+ true
323
342
  end
343
+ deprecate :supports_ranges?
324
344
 
325
345
  def supports_materialized_views?
326
- postgresql_version >= 90300
346
+ true
327
347
  end
328
348
 
329
349
  def supports_foreign_tables?
330
- postgresql_version >= 90300
350
+ true
331
351
  end
332
352
 
333
353
  def supports_pgcrypto_uuid?
334
- postgresql_version >= 90400
354
+ database_version >= 90400
355
+ end
356
+
357
+ def supports_optimizer_hints?
358
+ unless defined?(@has_pg_hint_plan)
359
+ @has_pg_hint_plan = extension_available?("pg_hint_plan")
360
+ end
361
+ @has_pg_hint_plan
362
+ end
363
+
364
+ def supports_lazy_transactions?
365
+ true
335
366
  end
336
367
 
337
368
  def get_advisory_lock(lock_id) # :nodoc:
@@ -360,9 +391,12 @@ module ActiveRecord
360
391
  }
361
392
  end
362
393
 
394
+ def extension_available?(name)
395
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
396
+ end
397
+
363
398
  def extension_enabled?(name)
364
- res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
365
- res.cast_values.first
399
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
366
400
  end
367
401
 
368
402
  def extensions
@@ -373,8 +407,6 @@ module ActiveRecord
373
407
  def max_identifier_length
374
408
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
375
409
  end
376
- alias table_alias_length max_identifier_length
377
- alias index_name_length max_identifier_length
378
410
 
379
411
  # Set the authorized user for this session
380
412
  def session_auth=(user)
@@ -397,15 +429,37 @@ module ActiveRecord
397
429
  }
398
430
 
399
431
  # Returns the version of the connected PostgreSQL server.
400
- def postgresql_version
432
+ def get_database_version # :nodoc:
401
433
  @connection.server_version
402
434
  end
435
+ alias :postgresql_version :database_version
403
436
 
404
437
  def default_index_type?(index) # :nodoc:
405
438
  index.using == :btree || super
406
439
  end
407
440
 
441
+ def build_insert_sql(insert) # :nodoc:
442
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
443
+
444
+ if insert.skip_duplicates?
445
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
446
+ elsif insert.update_duplicates?
447
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
448
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
449
+ end
450
+
451
+ sql << " RETURNING #{insert.returning}" if insert.returning
452
+ sql
453
+ end
454
+
455
+ def check_version # :nodoc:
456
+ if database_version < 90300
457
+ raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
458
+ end
459
+ end
460
+
408
461
  private
462
+
409
463
  # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
410
464
  VALUE_LIMIT_VIOLATION = "22001"
411
465
  NUMERIC_VALUE_OUT_OF_RANGE = "22003"
@@ -417,34 +471,34 @@ module ActiveRecord
417
471
  LOCK_NOT_AVAILABLE = "55P03"
418
472
  QUERY_CANCELED = "57014"
419
473
 
420
- def translate_exception(exception, message)
474
+ def translate_exception(exception, message:, sql:, binds:)
421
475
  return exception unless exception.respond_to?(:result)
422
476
 
423
477
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
424
478
  when UNIQUE_VIOLATION
425
- RecordNotUnique.new(message)
479
+ RecordNotUnique.new(message, sql: sql, binds: binds)
426
480
  when FOREIGN_KEY_VIOLATION
427
- InvalidForeignKey.new(message)
481
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
428
482
  when VALUE_LIMIT_VIOLATION
429
- ValueTooLong.new(message)
483
+ ValueTooLong.new(message, sql: sql, binds: binds)
430
484
  when NUMERIC_VALUE_OUT_OF_RANGE
431
- RangeError.new(message)
485
+ RangeError.new(message, sql: sql, binds: binds)
432
486
  when NOT_NULL_VIOLATION
433
- NotNullViolation.new(message)
487
+ NotNullViolation.new(message, sql: sql, binds: binds)
434
488
  when SERIALIZATION_FAILURE
435
- SerializationFailure.new(message)
489
+ SerializationFailure.new(message, sql: sql, binds: binds)
436
490
  when DEADLOCK_DETECTED
437
- Deadlocked.new(message)
491
+ Deadlocked.new(message, sql: sql, binds: binds)
438
492
  when LOCK_NOT_AVAILABLE
439
- LockWaitTimeout.new(message)
493
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
440
494
  when QUERY_CANCELED
441
- QueryCanceled.new(message)
495
+ QueryCanceled.new(message, sql: sql, binds: binds)
442
496
  else
443
497
  super
444
498
  end
445
499
  end
446
500
 
447
- def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
501
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
448
502
  if !type_map.key?(oid)
449
503
  load_additional_types([oid])
450
504
  end
@@ -533,13 +587,13 @@ module ActiveRecord
533
587
  # Quoted types
534
588
  when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
535
589
  # The default 'now'::date is CURRENT_DATE
536
- if $1 == "now".freeze && $2 == "date".freeze
590
+ if $1 == "now" && $2 == "date"
537
591
  nil
538
592
  else
539
- $1.gsub("''".freeze, "'".freeze)
593
+ $1.gsub("''", "'")
540
594
  end
541
595
  # Boolean types
542
- when "true".freeze, "false".freeze
596
+ when "true", "false"
543
597
  default
544
598
  # Numeric types
545
599
  when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
@@ -565,18 +619,11 @@ module ActiveRecord
565
619
  def load_additional_types(oids = nil)
566
620
  initializer = OID::TypeMapInitializer.new(type_map)
567
621
 
568
- if supports_ranges?
569
- query = <<-SQL
570
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
571
- FROM pg_type as t
572
- LEFT JOIN pg_range as r ON oid = rngtypid
573
- SQL
574
- else
575
- query = <<-SQL
576
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
577
- FROM pg_type as t
578
- SQL
579
- end
622
+ query = <<~SQL
623
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
624
+ FROM pg_type as t
625
+ LEFT JOIN pg_range as r ON oid = rngtypid
626
+ SQL
580
627
 
581
628
  if oids
582
629
  query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
@@ -592,6 +639,10 @@ module ActiveRecord
592
639
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
593
640
 
594
641
  def execute_and_clear(sql, name, binds, prepare: false)
642
+ if preventing_writes? && write_query?(sql)
643
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
644
+ end
645
+
595
646
  if without_prepared_statement?(binds)
596
647
  result = exec_no_cache(sql, name, [])
597
648
  elsif !prepare
@@ -605,6 +656,12 @@ module ActiveRecord
605
656
  end
606
657
 
607
658
  def exec_no_cache(sql, name, binds)
659
+ materialize_transactions
660
+
661
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
662
+ # made since we established the connection
663
+ update_typemap_for_default_timezone
664
+
608
665
  type_casted_binds = type_casted_binds(binds)
609
666
  log(sql, name, binds, type_casted_binds) do
610
667
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -614,7 +671,10 @@ module ActiveRecord
614
671
  end
615
672
 
616
673
  def exec_cache(sql, name, binds)
617
- stmt_key = prepare_statement(sql)
674
+ materialize_transactions
675
+ update_typemap_for_default_timezone
676
+
677
+ stmt_key = prepare_statement(sql, binds)
618
678
  type_casted_binds = type_casted_binds(binds)
619
679
 
620
680
  log(sql, name, binds, type_casted_binds, stmt_key) do
@@ -647,7 +707,7 @@ module ActiveRecord
647
707
  #
648
708
  # Check here for more details:
649
709
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
650
- CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
710
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
651
711
  def is_cached_plan_failure?(e)
652
712
  pgerror = e.cause
653
713
  code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
@@ -668,7 +728,7 @@ module ActiveRecord
668
728
 
669
729
  # Prepare the statement if it hasn't been prepared, return
670
730
  # the statement key.
671
- def prepare_statement(sql)
731
+ def prepare_statement(sql, binds)
672
732
  @lock.synchronize do
673
733
  sql_key = sql_key(sql)
674
734
  unless @statements.key? sql_key
@@ -676,7 +736,7 @@ module ActiveRecord
676
736
  begin
677
737
  @connection.prepare nextkey, sql
678
738
  rescue => e
679
- raise translate_exception_class(e, sql)
739
+ raise translate_exception_class(e, sql, binds)
680
740
  end
681
741
  # Clear the queue
682
742
  @connection.get_last_result
@@ -691,12 +751,8 @@ module ActiveRecord
691
751
  def connect
692
752
  @connection = PG.connect(@connection_parameters)
693
753
  configure_connection
694
- rescue ::PG::Error => error
695
- if error.message.include?("does not exist")
696
- raise ActiveRecord::NoDatabaseError
697
- else
698
- raise
699
- end
754
+ add_pg_encoders
755
+ add_pg_decoders
700
756
  end
701
757
 
702
758
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -754,7 +810,7 @@ module ActiveRecord
754
810
  # - format_type includes the column size constraint, e.g. varchar(50)
755
811
  # - ::regclass is a function that gives the id for a table name
756
812
  def column_definitions(table_name)
757
- query(<<-end_sql, "SCHEMA")
813
+ query(<<~SQL, "SCHEMA")
758
814
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
759
815
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
760
816
  c.collname, col_description(a.attrelid, a.attnum) AS comment
@@ -765,7 +821,7 @@ module ActiveRecord
765
821
  WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
766
822
  AND a.attnum > 0 AND NOT a.attisdropped
767
823
  ORDER BY a.attnum
768
- end_sql
824
+ SQL
769
825
  end
770
826
 
771
827
  def extract_table_ref_from_insert_sql(sql)
@@ -777,10 +833,14 @@ module ActiveRecord
777
833
  Arel::Visitors::PostgreSQL.new(self)
778
834
  end
779
835
 
836
+ def build_statement_pool
837
+ StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
838
+ end
839
+
780
840
  def can_perform_case_insensitive_comparison_for?(column)
781
841
  @case_insensitive_cache ||= {}
782
842
  @case_insensitive_cache[column.sql_type] ||= begin
783
- sql = <<-end_sql
843
+ sql = <<~SQL
784
844
  SELECT exists(
785
845
  SELECT * FROM pg_proc
786
846
  WHERE proname = 'lower'
@@ -792,7 +852,7 @@ module ActiveRecord
792
852
  WHERE proname = 'lower'
793
853
  AND castsource = #{quote column.sql_type}::regtype
794
854
  )
795
- end_sql
855
+ SQL
796
856
  execute_and_clear(sql, "SCHEMA", []) do |result|
797
857
  result.getvalue(0, 0)
798
858
  end
@@ -807,7 +867,22 @@ module ActiveRecord
807
867
  @connection.type_map_for_queries = map
808
868
  end
809
869
 
870
+ def update_typemap_for_default_timezone
871
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
872
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
873
+ PG::TextDecoder::TimestampUtc :
874
+ PG::TextDecoder::TimestampWithoutTimeZone
875
+
876
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
877
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
878
+ @default_timezone = ActiveRecord::Base.default_timezone
879
+ end
880
+ end
881
+
810
882
  def add_pg_decoders
883
+ @default_timezone = nil
884
+ @timestamp_decoder = nil
885
+
811
886
  coders_by_name = {
812
887
  "int2" => PG::TextDecoder::Integer,
813
888
  "int4" => PG::TextDecoder::Integer,
@@ -817,8 +892,15 @@ module ActiveRecord
817
892
  "float8" => PG::TextDecoder::Float,
818
893
  "bool" => PG::TextDecoder::Boolean,
819
894
  }
895
+
896
+ if defined?(PG::TextDecoder::TimestampUtc)
897
+ # Use native PG encoders available since pg-1.1
898
+ coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
899
+ coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
900
+ end
901
+
820
902
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
821
- query = <<-SQL % known_coder_types.join(", ")
903
+ query = <<~SQL % known_coder_types.join(", ")
822
904
  SELECT t.oid, t.typname
823
905
  FROM pg_type as t
824
906
  WHERE t.typname IN (%s)
@@ -832,6 +914,10 @@ module ActiveRecord
832
914
  map = PG::TypeMapByOid.new
833
915
  coders.each { |coder| map.add_coder(coder) }
834
916
  @connection.type_map_for_results = map
917
+
918
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
919
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
920
+ update_typemap_for_default_timezone
835
921
  end
836
922
 
837
923
  def construct_coder(row, coder_class)
@@ -13,6 +13,7 @@ module ActiveRecord
13
13
  @columns_hash = {}
14
14
  @primary_keys = {}
15
15
  @data_sources = {}
16
+ @indexes = {}
16
17
  end
17
18
 
18
19
  def initialize_dup(other)
@@ -21,22 +22,27 @@ module ActiveRecord
21
22
  @columns_hash = @columns_hash.dup
22
23
  @primary_keys = @primary_keys.dup
23
24
  @data_sources = @data_sources.dup
25
+ @indexes = @indexes.dup
24
26
  end
25
27
 
26
28
  def encode_with(coder)
27
- coder["columns"] = @columns
28
- coder["columns_hash"] = @columns_hash
29
- coder["primary_keys"] = @primary_keys
30
- coder["data_sources"] = @data_sources
31
- coder["version"] = connection.migration_context.current_version
29
+ coder["columns"] = @columns
30
+ coder["columns_hash"] = @columns_hash
31
+ coder["primary_keys"] = @primary_keys
32
+ coder["data_sources"] = @data_sources
33
+ coder["indexes"] = @indexes
34
+ coder["version"] = connection.migration_context.current_version
35
+ coder["database_version"] = database_version
32
36
  end
33
37
 
34
38
  def init_with(coder)
35
- @columns = coder["columns"]
36
- @columns_hash = coder["columns_hash"]
37
- @primary_keys = coder["primary_keys"]
38
- @data_sources = coder["data_sources"]
39
- @version = coder["version"]
39
+ @columns = coder["columns"]
40
+ @columns_hash = coder["columns_hash"]
41
+ @primary_keys = coder["primary_keys"]
42
+ @data_sources = coder["data_sources"]
43
+ @indexes = coder["indexes"] || {}
44
+ @version = coder["version"]
45
+ @database_version = coder["database_version"]
40
46
  end
41
47
 
42
48
  def primary_keys(table_name)
@@ -57,6 +63,7 @@ module ActiveRecord
57
63
  primary_keys(table_name)
58
64
  columns(table_name)
59
65
  columns_hash(table_name)
66
+ indexes(table_name)
60
67
  end
61
68
  end
62
69
 
@@ -77,17 +84,32 @@ module ActiveRecord
77
84
  }]
78
85
  end
79
86
 
87
+ # Checks whether the columns hash is already cached for a table.
88
+ def columns_hash?(table_name)
89
+ @columns_hash.key?(table_name)
90
+ end
91
+
92
+ def indexes(table_name)
93
+ @indexes[table_name] ||= connection.indexes(table_name)
94
+ end
95
+
96
+ def database_version # :nodoc:
97
+ @database_version ||= connection.get_database_version
98
+ end
99
+
80
100
  # Clears out internal caches
81
101
  def clear!
82
102
  @columns.clear
83
103
  @columns_hash.clear
84
104
  @primary_keys.clear
85
105
  @data_sources.clear
106
+ @indexes.clear
86
107
  @version = nil
108
+ @database_version = nil
87
109
  end
88
110
 
89
111
  def size
90
- [@columns, @columns_hash, @primary_keys, @data_sources].map(&:size).inject :+
112
+ [@columns, @columns_hash, @primary_keys, @data_sources].sum(&:size)
91
113
  end
92
114
 
93
115
  # Clear out internal caches for the data source +name+.
@@ -96,20 +118,21 @@ module ActiveRecord
96
118
  @columns_hash.delete name
97
119
  @primary_keys.delete name
98
120
  @data_sources.delete name
121
+ @indexes.delete name
99
122
  end
100
123
 
101
124
  def marshal_dump
102
125
  # if we get current version during initialization, it happens stack over flow.
103
126
  @version = connection.migration_context.current_version
104
- [@version, @columns, @columns_hash, @primary_keys, @data_sources]
127
+ [@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
105
128
  end
106
129
 
107
130
  def marshal_load(array)
108
- @version, @columns, @columns_hash, @primary_keys, @data_sources = array
131
+ @version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
132
+ @indexes = @indexes || {}
109
133
  end
110
134
 
111
135
  private
112
-
113
136
  def prepare_data_sources
114
137
  connection.data_sources.each { |source| @data_sources[source] = true }
115
138
  end
@@ -16,19 +16,22 @@ module ActiveRecord
16
16
 
17
17
  def ==(other)
18
18
  other.is_a?(SqlTypeMetadata) &&
19
- attributes_for_hash == other.attributes_for_hash
19
+ sql_type == other.sql_type &&
20
+ type == other.type &&
21
+ limit == other.limit &&
22
+ precision == other.precision &&
23
+ scale == other.scale
20
24
  end
21
25
  alias eql? ==
22
26
 
23
27
  def hash
24
- attributes_for_hash.hash
28
+ SqlTypeMetadata.hash ^
29
+ sql_type.hash ^
30
+ type.hash ^
31
+ limit.hash ^
32
+ precision.hash >> 1 ^
33
+ scale.hash >> 2
25
34
  end
26
-
27
- protected
28
-
29
- def attributes_for_hash
30
- [self.class, sql_type, type, limit, precision, scale]
31
- end
32
35
  end
33
36
  end
34
37
  end