activerecord 5.2.3 → 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 +624 -548
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +19 -14
  9. data/lib/active_record/associations/association.rb +52 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/association.rb +14 -18
  14. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  15. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  17. data/lib/active_record/associations/builder/has_many.rb +2 -0
  18. data/lib/active_record/associations/builder/has_one.rb +35 -1
  19. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  20. data/lib/active_record/associations/collection_association.rb +6 -21
  21. data/lib/active_record/associations/collection_proxy.rb +12 -15
  22. data/lib/active_record/associations/foreign_association.rb +7 -0
  23. data/lib/active_record/associations/has_many_association.rb +2 -10
  24. data/lib/active_record/associations/has_many_through_association.rb +18 -25
  25. data/lib/active_record/associations/has_one_association.rb +28 -30
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
  29. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +39 -31
  31. data/lib/active_record/associations/preloader/association.rb +38 -36
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/singular_association.rb +2 -16
  34. data/lib/active_record/attribute_assignment.rb +7 -10
  35. data/lib/active_record/attribute_methods.rb +28 -100
  36. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  37. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  38. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  39. data/lib/active_record/attribute_methods/query.rb +2 -3
  40. data/lib/active_record/attribute_methods/read.rb +15 -53
  41. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  42. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  43. data/lib/active_record/attribute_methods/write.rb +17 -24
  44. data/lib/active_record/attributes.rb +13 -0
  45. data/lib/active_record/autosave_association.rb +16 -6
  46. data/lib/active_record/base.rb +2 -3
  47. data/lib/active_record/callbacks.rb +5 -19
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +116 -19
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +20 -11
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +133 -54
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  60. data/lib/active_record/connection_adapters/column.rb +17 -13
  61. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  62. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  69. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  70. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  75. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  83. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  86. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  87. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  88. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  89. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  90. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  91. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -143
  94. data/lib/active_record/connection_handling.rb +149 -27
  95. data/lib/active_record/core.rb +100 -60
  96. data/lib/active_record/counter_cache.rb +4 -29
  97. data/lib/active_record/database_configurations.rb +233 -0
  98. data/lib/active_record/database_configurations/database_config.rb +37 -0
  99. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  100. data/lib/active_record/database_configurations/url_config.rb +79 -0
  101. data/lib/active_record/dynamic_matchers.rb +1 -1
  102. data/lib/active_record/enum.rb +37 -7
  103. data/lib/active_record/errors.rb +15 -7
  104. data/lib/active_record/explain.rb +1 -1
  105. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  106. data/lib/active_record/fixture_set/render_context.rb +17 -0
  107. data/lib/active_record/fixture_set/table_row.rb +153 -0
  108. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  109. data/lib/active_record/fixtures.rb +145 -472
  110. data/lib/active_record/gem_version.rb +3 -3
  111. data/lib/active_record/inheritance.rb +13 -3
  112. data/lib/active_record/insert_all.rb +179 -0
  113. data/lib/active_record/integration.rb +68 -16
  114. data/lib/active_record/internal_metadata.rb +10 -2
  115. data/lib/active_record/locking/optimistic.rb +5 -6
  116. data/lib/active_record/locking/pessimistic.rb +3 -3
  117. data/lib/active_record/log_subscriber.rb +7 -26
  118. data/lib/active_record/middleware/database_selector.rb +75 -0
  119. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  120. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  121. data/lib/active_record/migration.rb +100 -81
  122. data/lib/active_record/migration/command_recorder.rb +50 -6
  123. data/lib/active_record/migration/compatibility.rb +76 -49
  124. data/lib/active_record/model_schema.rb +30 -9
  125. data/lib/active_record/nested_attributes.rb +2 -2
  126. data/lib/active_record/no_touching.rb +7 -0
  127. data/lib/active_record/persistence.rb +228 -24
  128. data/lib/active_record/query_cache.rb +11 -4
  129. data/lib/active_record/querying.rb +32 -20
  130. data/lib/active_record/railtie.rb +80 -43
  131. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  132. data/lib/active_record/railties/controller_runtime.rb +30 -35
  133. data/lib/active_record/railties/databases.rake +196 -46
  134. data/lib/active_record/reflection.rb +42 -44
  135. data/lib/active_record/relation.rb +310 -80
  136. data/lib/active_record/relation/batches.rb +13 -10
  137. data/lib/active_record/relation/calculations.rb +58 -51
  138. data/lib/active_record/relation/delegation.rb +26 -43
  139. data/lib/active_record/relation/finder_methods.rb +14 -27
  140. data/lib/active_record/relation/merger.rb +11 -20
  141. data/lib/active_record/relation/predicate_builder.rb +4 -6
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  145. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  147. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  148. data/lib/active_record/relation/query_attribute.rb +13 -8
  149. data/lib/active_record/relation/query_methods.rb +206 -78
  150. data/lib/active_record/relation/spawn_methods.rb +1 -1
  151. data/lib/active_record/relation/where_clause.rb +14 -10
  152. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  153. data/lib/active_record/result.rb +30 -11
  154. data/lib/active_record/sanitization.rb +32 -40
  155. data/lib/active_record/schema.rb +2 -11
  156. data/lib/active_record/schema_dumper.rb +22 -7
  157. data/lib/active_record/schema_migration.rb +5 -1
  158. data/lib/active_record/scoping.rb +8 -8
  159. data/lib/active_record/scoping/default.rb +6 -7
  160. data/lib/active_record/scoping/named.rb +19 -15
  161. data/lib/active_record/statement_cache.rb +32 -5
  162. data/lib/active_record/store.rb +87 -8
  163. data/lib/active_record/table_metadata.rb +10 -17
  164. data/lib/active_record/tasks/database_tasks.rb +194 -25
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  168. data/lib/active_record/test_databases.rb +23 -0
  169. data/lib/active_record/test_fixtures.rb +224 -0
  170. data/lib/active_record/timestamp.rb +39 -25
  171. data/lib/active_record/touch_later.rb +4 -2
  172. data/lib/active_record/transactions.rb +56 -65
  173. data/lib/active_record/translation.rb +1 -1
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  176. data/lib/active_record/type_caster/connection.rb +15 -14
  177. data/lib/active_record/type_caster/map.rb +1 -4
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record/validations/uniqueness.rb +15 -27
  180. data/lib/arel.rb +51 -0
  181. data/lib/arel/alias_predication.rb +9 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/attributes/attribute.rb +37 -0
  184. data/lib/arel/collectors/bind.rb +24 -0
  185. data/lib/arel/collectors/composite.rb +31 -0
  186. data/lib/arel/collectors/plain_string.rb +20 -0
  187. data/lib/arel/collectors/sql_string.rb +20 -0
  188. data/lib/arel/collectors/substitute_binds.rb +28 -0
  189. data/lib/arel/crud.rb +42 -0
  190. data/lib/arel/delete_manager.rb +18 -0
  191. data/lib/arel/errors.rb +9 -0
  192. data/lib/arel/expressions.rb +29 -0
  193. data/lib/arel/factory_methods.rb +49 -0
  194. data/lib/arel/insert_manager.rb +49 -0
  195. data/lib/arel/math.rb +45 -0
  196. data/lib/arel/nodes.rb +68 -0
  197. data/lib/arel/nodes/and.rb +32 -0
  198. data/lib/arel/nodes/ascending.rb +23 -0
  199. data/lib/arel/nodes/binary.rb +52 -0
  200. data/lib/arel/nodes/bind_param.rb +36 -0
  201. data/lib/arel/nodes/case.rb +55 -0
  202. data/lib/arel/nodes/casted.rb +50 -0
  203. data/lib/arel/nodes/comment.rb +29 -0
  204. data/lib/arel/nodes/count.rb +12 -0
  205. data/lib/arel/nodes/delete_statement.rb +45 -0
  206. data/lib/arel/nodes/descending.rb +23 -0
  207. data/lib/arel/nodes/equality.rb +18 -0
  208. data/lib/arel/nodes/extract.rb +24 -0
  209. data/lib/arel/nodes/false.rb +16 -0
  210. data/lib/arel/nodes/full_outer_join.rb +8 -0
  211. data/lib/arel/nodes/function.rb +44 -0
  212. data/lib/arel/nodes/grouping.rb +8 -0
  213. data/lib/arel/nodes/in.rb +8 -0
  214. data/lib/arel/nodes/infix_operation.rb +80 -0
  215. data/lib/arel/nodes/inner_join.rb +8 -0
  216. data/lib/arel/nodes/insert_statement.rb +37 -0
  217. data/lib/arel/nodes/join_source.rb +20 -0
  218. data/lib/arel/nodes/matches.rb +18 -0
  219. data/lib/arel/nodes/named_function.rb +23 -0
  220. data/lib/arel/nodes/node.rb +50 -0
  221. data/lib/arel/nodes/node_expression.rb +13 -0
  222. data/lib/arel/nodes/outer_join.rb +8 -0
  223. data/lib/arel/nodes/over.rb +15 -0
  224. data/lib/arel/nodes/regexp.rb +16 -0
  225. data/lib/arel/nodes/right_outer_join.rb +8 -0
  226. data/lib/arel/nodes/select_core.rb +67 -0
  227. data/lib/arel/nodes/select_statement.rb +41 -0
  228. data/lib/arel/nodes/sql_literal.rb +16 -0
  229. data/lib/arel/nodes/string_join.rb +11 -0
  230. data/lib/arel/nodes/table_alias.rb +27 -0
  231. data/lib/arel/nodes/terminal.rb +16 -0
  232. data/lib/arel/nodes/true.rb +16 -0
  233. data/lib/arel/nodes/unary.rb +45 -0
  234. data/lib/arel/nodes/unary_operation.rb +20 -0
  235. data/lib/arel/nodes/unqualified_column.rb +22 -0
  236. data/lib/arel/nodes/update_statement.rb +41 -0
  237. data/lib/arel/nodes/values_list.rb +9 -0
  238. data/lib/arel/nodes/window.rb +126 -0
  239. data/lib/arel/nodes/with.rb +11 -0
  240. data/lib/arel/order_predications.rb +13 -0
  241. data/lib/arel/predications.rb +257 -0
  242. data/lib/arel/select_manager.rb +271 -0
  243. data/lib/arel/table.rb +110 -0
  244. data/lib/arel/tree_manager.rb +72 -0
  245. data/lib/arel/update_manager.rb +34 -0
  246. data/lib/arel/visitors.rb +20 -0
  247. data/lib/arel/visitors/depth_first.rb +204 -0
  248. data/lib/arel/visitors/dot.rb +297 -0
  249. data/lib/arel/visitors/ibm_db.rb +34 -0
  250. data/lib/arel/visitors/informix.rb +62 -0
  251. data/lib/arel/visitors/mssql.rb +157 -0
  252. data/lib/arel/visitors/mysql.rb +83 -0
  253. data/lib/arel/visitors/oracle.rb +159 -0
  254. data/lib/arel/visitors/oracle12.rb +66 -0
  255. data/lib/arel/visitors/postgresql.rb +110 -0
  256. data/lib/arel/visitors/sqlite.rb +39 -0
  257. data/lib/arel/visitors/to_sql.rb +889 -0
  258. data/lib/arel/visitors/visitor.rb +46 -0
  259. data/lib/arel/visitors/where_sql.rb +23 -0
  260. data/lib/arel/window_predications.rb +9 -0
  261. data/lib/rails/generators/active_record/migration.rb +14 -1
  262. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  263. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  264. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  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 +107 -25
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -22,8 +22,8 @@ module ActiveRecord
22
22
  def create_database(name, options = {})
23
23
  options = { encoding: "utf8" }.merge!(options.symbolize_keys)
24
24
 
25
- option_string = options.inject("") do |memo, (key, value)|
26
- memo += case key
25
+ option_string = options.each_with_object(+"") do |(key, value), memo|
26
+ memo << case key
27
27
  when :owner
28
28
  " OWNER = \"#{value}\""
29
29
  when :template
@@ -68,7 +68,7 @@ module ActiveRecord
68
68
  table = quoted_scope(table_name)
69
69
  index = quoted_scope(index_name)
70
70
 
71
- query_value(<<-SQL, "SCHEMA").to_i > 0
71
+ query_value(<<~SQL, "SCHEMA").to_i > 0
72
72
  SELECT COUNT(*)
73
73
  FROM pg_class t
74
74
  INNER JOIN pg_index d ON t.oid = d.indrelid
@@ -85,7 +85,7 @@ module ActiveRecord
85
85
  def indexes(table_name) # :nodoc:
86
86
  scope = quoted_scope(table_name)
87
87
 
88
- result = query(<<-SQL, "SCHEMA")
88
+ result = query(<<~SQL, "SCHEMA")
89
89
  SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
90
90
  pg_catalog.obj_description(i.oid, 'pg_class') AS comment
91
91
  FROM pg_class t
@@ -115,7 +115,7 @@ module ActiveRecord
115
115
  if indkey.include?(0)
116
116
  columns = expressions
117
117
  else
118
- columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
118
+ columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
119
119
  SELECT a.attnum, a.attname
120
120
  FROM pg_attribute a
121
121
  WHERE a.attrelid = #{oid}
@@ -158,7 +158,7 @@ module ActiveRecord
158
158
  def table_comment(table_name) # :nodoc:
159
159
  scope = quoted_scope(table_name, type: "BASE TABLE")
160
160
  if scope[:name]
161
- query_value(<<-SQL.strip_heredoc, "SCHEMA")
161
+ query_value(<<~SQL, "SCHEMA")
162
162
  SELECT pg_catalog.obj_description(c.oid, 'pg_class')
163
163
  FROM pg_catalog.pg_class c
164
164
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -196,7 +196,7 @@ module ActiveRecord
196
196
 
197
197
  # Returns an array of schema names.
198
198
  def schema_names
199
- query_values(<<-SQL, "SCHEMA")
199
+ query_values(<<~SQL, "SCHEMA")
200
200
  SELECT nspname
201
201
  FROM pg_namespace
202
202
  WHERE nspname !~ '^pg_.*'
@@ -287,7 +287,7 @@ module ActiveRecord
287
287
  quoted_sequence = quote_table_name(sequence)
288
288
  max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
289
289
  if max_pk.nil?
290
- if postgresql_version >= 100000
290
+ if database_version >= 100000
291
291
  minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
292
292
  else
293
293
  minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
@@ -302,7 +302,7 @@ module ActiveRecord
302
302
  def pk_and_sequence_for(table) #:nodoc:
303
303
  # First try looking for a sequence with a dependency on the
304
304
  # given table's primary key.
305
- result = query(<<-end_sql, "SCHEMA")[0]
305
+ result = query(<<~SQL, "SCHEMA")[0]
306
306
  SELECT attr.attname, nsp.nspname, seq.relname
307
307
  FROM pg_class seq,
308
308
  pg_attribute attr,
@@ -319,10 +319,10 @@ module ActiveRecord
319
319
  AND cons.contype = 'p'
320
320
  AND dep.classid = 'pg_class'::regclass
321
321
  AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
322
- end_sql
322
+ SQL
323
323
 
324
324
  if result.nil? || result.empty?
325
- result = query(<<-end_sql, "SCHEMA")[0]
325
+ result = query(<<~SQL, "SCHEMA")[0]
326
326
  SELECT attr.attname, nsp.nspname,
327
327
  CASE
328
328
  WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
@@ -339,7 +339,7 @@ module ActiveRecord
339
339
  WHERE t.oid = #{quote(quote_table_name(table))}::regclass
340
340
  AND cons.contype = 'p'
341
341
  AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
342
- end_sql
342
+ SQL
343
343
  end
344
344
 
345
345
  pk = result.shift
@@ -353,7 +353,7 @@ module ActiveRecord
353
353
  end
354
354
 
355
355
  def primary_keys(table_name) # :nodoc:
356
- query_values(<<-SQL.strip_heredoc, "SCHEMA")
356
+ query_values(<<~SQL, "SCHEMA")
357
357
  SELECT a.attname
358
358
  FROM (
359
359
  SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
@@ -368,31 +368,6 @@ module ActiveRecord
368
368
  SQL
369
369
  end
370
370
 
371
- def bulk_change_table(table_name, operations)
372
- sql_fragments = []
373
- non_combinable_operations = []
374
-
375
- operations.each do |command, args|
376
- table, arguments = args.shift, args
377
- method = :"#{command}_for_alter"
378
-
379
- if respond_to?(method, true)
380
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
381
- sql_fragments << sqls
382
- non_combinable_operations.concat(procs)
383
- else
384
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
385
- non_combinable_operations.each(&:call)
386
- sql_fragments = []
387
- non_combinable_operations = []
388
- send(command, table, *arguments)
389
- end
390
- end
391
-
392
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
393
- non_combinable_operations.each(&:call)
394
- end
395
-
396
371
  # Renames a table.
397
372
  # Also renames a table's primary key sequence if the sequence name exists and
398
373
  # matches the Active Record default.
@@ -443,14 +418,16 @@ module ActiveRecord
443
418
  end
444
419
 
445
420
  # Adds comment for given table column or drops it if +comment+ is a +nil+
446
- def change_column_comment(table_name, column_name, comment) # :nodoc:
421
+ def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
447
422
  clear_cache!
423
+ comment = extract_new_comment_value(comment_or_changes)
448
424
  execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
449
425
  end
450
426
 
451
427
  # Adds comment for given table or drops it if +comment+ is a +nil+
452
- def change_table_comment(table_name, comment) # :nodoc:
428
+ def change_table_comment(table_name, comment_or_changes) # :nodoc:
453
429
  clear_cache!
430
+ comment = extract_new_comment_value(comment_or_changes)
454
431
  execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
455
432
  end
456
433
 
@@ -502,7 +479,7 @@ module ActiveRecord
502
479
 
503
480
  def foreign_keys(table_name)
504
481
  scope = quoted_scope(table_name)
505
- fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
482
+ fk_info = exec_query(<<~SQL, "SCHEMA")
506
483
  SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
507
484
  FROM pg_constraint c
508
485
  JOIN pg_class t1 ON c.conrelid = t1.oid
@@ -548,21 +525,21 @@ module ActiveRecord
548
525
  # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
549
526
  case limit
550
527
  when nil, 0..0x3fffffff; super(type)
551
- else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
528
+ else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
552
529
  end
553
530
  when "text"
554
531
  # PostgreSQL doesn't support limits on text columns.
555
532
  # The hard limit is 1GB, according to section 8.3 in the manual.
556
533
  case limit
557
534
  when nil, 0..0x3fffffff; super(type)
558
- else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
535
+ else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
559
536
  end
560
537
  when "integer"
561
538
  case limit
562
539
  when 1, 2; "smallint"
563
540
  when nil, 3, 4; "integer"
564
541
  when 5..8; "bigint"
565
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
542
+ else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
566
543
  end
567
544
  else
568
545
  super
@@ -623,10 +600,10 @@ module ActiveRecord
623
600
  # validate_foreign_key :accounts, name: :special_fk_name
624
601
  #
625
602
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
626
- def validate_foreign_key(from_table, options_or_to_table = {})
603
+ def validate_foreign_key(from_table, to_table = nil, **options)
627
604
  return unless supports_validate_constraints?
628
605
 
629
- fk_name_to_validate = foreign_key_for!(from_table, options_or_to_table).name
606
+ fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
630
607
 
631
608
  validate_constraint from_table, fk_name_to_validate
632
609
  end
@@ -637,7 +614,7 @@ module ActiveRecord
637
614
  end
638
615
 
639
616
  def create_table_definition(*args)
640
- PostgreSQL::TableDefinition.new(*args)
617
+ PostgreSQL::TableDefinition.new(self, *args)
641
618
  end
642
619
 
643
620
  def create_alter_table(name)
@@ -650,16 +627,19 @@ module ActiveRecord
650
627
  default_value = extract_value_from_default(default)
651
628
  default_function = extract_default_function(default_value, default)
652
629
 
653
- PostgreSQLColumn.new(
630
+ if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
631
+ serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
632
+ end
633
+
634
+ PostgreSQL::Column.new(
654
635
  column_name,
655
636
  default_value,
656
637
  type_metadata,
657
638
  !notnull,
658
- table_name,
659
639
  default_function,
660
- collation,
640
+ collation: collation,
661
641
  comment: comment.presence,
662
- max_identifier_length: max_identifier_length
642
+ serial: serial
663
643
  )
664
644
  end
665
645
 
@@ -672,7 +652,23 @@ module ActiveRecord
672
652
  precision: cast_type.precision,
673
653
  scale: cast_type.scale,
674
654
  )
675
- PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
655
+ PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod)
656
+ end
657
+
658
+ def sequence_name_from_parts(table_name, column_name, suffix)
659
+ over_length = [table_name, column_name, suffix].sum(&:length) + 2 - max_identifier_length
660
+
661
+ if over_length > 0
662
+ column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
663
+ over_length -= column_name.length - column_name_length
664
+ column_name = column_name[0, column_name_length - [over_length, 0].min]
665
+ end
666
+
667
+ if over_length > 0
668
+ table_name = table_name[0, table_name.length - over_length]
669
+ end
670
+
671
+ "#{table_name}_#{column_name}_#{suffix}"
676
672
  end
677
673
 
678
674
  def extract_foreign_key_action(specifier)
@@ -716,6 +712,12 @@ module ActiveRecord
716
712
  end
717
713
 
718
714
  def add_timestamps_for_alter(table_name, options = {})
715
+ options[:null] = false if options[:null].nil?
716
+
717
+ if !options.key?(:precision) && supports_datetime_with_precision?
718
+ options[:precision] = 6
719
+ end
720
+
719
721
  [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
720
722
  end
721
723
 
@@ -739,7 +741,7 @@ module ActiveRecord
739
741
  scope = quoted_scope(name, type: type)
740
742
  scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
741
743
 
742
- sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
744
+ sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
743
745
  sql << " WHERE n.nspname = #{scope[:schema]}"
744
746
  sql << " AND c.relname = #{scope[:name]}" if scope[:name]
745
747
  sql << " AND c.relkind IN (#{scope[:type]})"
@@ -1,39 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
+ # :stopdoc:
4
5
  module ConnectionAdapters
5
- class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
6
- undef to_yaml if method_defined?(:to_yaml)
6
+ module PostgreSQL
7
+ class TypeMetadata < DelegateClass(SqlTypeMetadata)
8
+ undef to_yaml if method_defined?(:to_yaml)
7
9
 
8
- attr_reader :oid, :fmod, :array
10
+ attr_reader :oid, :fmod
9
11
 
10
- def initialize(type_metadata, oid: nil, fmod: nil)
11
- super(type_metadata)
12
- @type_metadata = type_metadata
13
- @oid = oid
14
- @fmod = fmod
15
- @array = /\[\]$/.match?(type_metadata.sql_type)
16
- end
17
-
18
- def sql_type
19
- super.gsub(/\[\]$/, "".freeze)
20
- end
21
-
22
- def ==(other)
23
- other.is_a?(PostgreSQLTypeMetadata) &&
24
- attributes_for_hash == other.attributes_for_hash
25
- end
26
- alias eql? ==
27
-
28
- def hash
29
- attributes_for_hash.hash
30
- end
12
+ def initialize(type_metadata, oid: nil, fmod: nil)
13
+ super(type_metadata)
14
+ @oid = oid
15
+ @fmod = fmod
16
+ end
31
17
 
32
- protected
18
+ def ==(other)
19
+ other.is_a?(TypeMetadata) &&
20
+ __getobj__ == other.__getobj__ &&
21
+ oid == other.oid &&
22
+ fmod == other.fmod
23
+ end
24
+ alias eql? ==
33
25
 
34
- def attributes_for_hash
35
- [self.class, @type_metadata, oid, fmod]
26
+ def hash
27
+ TypeMetadata.hash ^
28
+ __getobj__.hash ^
29
+ oid.hash ^
30
+ fmod.hash
36
31
  end
32
+ end
37
33
  end
34
+ PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata
38
35
  end
39
36
  end
@@ -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)