activerecord 5.2.6 → 6.0.5

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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +928 -559
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/advisory_lock_base.rb +18 -0
  7. data/lib/active_record/aggregations.rb +4 -3
  8. data/lib/active_record/association_relation.rb +10 -8
  9. data/lib/active_record/associations/alias_tracker.rb +0 -1
  10. data/lib/active_record/associations/association.rb +55 -19
  11. data/lib/active_record/associations/association_scope.rb +11 -7
  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 +3 -13
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  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 +19 -23
  22. data/lib/active_record/associations/collection_proxy.rb +14 -17
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +2 -11
  25. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  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/join_association.rb +16 -10
  29. data/lib/active_record/associations/join_dependency/join_part.rb +4 -4
  30. data/lib/active_record/associations/join_dependency.rb +47 -30
  31. data/lib/active_record/associations/preloader/association.rb +61 -41
  32. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  33. data/lib/active_record/associations/preloader.rb +44 -33
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/associations/through_association.rb +1 -1
  36. data/lib/active_record/associations.rb +21 -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/before_type_cast.rb +4 -2
  40. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  41. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  42. data/lib/active_record/attribute_methods/query.rb +2 -3
  43. data/lib/active_record/attribute_methods/read.rb +15 -54
  44. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  46. data/lib/active_record/attribute_methods/write.rb +17 -25
  47. data/lib/active_record/attribute_methods.rb +28 -100
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +12 -14
  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 +109 -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 +105 -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 +197 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -217
  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 +139 -19
  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/date_time.rb +8 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -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 +25 -7
  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 +137 -147
  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 +107 -66
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations/database_config.rb +37 -0
  112. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  113. data/lib/active_record/database_configurations/url_config.rb +78 -0
  114. data/lib/active_record/database_configurations.rb +233 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +44 -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 +14 -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/resolver/session.rb +45 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector.rb +74 -0
  135. data/lib/active_record/migration/command_recorder.rb +54 -22
  136. data/lib/active_record/migration/compatibility.rb +79 -52
  137. data/lib/active_record/migration/join_table.rb +0 -1
  138. data/lib/active_record/migration.rb +104 -85
  139. data/lib/active_record/model_schema.rb +62 -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 +51 -51
  151. data/lib/active_record/relation/batches.rb +13 -11
  152. data/lib/active_record/relation/calculations.rb +55 -49
  153. data/lib/active_record/relation/delegation.rb +35 -50
  154. data/lib/active_record/relation/finder_methods.rb +23 -28
  155. data/lib/active_record/relation/from_clause.rb +4 -0
  156. data/lib/active_record/relation/merger.rb +12 -17
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  158. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  159. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  160. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  161. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  162. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  163. data/lib/active_record/relation/predicate_builder.rb +5 -11
  164. data/lib/active_record/relation/query_attribute.rb +13 -8
  165. data/lib/active_record/relation/query_methods.rb +232 -69
  166. data/lib/active_record/relation/spawn_methods.rb +1 -2
  167. data/lib/active_record/relation/where_clause.rb +14 -11
  168. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  169. data/lib/active_record/relation.rb +326 -81
  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/default.rb +4 -6
  176. data/lib/active_record/scoping/named.rb +25 -16
  177. data/lib/active_record/scoping.rb +8 -9
  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 +243 -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/adapter_specific_registry.rb +3 -13
  193. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  194. data/lib/active_record/type/serialized.rb +0 -1
  195. data/lib/active_record/type/time.rb +10 -0
  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.rb +3 -5
  199. data/lib/active_record/type_caster/connection.rb +15 -14
  200. data/lib/active_record/type_caster/map.rb +1 -4
  201. data/lib/active_record/validations/associated.rb +0 -1
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/active_record/validations.rb +3 -3
  204. data/lib/active_record.rb +10 -2
  205. data/lib/arel/alias_predication.rb +9 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/attributes.rb +22 -0
  208. data/lib/arel/collectors/bind.rb +24 -0
  209. data/lib/arel/collectors/composite.rb +31 -0
  210. data/lib/arel/collectors/plain_string.rb +20 -0
  211. data/lib/arel/collectors/sql_string.rb +20 -0
  212. data/lib/arel/collectors/substitute_binds.rb +28 -0
  213. data/lib/arel/crud.rb +42 -0
  214. data/lib/arel/delete_manager.rb +18 -0
  215. data/lib/arel/errors.rb +9 -0
  216. data/lib/arel/expressions.rb +29 -0
  217. data/lib/arel/factory_methods.rb +49 -0
  218. data/lib/arel/insert_manager.rb +49 -0
  219. data/lib/arel/math.rb +45 -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/nodes.rb +68 -0
  264. data/lib/arel/order_predications.rb +13 -0
  265. data/lib/arel/predications.rb +256 -0
  266. data/lib/arel/select_manager.rb +271 -0
  267. data/lib/arel/table.rb +110 -0
  268. data/lib/arel/tree_manager.rb +72 -0
  269. data/lib/arel/update_manager.rb +34 -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/visitors.rb +20 -0
  284. data/lib/arel/window_predications.rb +9 -0
  285. data/lib/arel.rb +62 -0
  286. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  287. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  288. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  289. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  290. data/lib/rails/generators/active_record/migration.rb +14 -2
  291. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  292. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  293. metadata +116 -29
  294. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -5,7 +5,7 @@ gem "pg", ">= 0.18", "< 2.0"
5
5
  require "pg"
6
6
 
7
7
  # Use async_exec instead of exec_params on pg versions before 1.1
8
- class ::PG::Connection
8
+ class ::PG::Connection # :nodoc:
9
9
  unless self.public_method_defined?(:async_exec_params)
10
10
  remove_method :exec_params
11
11
  alias exec_params async_exec
@@ -43,9 +43,14 @@ module ActiveRecord
43
43
  valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
44
44
  conn_params.slice!(*valid_conn_param_keys)
45
45
 
46
- # The postgres drivers don't allow the creation of an unconnected PG::Connection object,
47
- # so just pass a nil connection object for the time being.
48
- ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
46
+ conn = PG.connect(conn_params)
47
+ ConnectionAdapters::PostgreSQLAdapter.new(conn, logger, conn_params, config)
48
+ rescue ::PG::Error => error
49
+ if error.message.include?(conn_params[:dbname])
50
+ raise ActiveRecord::NoDatabaseError
51
+ else
52
+ raise
53
+ end
49
54
  end
50
55
  end
51
56
 
@@ -78,7 +83,20 @@ module ActiveRecord
78
83
  # In addition, default connection parameters of libpq can be set per environment variables.
79
84
  # See https://www.postgresql.org/docs/current/static/libpq-envars.html .
80
85
  class PostgreSQLAdapter < AbstractAdapter
81
- ADAPTER_NAME = "PostgreSQL".freeze
86
+ ADAPTER_NAME = "PostgreSQL"
87
+
88
+ ##
89
+ # :singleton-method:
90
+ # PostgreSQL allows the creation of "unlogged" tables, which do not record
91
+ # data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
92
+ # but significantly increases the risk of data loss if the database
93
+ # crashes. As a result, this should not be used in production
94
+ # environments. If you would like all created tables to be unlogged in
95
+ # the test environment you can add the following line to your test.rb
96
+ # file:
97
+ #
98
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
99
+ class_attribute :create_unlogged_tables, default: false
82
100
 
83
101
  NATIVE_DATABASE_TYPES = {
84
102
  primary_key: "bigserial primary key",
@@ -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)
@@ -13,6 +13,7 @@ module ActiveRecord
13
13
  @columns_hash = {}
14
14
  @primary_keys = {}
15
15
  @data_sources = {}
16
+ @indexes = {}
16
17
  end
17
18
 
18
19
  def initialize_dup(other)
@@ -21,22 +22,27 @@ module ActiveRecord
21
22
  @columns_hash = @columns_hash.dup
22
23
  @primary_keys = @primary_keys.dup
23
24
  @data_sources = @data_sources.dup
25
+ @indexes = @indexes.dup
24
26
  end
25
27
 
26
28
  def encode_with(coder)
27
- coder["columns"] = @columns
28
- coder["columns_hash"] = @columns_hash
29
- coder["primary_keys"] = @primary_keys
30
- coder["data_sources"] = @data_sources
31
- coder["version"] = connection.migration_context.current_version
29
+ coder["columns"] = @columns
30
+ coder["columns_hash"] = @columns_hash
31
+ coder["primary_keys"] = @primary_keys
32
+ coder["data_sources"] = @data_sources
33
+ coder["indexes"] = @indexes
34
+ coder["version"] = connection.migration_context.current_version
35
+ coder["database_version"] = database_version
32
36
  end
33
37
 
34
38
  def init_with(coder)
35
- @columns = coder["columns"]
36
- @columns_hash = coder["columns_hash"]
37
- @primary_keys = coder["primary_keys"]
38
- @data_sources = coder["data_sources"]
39
- @version = coder["version"]
39
+ @columns = coder["columns"]
40
+ @columns_hash = coder["columns_hash"]
41
+ @primary_keys = coder["primary_keys"]
42
+ @data_sources = coder["data_sources"]
43
+ @indexes = coder["indexes"] || {}
44
+ @version = coder["version"]
45
+ @database_version = coder["database_version"]
40
46
  end
41
47
 
42
48
  def primary_keys(table_name)
@@ -57,6 +63,7 @@ module ActiveRecord
57
63
  primary_keys(table_name)
58
64
  columns(table_name)
59
65
  columns_hash(table_name)
66
+ indexes(table_name)
60
67
  end
61
68
  end
62
69
 
@@ -77,17 +84,32 @@ module ActiveRecord
77
84
  }]
78
85
  end
79
86
 
87
+ # Checks whether the columns hash is already cached for a table.
88
+ def columns_hash?(table_name)
89
+ @columns_hash.key?(table_name)
90
+ end
91
+
92
+ def indexes(table_name)
93
+ @indexes[table_name] ||= connection.indexes(table_name)
94
+ end
95
+
96
+ def database_version # :nodoc:
97
+ @database_version ||= connection.get_database_version
98
+ end
99
+
80
100
  # Clears out internal caches
81
101
  def clear!
82
102
  @columns.clear
83
103
  @columns_hash.clear
84
104
  @primary_keys.clear
85
105
  @data_sources.clear
106
+ @indexes.clear
86
107
  @version = nil
108
+ @database_version = nil
87
109
  end
88
110
 
89
111
  def size
90
- [@columns, @columns_hash, @primary_keys, @data_sources].map(&:size).inject :+
112
+ [@columns, @columns_hash, @primary_keys, @data_sources].sum(&:size)
91
113
  end
92
114
 
93
115
  # Clear out internal caches for the data source +name+.
@@ -96,20 +118,21 @@ module ActiveRecord
96
118
  @columns_hash.delete name
97
119
  @primary_keys.delete name
98
120
  @data_sources.delete name
121
+ @indexes.delete name
99
122
  end
100
123
 
101
124
  def marshal_dump
102
125
  # if we get current version during initialization, it happens stack over flow.
103
126
  @version = connection.migration_context.current_version
104
- [@version, @columns, @columns_hash, @primary_keys, @data_sources]
127
+ [@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
105
128
  end
106
129
 
107
130
  def marshal_load(array)
108
- @version, @columns, @columns_hash, @primary_keys, @data_sources = array
131
+ @version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
132
+ @indexes = @indexes || {}
109
133
  end
110
134
 
111
135
  private
112
-
113
136
  def prepare_data_sources
114
137
  connection.data_sources.each { |source| @data_sources[source] = true }
115
138
  end
@@ -16,19 +16,22 @@ module ActiveRecord
16
16
 
17
17
  def ==(other)
18
18
  other.is_a?(SqlTypeMetadata) &&
19
- attributes_for_hash == other.attributes_for_hash
19
+ sql_type == other.sql_type &&
20
+ type == other.type &&
21
+ limit == other.limit &&
22
+ precision == other.precision &&
23
+ scale == other.scale
20
24
  end
21
25
  alias eql? ==
22
26
 
23
27
  def hash
24
- attributes_for_hash.hash
28
+ SqlTypeMetadata.hash ^
29
+ sql_type.hash ^
30
+ type.hash ^
31
+ limit.hash ^
32
+ precision.hash >> 1 ^
33
+ scale.hash >> 2
25
34
  end
26
-
27
- protected
28
-
29
- def attributes_for_hash
30
- [self.class, sql_type, type, limit, precision, scale]
31
- end
32
35
  end
33
36
  end
34
37
  end