activerecord 5.2.4.4 → 6.0.3.4

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +777 -552
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +10 -2
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +4 -3
  9. data/lib/active_record/association_relation.rb +10 -8
  10. data/lib/active_record/associations.rb +21 -16
  11. data/lib/active_record/associations/alias_tracker.rb +0 -1
  12. data/lib/active_record/associations/association.rb +56 -19
  13. data/lib/active_record/associations/association_scope.rb +4 -6
  14. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  16. data/lib/active_record/associations/builder/association.rb +14 -18
  17. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  18. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +35 -1
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +12 -23
  24. data/lib/active_record/associations/collection_proxy.rb +13 -17
  25. data/lib/active_record/associations/foreign_association.rb +7 -0
  26. data/lib/active_record/associations/has_many_association.rb +2 -11
  27. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  28. data/lib/active_record/associations/has_one_association.rb +28 -30
  29. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +37 -28
  31. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  33. data/lib/active_record/associations/preloader.rb +39 -32
  34. data/lib/active_record/associations/preloader/association.rb +38 -36
  35. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  36. data/lib/active_record/associations/singular_association.rb +2 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods.rb +28 -100
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  41. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  42. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  43. data/lib/active_record/attribute_methods/query.rb +2 -3
  44. data/lib/active_record/attribute_methods/read.rb +15 -54
  45. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  47. data/lib/active_record/attribute_methods/write.rb +17 -25
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +3 -5
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +0 -1
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +103 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +100 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +191 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +142 -215
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +132 -16
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +135 -146
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +103 -61
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations.rb +233 -0
  112. data/lib/active_record/database_configurations/database_config.rb +37 -0
  113. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  114. data/lib/active_record/database_configurations/url_config.rb +78 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +37 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +5 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector.rb +74 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  135. data/lib/active_record/migration.rb +104 -85
  136. data/lib/active_record/migration/command_recorder.rb +54 -22
  137. data/lib/active_record/migration/compatibility.rb +79 -52
  138. data/lib/active_record/migration/join_table.rb +0 -1
  139. data/lib/active_record/model_schema.rb +33 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -43
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +40 -38
  151. data/lib/active_record/relation.rb +322 -80
  152. data/lib/active_record/relation/batches.rb +13 -11
  153. data/lib/active_record/relation/calculations.rb +54 -48
  154. data/lib/active_record/relation/delegation.rb +33 -49
  155. data/lib/active_record/relation/finder_methods.rb +23 -28
  156. data/lib/active_record/relation/from_clause.rb +4 -0
  157. data/lib/active_record/relation/merger.rb +11 -21
  158. data/lib/active_record/relation/predicate_builder.rb +5 -11
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  160. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  164. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  165. data/lib/active_record/relation/query_attribute.rb +13 -8
  166. data/lib/active_record/relation/query_methods.rb +221 -70
  167. data/lib/active_record/relation/spawn_methods.rb +1 -2
  168. data/lib/active_record/relation/where_clause.rb +14 -11
  169. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping.rb +8 -9
  176. data/lib/active_record/scoping/default.rb +4 -6
  177. data/lib/active_record/scoping/named.rb +21 -17
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +225 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type.rb +3 -5
  193. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  194. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  195. data/lib/active_record/type/serialized.rb +0 -1
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type_caster/connection.rb +15 -14
  199. data/lib/active_record/type_caster/map.rb +1 -4
  200. data/lib/active_record/validations.rb +3 -3
  201. data/lib/active_record/validations/associated.rb +1 -2
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/arel.rb +62 -0
  204. data/lib/arel/alias_predication.rb +9 -0
  205. data/lib/arel/attributes.rb +22 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/collectors/bind.rb +24 -0
  208. data/lib/arel/collectors/composite.rb +31 -0
  209. data/lib/arel/collectors/plain_string.rb +20 -0
  210. data/lib/arel/collectors/sql_string.rb +20 -0
  211. data/lib/arel/collectors/substitute_binds.rb +28 -0
  212. data/lib/arel/crud.rb +42 -0
  213. data/lib/arel/delete_manager.rb +18 -0
  214. data/lib/arel/errors.rb +9 -0
  215. data/lib/arel/expressions.rb +29 -0
  216. data/lib/arel/factory_methods.rb +49 -0
  217. data/lib/arel/insert_manager.rb +49 -0
  218. data/lib/arel/math.rb +45 -0
  219. data/lib/arel/nodes.rb +68 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/order_predications.rb +13 -0
  264. data/lib/arel/predications.rb +256 -0
  265. data/lib/arel/select_manager.rb +271 -0
  266. data/lib/arel/table.rb +110 -0
  267. data/lib/arel/tree_manager.rb +72 -0
  268. data/lib/arel/update_manager.rb +34 -0
  269. data/lib/arel/visitors.rb +20 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/window_predications.rb +9 -0
  284. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  285. data/lib/rails/generators/active_record/migration.rb +14 -2
  286. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  287. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  288. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  289. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  290. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  291. metadata +115 -29
  292. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module PostgreSQL
6
6
  class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
7
7
  private
8
-
9
8
  def extensions(stream)
10
9
  extensions = @connection.extensions
11
10
  if extensions.any?
@@ -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,13 +68,13 @@ 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
75
75
  INNER JOIN pg_class i ON d.indexrelid = i.oid
76
76
  LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
77
- WHERE i.relkind = 'i'
77
+ WHERE i.relkind IN ('i', 'I')
78
78
  AND i.relname = #{index[:name]}
79
79
  AND t.relname = #{table[:name]}
80
80
  AND n.nspname = #{index[:schema]}
@@ -85,14 +85,14 @@ 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
92
92
  INNER JOIN pg_index d ON t.oid = d.indrelid
93
93
  INNER JOIN pg_class i ON d.indexrelid = i.oid
94
94
  LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
95
- WHERE i.relkind = 'i'
95
+ WHERE i.relkind IN ('i', 'I')
96
96
  AND d.indisprimary = 'f'
97
97
  AND t.relname = #{scope[:name]}
98
98
  AND n.nspname = #{scope[:schema]}
@@ -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.
@@ -415,7 +390,7 @@ module ActiveRecord
415
390
  rename_table_indexes(table_name, new_name)
416
391
  end
417
392
 
418
- def add_column(table_name, column_name, type, options = {}) #:nodoc:
393
+ def add_column(table_name, column_name, type, **options) #:nodoc:
419
394
  clear_cache!
420
395
  super
421
396
  change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
@@ -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
 
@@ -462,7 +439,7 @@ module ActiveRecord
462
439
  end
463
440
 
464
441
  def add_index(table_name, column_name, options = {}) #:nodoc:
465
- index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
442
+ index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, **options)
466
443
  execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns_and_opclasses})#{index_options}").tap do
467
444
  execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
468
445
  end
@@ -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
@@ -576,12 +553,12 @@ module ActiveRecord
576
553
  # requires that the ORDER BY include the distinct column.
577
554
  def columns_for_distinct(columns, orders) #:nodoc:
578
555
  order_columns = orders.reject(&:blank?).map { |s|
579
- # Convert Arel node to string
580
- s = s.to_sql unless s.is_a?(String)
581
- # Remove any ASC/DESC modifiers
582
- s.gsub(/\s+(?:ASC|DESC)\b/i, "")
583
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
584
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
556
+ # Convert Arel node to string
557
+ s = visitor.compile(s) unless s.is_a?(String)
558
+ # Remove any ASC/DESC modifiers
559
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
560
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
561
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
585
562
 
586
563
  (order_columns << super).join(", ")
587
564
  end
@@ -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
@@ -636,8 +613,8 @@ module ActiveRecord
636
613
  PostgreSQL::SchemaCreation.new(self)
637
614
  end
638
615
 
639
- def create_table_definition(*args)
640
- PostgreSQL::TableDefinition.new(*args)
616
+ def create_table_definition(*args, **options)
617
+ PostgreSQL::TableDefinition.new(self, *args, **options)
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,14 +679,14 @@ module ActiveRecord
683
679
  end
684
680
  end
685
681
 
686
- def add_column_for_alter(table_name, column_name, type, options = {})
682
+ def add_column_for_alter(table_name, column_name, type, **options)
687
683
  return super unless options.key?(:comment)
688
684
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
689
685
  end
690
686
 
691
687
  def change_column_for_alter(table_name, column_name, type, options = {})
692
688
  td = create_table_definition(table_name)
693
- cd = td.new_column_definition(column_name, type, options)
689
+ cd = td.new_column_definition(column_name, type, **options)
694
690
  sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
695
691
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
696
692
  sqls
@@ -715,14 +711,6 @@ module ActiveRecord
715
711
  "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
716
712
  end
717
713
 
718
- def add_timestamps_for_alter(table_name, options = {})
719
- [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
720
- end
721
-
722
- def remove_timestamps_for_alter(table_name, options = {})
723
- [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
724
- end
725
-
726
714
  def add_index_opclass(quoted_columns, **options)
727
715
  opclasses = options_for_index_columns(options[:opclass])
728
716
  quoted_columns.each do |name, column|
@@ -731,7 +719,7 @@ module ActiveRecord
731
719
  end
732
720
 
733
721
  def add_options_for_index_columns(quoted_columns, **options)
734
- quoted_columns = add_index_opclass(quoted_columns, options)
722
+ quoted_columns = add_index_opclass(quoted_columns, **options)
735
723
  super
736
724
  end
737
725
 
@@ -739,7 +727,7 @@ module ActiveRecord
739
727
  scope = quoted_scope(name, type: type)
740
728
  scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
741
729
 
742
- sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
730
+ sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
743
731
  sql << " WHERE n.nspname = #{scope[:schema]}"
744
732
  sql << " AND c.relname = #{scope[:name]}" if scope[:name]
745
733
  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
@@ -37,7 +37,6 @@ module ActiveRecord
37
37
  end
38
38
 
39
39
  protected
40
-
41
40
  def parts
42
41
  @parts ||= [@schema, @identifier].compact
43
42
  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",
@@ -138,6 +156,10 @@ module ActiveRecord
138
156
  true
139
157
  end
140
158
 
159
+ def supports_partitioned_indexes?
160
+ database_version >= 110_000
161
+ end
162
+
141
163
  def supports_partial_index?
142
164
  true
143
165
  end
@@ -167,7 +189,7 @@ module ActiveRecord
167
189
  end
168
190
 
169
191
  def supports_json?
170
- postgresql_version >= 90200
192
+ true
171
193
  end
172
194
 
173
195
  def supports_comments?
@@ -178,6 +200,17 @@ module ActiveRecord
178
200
  true
179
201
  end
180
202
 
203
+ def supports_insert_returning?
204
+ true
205
+ end
206
+
207
+ def supports_insert_on_conflict?
208
+ database_version >= 90500
209
+ end
210
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
211
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
212
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
213
+
181
214
  def index_algorithms
182
215
  { concurrently: "CONCURRENTLY" }
183
216
  end
@@ -220,15 +253,8 @@ module ActiveRecord
220
253
  @local_tz = nil
221
254
  @max_identifier_length = nil
222
255
 
223
- connect
256
+ configure_connection
224
257
  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
258
  add_pg_decoders
233
259
 
234
260
  @type_map = Type::HashLookupTypeMap.new
@@ -237,15 +263,10 @@ module ActiveRecord
237
263
  @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
238
264
  end
239
265
 
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, []
266
+ def self.database_exists?(config)
267
+ !!ActiveRecord::Base.postgresql_connection(config)
268
+ rescue ActiveRecord::NoDatabaseError
269
+ false
249
270
  end
250
271
 
251
272
  # Is this connection alive and ready for queries?
@@ -264,6 +285,8 @@ module ActiveRecord
264
285
  super
265
286
  @connection.reset
266
287
  configure_connection
288
+ rescue PG::ConnectionBad
289
+ connect
267
290
  end
268
291
  end
269
292
 
@@ -289,6 +312,7 @@ module ActiveRecord
289
312
  end
290
313
 
291
314
  def discard! # :nodoc:
315
+ super
292
316
  @connection.socket_io.reopen(IO::NULL) rescue nil
293
317
  @connection = nil
294
318
  end
@@ -318,20 +342,35 @@ module ActiveRecord
318
342
  end
319
343
 
320
344
  def supports_ranges?
321
- # Range datatypes weren't introduced until PostgreSQL 9.2
322
- postgresql_version >= 90200
345
+ true
323
346
  end
347
+ deprecate :supports_ranges?
324
348
 
325
349
  def supports_materialized_views?
326
- postgresql_version >= 90300
350
+ true
327
351
  end
328
352
 
329
353
  def supports_foreign_tables?
330
- postgresql_version >= 90300
354
+ true
331
355
  end
332
356
 
333
357
  def supports_pgcrypto_uuid?
334
- postgresql_version >= 90400
358
+ database_version >= 90400
359
+ end
360
+
361
+ def supports_optimizer_hints?
362
+ unless defined?(@has_pg_hint_plan)
363
+ @has_pg_hint_plan = extension_available?("pg_hint_plan")
364
+ end
365
+ @has_pg_hint_plan
366
+ end
367
+
368
+ def supports_common_table_expressions?
369
+ true
370
+ end
371
+
372
+ def supports_lazy_transactions?
373
+ true
335
374
  end
336
375
 
337
376
  def get_advisory_lock(lock_id) # :nodoc:
@@ -360,9 +399,12 @@ module ActiveRecord
360
399
  }
361
400
  end
362
401
 
402
+ def extension_available?(name)
403
+ query_value("SELECT true FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
404
+ end
405
+
363
406
  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
407
+ query_value("SELECT installed_version IS NOT NULL FROM pg_available_extensions WHERE name = #{quote(name)}", "SCHEMA")
366
408
  end
367
409
 
368
410
  def extensions
@@ -373,8 +415,6 @@ module ActiveRecord
373
415
  def max_identifier_length
374
416
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
375
417
  end
376
- alias table_alias_length max_identifier_length
377
- alias index_name_length max_identifier_length
378
418
 
379
419
  # Set the authorized user for this session
380
420
  def session_auth=(user)
@@ -397,14 +437,35 @@ module ActiveRecord
397
437
  }
398
438
 
399
439
  # Returns the version of the connected PostgreSQL server.
400
- def postgresql_version
440
+ def get_database_version # :nodoc:
401
441
  @connection.server_version
402
442
  end
443
+ alias :postgresql_version :database_version
403
444
 
404
445
  def default_index_type?(index) # :nodoc:
405
446
  index.using == :btree || super
406
447
  end
407
448
 
449
+ def build_insert_sql(insert) # :nodoc:
450
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
451
+
452
+ if insert.skip_duplicates?
453
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
454
+ elsif insert.update_duplicates?
455
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
456
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
457
+ end
458
+
459
+ sql << " RETURNING #{insert.returning}" if insert.returning
460
+ sql
461
+ end
462
+
463
+ def check_version # :nodoc:
464
+ if database_version < 90300
465
+ raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
466
+ end
467
+ end
468
+
408
469
  private
409
470
  # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
410
471
  VALUE_LIMIT_VIOLATION = "22001"
@@ -417,34 +478,34 @@ module ActiveRecord
417
478
  LOCK_NOT_AVAILABLE = "55P03"
418
479
  QUERY_CANCELED = "57014"
419
480
 
420
- def translate_exception(exception, message)
481
+ def translate_exception(exception, message:, sql:, binds:)
421
482
  return exception unless exception.respond_to?(:result)
422
483
 
423
484
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
424
485
  when UNIQUE_VIOLATION
425
- RecordNotUnique.new(message)
486
+ RecordNotUnique.new(message, sql: sql, binds: binds)
426
487
  when FOREIGN_KEY_VIOLATION
427
- InvalidForeignKey.new(message)
488
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
428
489
  when VALUE_LIMIT_VIOLATION
429
- ValueTooLong.new(message)
490
+ ValueTooLong.new(message, sql: sql, binds: binds)
430
491
  when NUMERIC_VALUE_OUT_OF_RANGE
431
- RangeError.new(message)
492
+ RangeError.new(message, sql: sql, binds: binds)
432
493
  when NOT_NULL_VIOLATION
433
- NotNullViolation.new(message)
494
+ NotNullViolation.new(message, sql: sql, binds: binds)
434
495
  when SERIALIZATION_FAILURE
435
- SerializationFailure.new(message)
496
+ SerializationFailure.new(message, sql: sql, binds: binds)
436
497
  when DEADLOCK_DETECTED
437
- Deadlocked.new(message)
498
+ Deadlocked.new(message, sql: sql, binds: binds)
438
499
  when LOCK_NOT_AVAILABLE
439
- LockWaitTimeout.new(message)
500
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
440
501
  when QUERY_CANCELED
441
- QueryCanceled.new(message)
502
+ QueryCanceled.new(message, sql: sql, binds: binds)
442
503
  else
443
504
  super
444
505
  end
445
506
  end
446
507
 
447
- def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
508
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
448
509
  if !type_map.key?(oid)
449
510
  load_additional_types([oid])
450
511
  end
@@ -533,13 +594,13 @@ module ActiveRecord
533
594
  # Quoted types
534
595
  when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
535
596
  # The default 'now'::date is CURRENT_DATE
536
- if $1 == "now".freeze && $2 == "date".freeze
597
+ if $1 == "now" && $2 == "date"
537
598
  nil
538
599
  else
539
- $1.gsub("''".freeze, "'".freeze)
600
+ $1.gsub("''", "'")
540
601
  end
541
602
  # Boolean types
542
- when "true".freeze, "false".freeze
603
+ when "true", "false"
543
604
  default
544
605
  # Numeric types
545
606
  when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
@@ -565,21 +626,14 @@ module ActiveRecord
565
626
  def load_additional_types(oids = nil)
566
627
  initializer = OID::TypeMapInitializer.new(type_map)
567
628
 
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
629
+ query = <<~SQL
630
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
631
+ FROM pg_type as t
632
+ LEFT JOIN pg_range as r ON oid = rngtypid
633
+ SQL
580
634
 
581
635
  if oids
582
- query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
636
+ query += "WHERE t.oid IN (%s)" % oids.join(", ")
583
637
  else
584
638
  query += initializer.query_conditions_for_initial_load
585
639
  end
@@ -592,6 +646,10 @@ module ActiveRecord
592
646
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
593
647
 
594
648
  def execute_and_clear(sql, name, binds, prepare: false)
649
+ if preventing_writes? && write_query?(sql)
650
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
651
+ end
652
+
595
653
  if without_prepared_statement?(binds)
596
654
  result = exec_no_cache(sql, name, [])
597
655
  elsif !prepare
@@ -605,6 +663,12 @@ module ActiveRecord
605
663
  end
606
664
 
607
665
  def exec_no_cache(sql, name, binds)
666
+ materialize_transactions
667
+
668
+ # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
669
+ # made since we established the connection
670
+ update_typemap_for_default_timezone
671
+
608
672
  type_casted_binds = type_casted_binds(binds)
609
673
  log(sql, name, binds, type_casted_binds) do
610
674
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -614,7 +678,10 @@ module ActiveRecord
614
678
  end
615
679
 
616
680
  def exec_cache(sql, name, binds)
617
- stmt_key = prepare_statement(sql)
681
+ materialize_transactions
682
+ update_typemap_for_default_timezone
683
+
684
+ stmt_key = prepare_statement(sql, binds)
618
685
  type_casted_binds = type_casted_binds(binds)
619
686
 
620
687
  log(sql, name, binds, type_casted_binds, stmt_key) do
@@ -647,7 +714,7 @@ module ActiveRecord
647
714
  #
648
715
  # Check here for more details:
649
716
  # 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
717
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
651
718
  def is_cached_plan_failure?(e)
652
719
  pgerror = e.cause
653
720
  code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
@@ -668,7 +735,7 @@ module ActiveRecord
668
735
 
669
736
  # Prepare the statement if it hasn't been prepared, return
670
737
  # the statement key.
671
- def prepare_statement(sql)
738
+ def prepare_statement(sql, binds)
672
739
  @lock.synchronize do
673
740
  sql_key = sql_key(sql)
674
741
  unless @statements.key? sql_key
@@ -676,7 +743,7 @@ module ActiveRecord
676
743
  begin
677
744
  @connection.prepare nextkey, sql
678
745
  rescue => e
679
- raise translate_exception_class(e, sql)
746
+ raise translate_exception_class(e, sql, binds)
680
747
  end
681
748
  # Clear the queue
682
749
  @connection.get_last_result
@@ -691,12 +758,8 @@ module ActiveRecord
691
758
  def connect
692
759
  @connection = PG.connect(@connection_parameters)
693
760
  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
761
+ add_pg_encoders
762
+ add_pg_decoders
700
763
  end
701
764
 
702
765
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -754,7 +817,7 @@ module ActiveRecord
754
817
  # - format_type includes the column size constraint, e.g. varchar(50)
755
818
  # - ::regclass is a function that gives the id for a table name
756
819
  def column_definitions(table_name)
757
- query(<<-end_sql, "SCHEMA")
820
+ query(<<~SQL, "SCHEMA")
758
821
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
759
822
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
760
823
  c.collname, col_description(a.attrelid, a.attnum) AS comment
@@ -765,7 +828,7 @@ module ActiveRecord
765
828
  WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
766
829
  AND a.attnum > 0 AND NOT a.attisdropped
767
830
  ORDER BY a.attnum
768
- end_sql
831
+ SQL
769
832
  end
770
833
 
771
834
  def extract_table_ref_from_insert_sql(sql)
@@ -777,10 +840,14 @@ module ActiveRecord
777
840
  Arel::Visitors::PostgreSQL.new(self)
778
841
  end
779
842
 
843
+ def build_statement_pool
844
+ StatementPool.new(@connection, self.class.type_cast_config_to_integer(@config[:statement_limit]))
845
+ end
846
+
780
847
  def can_perform_case_insensitive_comparison_for?(column)
781
848
  @case_insensitive_cache ||= {}
782
849
  @case_insensitive_cache[column.sql_type] ||= begin
783
- sql = <<-end_sql
850
+ sql = <<~SQL
784
851
  SELECT exists(
785
852
  SELECT * FROM pg_proc
786
853
  WHERE proname = 'lower'
@@ -792,7 +859,7 @@ module ActiveRecord
792
859
  WHERE proname = 'lower'
793
860
  AND castsource = #{quote column.sql_type}::regtype
794
861
  )
795
- end_sql
862
+ SQL
796
863
  execute_and_clear(sql, "SCHEMA", []) do |result|
797
864
  result.getvalue(0, 0)
798
865
  end
@@ -807,7 +874,22 @@ module ActiveRecord
807
874
  @connection.type_map_for_queries = map
808
875
  end
809
876
 
877
+ def update_typemap_for_default_timezone
878
+ if @default_timezone != ActiveRecord::Base.default_timezone && @timestamp_decoder
879
+ decoder_class = ActiveRecord::Base.default_timezone == :utc ?
880
+ PG::TextDecoder::TimestampUtc :
881
+ PG::TextDecoder::TimestampWithoutTimeZone
882
+
883
+ @timestamp_decoder = decoder_class.new(@timestamp_decoder.to_h)
884
+ @connection.type_map_for_results.add_coder(@timestamp_decoder)
885
+ @default_timezone = ActiveRecord::Base.default_timezone
886
+ end
887
+ end
888
+
810
889
  def add_pg_decoders
890
+ @default_timezone = nil
891
+ @timestamp_decoder = nil
892
+
811
893
  coders_by_name = {
812
894
  "int2" => PG::TextDecoder::Integer,
813
895
  "int4" => PG::TextDecoder::Integer,
@@ -817,8 +899,15 @@ module ActiveRecord
817
899
  "float8" => PG::TextDecoder::Float,
818
900
  "bool" => PG::TextDecoder::Boolean,
819
901
  }
902
+
903
+ if defined?(PG::TextDecoder::TimestampUtc)
904
+ # Use native PG encoders available since pg-1.1
905
+ coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
906
+ coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
907
+ end
908
+
820
909
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
821
- query = <<-SQL % known_coder_types.join(", ")
910
+ query = <<~SQL % known_coder_types.join(", ")
822
911
  SELECT t.oid, t.typname
823
912
  FROM pg_type as t
824
913
  WHERE t.typname IN (%s)
@@ -832,6 +921,10 @@ module ActiveRecord
832
921
  map = PG::TypeMapByOid.new
833
922
  coders.each { |coder| map.add_coder(coder) }
834
923
  @connection.type_map_for_results = map
924
+
925
+ # extract timestamp decoder for use in update_typemap_for_default_timezone
926
+ @timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
927
+ update_typemap_for_default_timezone
835
928
  end
836
929
 
837
930
  def construct_coder(row, coder_class)