activerecord 5.2.1.1 → 6.0.1

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +738 -445
  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/association_relation.rb +18 -9
  9. data/lib/active_record/associations.rb +20 -15
  10. data/lib/active_record/associations/association.rb +69 -20
  11. data/lib/active_record/associations/association_scope.rb +4 -6
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +15 -29
  22. data/lib/active_record/associations/collection_proxy.rb +19 -48
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +11 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +42 -25
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency.rb +28 -28
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +39 -31
  32. data/lib/active_record/associations/preloader/association.rb +38 -36
  33. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/attribute_assignment.rb +7 -10
  36. data/lib/active_record/attribute_methods.rb +28 -100
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  38. data/lib/active_record/attribute_methods/dirty.rb +114 -38
  39. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  40. data/lib/active_record/attribute_methods/query.rb +2 -3
  41. data/lib/active_record/attribute_methods/read.rb +15 -53
  42. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  44. data/lib/active_record/attribute_methods/write.rb +17 -24
  45. data/lib/active_record/attributes.rb +13 -0
  46. data/lib/active_record/autosave_association.rb +27 -13
  47. data/lib/active_record/base.rb +2 -3
  48. data/lib/active_record/callbacks.rb +6 -20
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
  61. data/lib/active_record/connection_adapters/column.rb +17 -13
  62. data/lib/active_record/connection_adapters/connection_specification.rb +55 -45
  63. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  66. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  67. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  68. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  69. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  70. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  71. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  72. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +8 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  81. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  82. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  83. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -77
  87. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -74
  90. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  91. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  92. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  93. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
  96. data/lib/active_record/connection_handling.rb +155 -26
  97. data/lib/active_record/core.rb +104 -59
  98. data/lib/active_record/counter_cache.rb +4 -29
  99. data/lib/active_record/database_configurations.rb +233 -0
  100. data/lib/active_record/database_configurations/database_config.rb +37 -0
  101. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  102. data/lib/active_record/database_configurations/url_config.rb +79 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/enum.rb +38 -7
  105. data/lib/active_record/errors.rb +30 -16
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  108. data/lib/active_record/fixture_set/render_context.rb +17 -0
  109. data/lib/active_record/fixture_set/table_row.rb +153 -0
  110. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  111. data/lib/active_record/fixtures.rb +145 -472
  112. data/lib/active_record/gem_version.rb +3 -3
  113. data/lib/active_record/inheritance.rb +13 -3
  114. data/lib/active_record/insert_all.rb +179 -0
  115. data/lib/active_record/integration.rb +68 -16
  116. data/lib/active_record/internal_metadata.rb +10 -2
  117. data/lib/active_record/locking/optimistic.rb +5 -6
  118. data/lib/active_record/locking/pessimistic.rb +3 -3
  119. data/lib/active_record/log_subscriber.rb +7 -26
  120. data/lib/active_record/middleware/database_selector.rb +75 -0
  121. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  122. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  123. data/lib/active_record/migration.rb +100 -81
  124. data/lib/active_record/migration/command_recorder.rb +50 -6
  125. data/lib/active_record/migration/compatibility.rb +91 -64
  126. data/lib/active_record/model_schema.rb +34 -10
  127. data/lib/active_record/nested_attributes.rb +2 -2
  128. data/lib/active_record/no_touching.rb +7 -0
  129. data/lib/active_record/persistence.rb +233 -28
  130. data/lib/active_record/query_cache.rb +11 -4
  131. data/lib/active_record/querying.rb +33 -21
  132. data/lib/active_record/railtie.rb +81 -46
  133. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  134. data/lib/active_record/railties/controller_runtime.rb +30 -35
  135. data/lib/active_record/railties/databases.rake +196 -46
  136. data/lib/active_record/reflection.rb +42 -44
  137. data/lib/active_record/relation.rb +320 -70
  138. data/lib/active_record/relation/batches.rb +13 -10
  139. data/lib/active_record/relation/calculations.rb +67 -57
  140. data/lib/active_record/relation/delegation.rb +48 -35
  141. data/lib/active_record/relation/finder_methods.rb +30 -30
  142. data/lib/active_record/relation/merger.rb +19 -25
  143. data/lib/active_record/relation/predicate_builder.rb +18 -15
  144. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
  145. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  146. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  147. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  148. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  149. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  150. data/lib/active_record/relation/query_attribute.rb +17 -10
  151. data/lib/active_record/relation/query_methods.rb +236 -73
  152. data/lib/active_record/relation/spawn_methods.rb +1 -1
  153. data/lib/active_record/relation/where_clause.rb +14 -10
  154. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  155. data/lib/active_record/result.rb +30 -11
  156. data/lib/active_record/sanitization.rb +32 -40
  157. data/lib/active_record/schema.rb +2 -11
  158. data/lib/active_record/schema_dumper.rb +22 -7
  159. data/lib/active_record/schema_migration.rb +5 -1
  160. data/lib/active_record/scoping.rb +8 -8
  161. data/lib/active_record/scoping/default.rb +6 -7
  162. data/lib/active_record/scoping/named.rb +21 -15
  163. data/lib/active_record/statement_cache.rb +32 -5
  164. data/lib/active_record/store.rb +87 -8
  165. data/lib/active_record/table_metadata.rb +10 -17
  166. data/lib/active_record/tasks/database_tasks.rb +195 -26
  167. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  168. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  169. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  170. data/lib/active_record/test_databases.rb +23 -0
  171. data/lib/active_record/test_fixtures.rb +224 -0
  172. data/lib/active_record/timestamp.rb +39 -25
  173. data/lib/active_record/touch_later.rb +4 -2
  174. data/lib/active_record/transactions.rb +57 -66
  175. data/lib/active_record/translation.rb +1 -1
  176. data/lib/active_record/type.rb +3 -4
  177. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  178. data/lib/active_record/type_caster/connection.rb +15 -14
  179. data/lib/active_record/type_caster/map.rb +1 -4
  180. data/lib/active_record/validations.rb +1 -0
  181. data/lib/active_record/validations/uniqueness.rb +15 -27
  182. data/lib/arel.rb +58 -0
  183. data/lib/arel/alias_predication.rb +9 -0
  184. data/lib/arel/attributes.rb +22 -0
  185. data/lib/arel/attributes/attribute.rb +37 -0
  186. data/lib/arel/collectors/bind.rb +24 -0
  187. data/lib/arel/collectors/composite.rb +31 -0
  188. data/lib/arel/collectors/plain_string.rb +20 -0
  189. data/lib/arel/collectors/sql_string.rb +20 -0
  190. data/lib/arel/collectors/substitute_binds.rb +28 -0
  191. data/lib/arel/crud.rb +42 -0
  192. data/lib/arel/delete_manager.rb +18 -0
  193. data/lib/arel/errors.rb +9 -0
  194. data/lib/arel/expressions.rb +29 -0
  195. data/lib/arel/factory_methods.rb +49 -0
  196. data/lib/arel/insert_manager.rb +49 -0
  197. data/lib/arel/math.rb +45 -0
  198. data/lib/arel/nodes.rb +68 -0
  199. data/lib/arel/nodes/and.rb +32 -0
  200. data/lib/arel/nodes/ascending.rb +23 -0
  201. data/lib/arel/nodes/binary.rb +52 -0
  202. data/lib/arel/nodes/bind_param.rb +36 -0
  203. data/lib/arel/nodes/case.rb +55 -0
  204. data/lib/arel/nodes/casted.rb +50 -0
  205. data/lib/arel/nodes/comment.rb +29 -0
  206. data/lib/arel/nodes/count.rb +12 -0
  207. data/lib/arel/nodes/delete_statement.rb +45 -0
  208. data/lib/arel/nodes/descending.rb +23 -0
  209. data/lib/arel/nodes/equality.rb +18 -0
  210. data/lib/arel/nodes/extract.rb +24 -0
  211. data/lib/arel/nodes/false.rb +16 -0
  212. data/lib/arel/nodes/full_outer_join.rb +8 -0
  213. data/lib/arel/nodes/function.rb +44 -0
  214. data/lib/arel/nodes/grouping.rb +8 -0
  215. data/lib/arel/nodes/in.rb +8 -0
  216. data/lib/arel/nodes/infix_operation.rb +80 -0
  217. data/lib/arel/nodes/inner_join.rb +8 -0
  218. data/lib/arel/nodes/insert_statement.rb +37 -0
  219. data/lib/arel/nodes/join_source.rb +20 -0
  220. data/lib/arel/nodes/matches.rb +18 -0
  221. data/lib/arel/nodes/named_function.rb +23 -0
  222. data/lib/arel/nodes/node.rb +50 -0
  223. data/lib/arel/nodes/node_expression.rb +13 -0
  224. data/lib/arel/nodes/outer_join.rb +8 -0
  225. data/lib/arel/nodes/over.rb +15 -0
  226. data/lib/arel/nodes/regexp.rb +16 -0
  227. data/lib/arel/nodes/right_outer_join.rb +8 -0
  228. data/lib/arel/nodes/select_core.rb +67 -0
  229. data/lib/arel/nodes/select_statement.rb +41 -0
  230. data/lib/arel/nodes/sql_literal.rb +16 -0
  231. data/lib/arel/nodes/string_join.rb +11 -0
  232. data/lib/arel/nodes/table_alias.rb +27 -0
  233. data/lib/arel/nodes/terminal.rb +16 -0
  234. data/lib/arel/nodes/true.rb +16 -0
  235. data/lib/arel/nodes/unary.rb +45 -0
  236. data/lib/arel/nodes/unary_operation.rb +20 -0
  237. data/lib/arel/nodes/unqualified_column.rb +22 -0
  238. data/lib/arel/nodes/update_statement.rb +41 -0
  239. data/lib/arel/nodes/values_list.rb +9 -0
  240. data/lib/arel/nodes/window.rb +126 -0
  241. data/lib/arel/nodes/with.rb +11 -0
  242. data/lib/arel/order_predications.rb +13 -0
  243. data/lib/arel/predications.rb +257 -0
  244. data/lib/arel/select_manager.rb +271 -0
  245. data/lib/arel/table.rb +110 -0
  246. data/lib/arel/tree_manager.rb +72 -0
  247. data/lib/arel/update_manager.rb +34 -0
  248. data/lib/arel/visitors.rb +20 -0
  249. data/lib/arel/visitors/depth_first.rb +204 -0
  250. data/lib/arel/visitors/dot.rb +297 -0
  251. data/lib/arel/visitors/ibm_db.rb +34 -0
  252. data/lib/arel/visitors/informix.rb +62 -0
  253. data/lib/arel/visitors/mssql.rb +157 -0
  254. data/lib/arel/visitors/mysql.rb +83 -0
  255. data/lib/arel/visitors/oracle.rb +159 -0
  256. data/lib/arel/visitors/oracle12.rb +66 -0
  257. data/lib/arel/visitors/postgresql.rb +110 -0
  258. data/lib/arel/visitors/sqlite.rb +39 -0
  259. data/lib/arel/visitors/to_sql.rb +889 -0
  260. data/lib/arel/visitors/visitor.rb +46 -0
  261. data/lib/arel/visitors/where_sql.rb +23 -0
  262. data/lib/arel/window_predications.rb +9 -0
  263. data/lib/rails/generators/active_record/migration.rb +14 -1
  264. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  265. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  266. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  267. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  268. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  269. metadata +111 -27
  270. 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}
@@ -124,7 +124,7 @@ module ActiveRecord
124
124
 
125
125
  # add info on sort order (only desc order is explicitly specified, asc is the default)
126
126
  # and non-default opclasses
127
- expressions.scan(/(?<column>\w+)\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
127
+ expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
128
128
  opclasses[column] = opclass.to_sym if opclass
129
129
  if nulls
130
130
  orders[column] = [desc, nulls].compact.join(" ")
@@ -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)
@@ -683,34 +679,20 @@ module ActiveRecord
683
679
  end
684
680
  end
685
681
 
686
- def change_column_sql(table_name, column_name, type, options = {})
687
- quoted_column_name = quote_column_name(column_name)
688
- sql_type = type_to_sql(type, options)
689
- sql = "ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}".dup
690
- if options[:collation]
691
- sql << " COLLATE \"#{options[:collation]}\""
692
- end
693
- if options[:using]
694
- sql << " USING #{options[:using]}"
695
- elsif options[:cast_as]
696
- cast_as_type = type_to_sql(options[:cast_as], options)
697
- sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
698
- end
699
-
700
- sql
682
+ def add_column_for_alter(table_name, column_name, type, options = {})
683
+ return super unless options.key?(:comment)
684
+ [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
701
685
  end
702
686
 
703
687
  def change_column_for_alter(table_name, column_name, type, options = {})
704
- sqls = [change_column_sql(table_name, column_name, type, options)]
705
- sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default)
706
- sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
688
+ td = create_table_definition(table_name)
689
+ cd = td.new_column_definition(column_name, type, options)
690
+ sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
707
691
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
708
692
  sqls
709
693
  end
710
694
 
711
-
712
- # Changes the default value of a table column.
713
- def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc:
695
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
714
696
  column = column_for(table_name, column_name)
715
697
  return unless column
716
698
 
@@ -725,11 +707,17 @@ module ActiveRecord
725
707
  end
726
708
  end
727
709
 
728
- def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc:
729
- "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
710
+ def change_column_null_for_alter(table_name, column_name, null, default = nil)
711
+ "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
730
712
  end
731
713
 
732
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
+
733
721
  [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
734
722
  end
735
723
 
@@ -753,7 +741,7 @@ module ActiveRecord
753
741
  scope = quoted_scope(name, type: type)
754
742
  scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
755
743
 
756
- 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"
757
745
  sql << " WHERE n.nspname = #{scope[:schema]}"
758
746
  sql << " AND c.relname = #{scope[:name]}" if scope[:name]
759
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
@@ -68,7 +68,7 @@ module ActiveRecord
68
68
  # * <tt>"schema_name".table_name</tt>
69
69
  # * <tt>"schema.name"."table name"</tt>
70
70
  def extract_schema_qualified_name(string)
71
- schema, table = string.scan(/[^".\s]+|"[^"]*"/)
71
+ schema, table = string.scan(/[^".]+|"[^"]*"/)
72
72
  if table.nil?
73
73
  table = schema
74
74
  schema = nil
@@ -4,6 +4,14 @@
4
4
  gem "pg", ">= 0.18", "< 2.0"
5
5
  require "pg"
6
6
 
7
+ # Use async_exec instead of exec_params on pg versions before 1.1
8
+ class ::PG::Connection # :nodoc:
9
+ unless self.public_method_defined?(:async_exec_params)
10
+ remove_method :exec_params
11
+ alias exec_params async_exec
12
+ end
13
+ end
14
+
7
15
  require "active_record/connection_adapters/abstract_adapter"
8
16
  require "active_record/connection_adapters/statement_pool"
9
17
  require "active_record/connection_adapters/postgresql/column"
@@ -35,9 +43,14 @@ module ActiveRecord
35
43
  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
36
44
  conn_params.slice!(*valid_conn_param_keys)
37
45
 
38
- # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
39
- # so just pass a nil connection object for the time being.
40
- 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
41
54
  end
42
55
  end
43
56
 
@@ -70,7 +83,20 @@ module ActiveRecord
70
83
  # In addition, default connection parameters of libpq can be set per environment variables.
71
84
  # See https://www.postgresql.org/docs/current/static/libpq-envars.html .
72
85
  class PostgreSQLAdapter < AbstractAdapter
73
- 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
74
100
 
75
101
  NATIVE_DATABASE_TYPES = {
76
102
  primary_key: "bigserial primary key",
@@ -159,7 +185,7 @@ module ActiveRecord
159
185
  end
160
186
 
161
187
  def supports_json?
162
- postgresql_version >= 90200
188
+ true
163
189
  end
164
190
 
165
191
  def supports_comments?
@@ -170,6 +196,17 @@ module ActiveRecord
170
196
  true
171
197
  end
172
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
+
173
210
  def index_algorithms
174
211
  { concurrently: "CONCURRENTLY" }
175
212
  end
@@ -212,15 +249,8 @@ module ActiveRecord
212
249
  @local_tz = nil
213
250
  @max_identifier_length = nil
214
251
 
215
- connect
252
+ configure_connection
216
253
  add_pg_encoders
217
- @statements = StatementPool.new @connection,
218
- self.class.type_cast_config_to_integer(config[:statement_limit])
219
-
220
- if postgresql_version < 90100
221
- raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
222
- end
223
-
224
254
  add_pg_decoders
225
255
 
226
256
  @type_map = Type::HashLookupTypeMap.new
@@ -229,15 +259,10 @@ module ActiveRecord
229
259
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
230
260
  end
231
261
 
232
- # Clears the prepared statements cache.
233
- def clear_cache!
234
- @lock.synchronize do
235
- @statements.clear
236
- end
237
- end
238
-
239
- def truncate(table_name, name = nil)
240
- 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
241
266
  end
242
267
 
243
268
  # Is this connection alive and ready for queries?
@@ -256,6 +281,8 @@ module ActiveRecord
256
281
  super
257
282
  @connection.reset
258
283
  configure_connection
284
+ rescue PG::ConnectionBad
285
+ connect
259
286
  end
260
287
  end
261
288
 
@@ -281,6 +308,7 @@ module ActiveRecord
281
308
  end
282
309
 
283
310
  def discard! # :nodoc:
311
+ super
284
312
  @connection.socket_io.reopen(IO::NULL) rescue nil
285
313
  @connection = nil
286
314
  end
@@ -310,20 +338,35 @@ module ActiveRecord
310
338
  end
311
339
 
312
340
  def supports_ranges?
313
- # Range datatypes weren't introduced until PostgreSQL 9.2
314
- postgresql_version >= 90200
341
+ true
315
342
  end
343
+ deprecate :supports_ranges?
316
344
 
317
345
  def supports_materialized_views?
318
- postgresql_version >= 90300
346
+ true
319
347
  end
320
348
 
321
349
  def supports_foreign_tables?
322
- postgresql_version >= 90300
350
+ true
323
351
  end
324
352
 
325
353
  def supports_pgcrypto_uuid?
326
- 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_common_table_expressions?
365
+ true
366
+ end
367
+
368
+ def supports_lazy_transactions?
369
+ true
327
370
  end
328
371
 
329
372
  def get_advisory_lock(lock_id) # :nodoc:
@@ -352,9 +395,12 @@ module ActiveRecord
352
395
  }
353
396
  end
354
397
 
398
+ def extension_available?(name)
399
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
400
+ end
401
+
355
402
  def extension_enabled?(name)
356
- res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
357
- res.cast_values.first
403
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
358
404
  end
359
405
 
360
406
  def extensions
@@ -365,8 +411,6 @@ module ActiveRecord
365
411
  def max_identifier_length
366
412
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
367
413
  end
368
- alias table_alias_length max_identifier_length
369
- alias index_name_length max_identifier_length
370
414
 
371
415
  # Set the authorized user for this session
372
416
  def session_auth=(user)
@@ -389,15 +433,37 @@ module ActiveRecord
389
433
  }
390
434
 
391
435
  # Returns the version of the connected PostgreSQL server.
392
- def postgresql_version
436
+ def get_database_version # :nodoc:
393
437
  @connection.server_version
394
438
  end
439
+ alias :postgresql_version :database_version
395
440
 
396
441
  def default_index_type?(index) # :nodoc:
397
442
  index.using == :btree || super
398
443
  end
399
444
 
445
+ def build_insert_sql(insert) # :nodoc:
446
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
447
+
448
+ if insert.skip_duplicates?
449
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
450
+ elsif insert.update_duplicates?
451
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
452
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
453
+ end
454
+
455
+ sql << " RETURNING #{insert.returning}" if insert.returning
456
+ sql
457
+ end
458
+
459
+ def check_version # :nodoc:
460
+ if database_version < 90300
461
+ raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
462
+ end
463
+ end
464
+
400
465
  private
466
+
401
467
  # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
402
468
  VALUE_LIMIT_VIOLATION = "22001"
403
469
  NUMERIC_VALUE_OUT_OF_RANGE = "22003"
@@ -409,34 +475,34 @@ module ActiveRecord
409
475
  LOCK_NOT_AVAILABLE = "55P03"
410
476
  QUERY_CANCELED = "57014"
411
477
 
412
- def translate_exception(exception, message)
478
+ def translate_exception(exception, message:, sql:, binds:)
413
479
  return exception unless exception.respond_to?(:result)
414
480
 
415
481
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
416
482
  when UNIQUE_VIOLATION
417
- RecordNotUnique.new(message)
483
+ RecordNotUnique.new(message, sql: sql, binds: binds)
418
484
  when FOREIGN_KEY_VIOLATION
419
- InvalidForeignKey.new(message)
485
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
420
486
  when VALUE_LIMIT_VIOLATION
421
- ValueTooLong.new(message)
487
+ ValueTooLong.new(message, sql: sql, binds: binds)
422
488
  when NUMERIC_VALUE_OUT_OF_RANGE
423
- RangeError.new(message)
489
+ RangeError.new(message, sql: sql, binds: binds)
424
490
  when NOT_NULL_VIOLATION
425
- NotNullViolation.new(message)
491
+ NotNullViolation.new(message, sql: sql, binds: binds)
426
492
  when SERIALIZATION_FAILURE
427
- SerializationFailure.new(message)
493
+ SerializationFailure.new(message, sql: sql, binds: binds)
428
494
  when DEADLOCK_DETECTED
429
- Deadlocked.new(message)
495
+ Deadlocked.new(message, sql: sql, binds: binds)
430
496
  when LOCK_NOT_AVAILABLE
431
- LockWaitTimeout.new(message)
497
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
432
498
  when QUERY_CANCELED
433
- QueryCanceled.new(message)
499
+ QueryCanceled.new(message, sql: sql, binds: binds)
434
500
  else
435
501
  super
436
502
  end
437
503
  end
438
504
 
439
- def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
505
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
440
506
  if !type_map.key?(oid)
441
507
  load_additional_types([oid])
442
508
  end
@@ -525,13 +591,13 @@ module ActiveRecord
525
591
  # Quoted types
526
592
  when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
527
593
  # The default 'now'::date is CURRENT_DATE
528
- if $1 == "now".freeze && $2 == "date".freeze
594
+ if $1 == "now" && $2 == "date"
529
595
  nil
530
596
  else
531
- $1.gsub("''".freeze, "'".freeze)
597
+ $1.gsub("''", "'")
532
598
  end
533
599
  # Boolean types
534
- when "true".freeze, "false".freeze
600
+ when "true", "false"
535
601
  default
536
602
  # Numeric types
537
603
  when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
@@ -557,18 +623,11 @@ module ActiveRecord
557
623
  def load_additional_types(oids = nil)
558
624
  initializer = OID::TypeMapInitializer.new(type_map)
559
625
 
560
- if supports_ranges?
561
- query = <<-SQL
562
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
563
- FROM pg_type as t
564
- LEFT JOIN pg_range as r ON oid = rngtypid
565
- SQL
566
- else
567
- query = <<-SQL
568
- SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
569
- FROM pg_type as t
570
- SQL
571
- end
626
+ query = <<~SQL
627
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
628
+ FROM pg_type as t
629
+ LEFT JOIN pg_range as r ON oid = rngtypid
630
+ SQL
572
631
 
573
632
  if oids
574
633
  query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
@@ -584,6 +643,10 @@ module ActiveRecord
584
643
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
585
644
 
586
645
  def execute_and_clear(sql, name, binds, prepare: false)
646
+ if preventing_writes? && write_query?(sql)
647
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
648
+ end
649
+
587
650
  if without_prepared_statement?(binds)
588
651
  result = exec_no_cache(sql, name, [])
589
652
  elsif !prepare
@@ -597,16 +660,25 @@ module ActiveRecord
597
660
  end
598
661
 
599
662
  def exec_no_cache(sql, name, binds)
663
+ materialize_transactions
664
+
665
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
666
+ # made since we established the connection
667
+ update_typemap_for_default_timezone
668
+
600
669
  type_casted_binds = type_casted_binds(binds)
601
670
  log(sql, name, binds, type_casted_binds) do
602
671
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
603
- @connection.async_exec(sql, type_casted_binds)
672
+ @connection.exec_params(sql, type_casted_binds)
604
673
  end
605
674
  end
606
675
  end
607
676
 
608
677
  def exec_cache(sql, name, binds)
609
- stmt_key = prepare_statement(sql)
678
+ materialize_transactions
679
+ update_typemap_for_default_timezone
680
+
681
+ stmt_key = prepare_statement(sql, binds)
610
682
  type_casted_binds = type_casted_binds(binds)
611
683
 
612
684
  log(sql, name, binds, type_casted_binds, stmt_key) do
@@ -639,7 +711,7 @@ module ActiveRecord
639
711
  #
640
712
  # Check here for more details:
641
713
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
642
- CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
714
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
643
715
  def is_cached_plan_failure?(e)
644
716
  pgerror = e.cause
645
717
  code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
@@ -660,7 +732,7 @@ module ActiveRecord
660
732
 
661
733
  # Prepare the statement if it hasn't been prepared, return
662
734
  # the statement key.
663
- def prepare_statement(sql)
735
+ def prepare_statement(sql, binds)
664
736
  @lock.synchronize do
665
737
  sql_key = sql_key(sql)
666
738
  unless @statements.key? sql_key
@@ -668,7 +740,7 @@ module ActiveRecord
668
740
  begin
669
741
  @connection.prepare nextkey, sql
670
742
  rescue => e
671
- raise translate_exception_class(e, sql)
743
+ raise translate_exception_class(e, sql, binds)
672
744
  end
673
745
  # Clear the queue
674
746
  @connection.get_last_result
@@ -683,12 +755,8 @@ module ActiveRecord
683
755
  def connect
684
756
  @connection = PG.connect(@connection_parameters)
685
757
  configure_connection
686
- rescue ::PG::Error => error
687
- if error.message.include?("does not exist")
688
- raise ActiveRecord::NoDatabaseError
689
- else
690
- raise
691
- end
758
+ add_pg_encoders
759
+ add_pg_decoders
692
760
  end
693
761
 
694
762
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -746,7 +814,7 @@ module ActiveRecord
746
814
  # - format_type includes the column size constraint, e.g. varchar(50)
747
815
  # - ::regclass is a function that gives the id for a table name
748
816
  def column_definitions(table_name)
749
- query(<<-end_sql, "SCHEMA")
817
+ query(<<~SQL, "SCHEMA")
750
818
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
751
819
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
752
820
  c.collname, col_description(a.attrelid, a.attnum) AS comment
@@ -757,7 +825,7 @@ module ActiveRecord
757
825
  WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
758
826
  AND a.attnum > 0 AND NOT a.attisdropped
759
827
  ORDER BY a.attnum
760
- end_sql
828
+ SQL
761
829
  end
762
830
 
763
831
  def extract_table_ref_from_insert_sql(sql)
@@ -769,10 +837,14 @@ module ActiveRecord
769
837
  Arel::Visitors::PostgreSQL.new(self)
770
838
  end
771
839
 
840
+ def build_statement_pool
841
+ StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
842
+ end
843
+
772
844
  def can_perform_case_insensitive_comparison_for?(column)
773
845
  @case_insensitive_cache ||= {}
774
846
  @case_insensitive_cache[column.sql_type] ||= begin
775
- sql = <<-end_sql
847
+ sql = <<~SQL
776
848
  SELECT exists(
777
849
  SELECT * FROM pg_proc
778
850
  WHERE proname = 'lower'
@@ -784,7 +856,7 @@ module ActiveRecord
784
856
  WHERE proname = 'lower'
785
857
  AND castsource = #{quote column.sql_type}::regtype
786
858
  )
787
- end_sql
859
+ SQL
788
860
  execute_and_clear(sql, "SCHEMA", []) do |result|
789
861
  result.getvalue(0, 0)
790
862
  end
@@ -799,7 +871,22 @@ module ActiveRecord
799
871
  @connection.type_map_for_queries = map
800
872
  end
801
873
 
874
+ def update_typemap_for_default_timezone
875
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
876
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
877
+ PG::TextDecoder::TimestampUtc :
878
+ PG::TextDecoder::TimestampWithoutTimeZone
879
+
880
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
881
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
882
+ @default_timezone = ActiveRecord::Base.default_timezone
883
+ end
884
+ end
885
+
802
886
  def add_pg_decoders
887
+ @default_timezone = nil
888
+ @timestamp_decoder = nil
889
+
803
890
  coders_by_name = {
804
891
  "int2" => PG::TextDecoder::Integer,
805
892
  "int4" => PG::TextDecoder::Integer,
@@ -809,8 +896,15 @@ module ActiveRecord
809
896
  "float8" => PG::TextDecoder::Float,
810
897
  "bool" => PG::TextDecoder::Boolean,
811
898
  }
899
+
900
+ if defined?(PG::TextDecoder::TimestampUtc)
901
+ # Use native PG encoders available since pg-1.1
902
+ coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
903
+ coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
904
+ end
905
+
812
906
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
813
- query = <<-SQL % known_coder_types.join(", ")
907
+ query = <<~SQL % known_coder_types.join(", ")
814
908
  SELECT t.oid, t.typname
815
909
  FROM pg_type as t
816
910
  WHERE t.typname IN (%s)
@@ -824,6 +918,10 @@ module ActiveRecord
824
918
  map = PG::TypeMapByOid.new
825
919
  coders.each { |coder| map.add_coder(coder) }
826
920
  @connection.type_map_for_results = map
921
+
922
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
923
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
924
+ update_typemap_for_default_timezone
827
925
  end
828
926
 
829
927
  def construct_coder(row, coder_class)