activerecord 5.2.8 → 6.0.0.beta1

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 (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -788
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +35 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  12. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  14. data/lib/active_record/associations/collection_association.rb +11 -25
  15. data/lib/active_record/associations/collection_proxy.rb +32 -6
  16. data/lib/active_record/associations/foreign_association.rb +7 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  19. data/lib/active_record/associations/has_one_association.rb +28 -30
  20. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  21. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  22. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/preloader/association.rb +1 -2
  25. data/lib/active_record/associations/preloader.rb +32 -29
  26. data/lib/active_record/associations/singular_association.rb +2 -16
  27. data/lib/active_record/associations.rb +16 -12
  28. data/lib/active_record/attribute_assignment.rb +7 -10
  29. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  31. data/lib/active_record/attribute_methods/read.rb +16 -48
  32. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  34. data/lib/active_record/attribute_methods/write.rb +15 -16
  35. data/lib/active_record/attribute_methods.rb +34 -56
  36. data/lib/active_record/autosave_association.rb +7 -21
  37. data/lib/active_record/base.rb +2 -2
  38. data/lib/active_record/callbacks.rb +3 -17
  39. data/lib/active_record/collection_cache_key.rb +1 -1
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  41. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  52. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  53. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  56. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  58. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  59. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  60. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  63. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  64. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  66. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  67. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  68. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  69. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  70. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  72. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  74. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  76. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  77. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  78. data/lib/active_record/connection_handling.rb +132 -26
  79. data/lib/active_record/core.rb +76 -43
  80. data/lib/active_record/counter_cache.rb +4 -29
  81. data/lib/active_record/database_configurations/database_config.rb +37 -0
  82. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  83. data/lib/active_record/database_configurations/url_config.rb +74 -0
  84. data/lib/active_record/database_configurations.rb +184 -0
  85. data/lib/active_record/enum.rb +22 -7
  86. data/lib/active_record/errors.rb +24 -21
  87. data/lib/active_record/explain.rb +1 -1
  88. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  89. data/lib/active_record/fixture_set/render_context.rb +17 -0
  90. data/lib/active_record/fixture_set/table_row.rb +153 -0
  91. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  92. data/lib/active_record/fixtures.rb +140 -472
  93. data/lib/active_record/gem_version.rb +4 -4
  94. data/lib/active_record/inheritance.rb +12 -2
  95. data/lib/active_record/integration.rb +56 -16
  96. data/lib/active_record/internal_metadata.rb +5 -1
  97. data/lib/active_record/locking/optimistic.rb +2 -2
  98. data/lib/active_record/locking/pessimistic.rb +3 -3
  99. data/lib/active_record/log_subscriber.rb +7 -26
  100. data/lib/active_record/migration/command_recorder.rb +35 -5
  101. data/lib/active_record/migration/compatibility.rb +34 -16
  102. data/lib/active_record/migration.rb +38 -37
  103. data/lib/active_record/model_schema.rb +30 -9
  104. data/lib/active_record/nested_attributes.rb +2 -2
  105. data/lib/active_record/no_touching.rb +7 -0
  106. data/lib/active_record/persistence.rb +18 -7
  107. data/lib/active_record/query_cache.rb +11 -4
  108. data/lib/active_record/querying.rb +19 -11
  109. data/lib/active_record/railtie.rb +71 -42
  110. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  111. data/lib/active_record/railties/controller_runtime.rb +30 -35
  112. data/lib/active_record/railties/databases.rake +94 -43
  113. data/lib/active_record/reflection.rb +60 -44
  114. data/lib/active_record/relation/batches.rb +13 -10
  115. data/lib/active_record/relation/calculations.rb +38 -28
  116. data/lib/active_record/relation/delegation.rb +4 -13
  117. data/lib/active_record/relation/finder_methods.rb +12 -25
  118. data/lib/active_record/relation/merger.rb +2 -6
  119. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  120. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  121. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  122. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  123. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  124. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  125. data/lib/active_record/relation/predicate_builder.rb +4 -6
  126. data/lib/active_record/relation/query_attribute.rb +15 -12
  127. data/lib/active_record/relation/query_methods.rb +29 -52
  128. data/lib/active_record/relation/where_clause.rb +4 -0
  129. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  130. data/lib/active_record/relation.rb +150 -69
  131. data/lib/active_record/result.rb +30 -11
  132. data/lib/active_record/sanitization.rb +2 -39
  133. data/lib/active_record/schema.rb +1 -10
  134. data/lib/active_record/schema_dumper.rb +12 -6
  135. data/lib/active_record/schema_migration.rb +4 -0
  136. data/lib/active_record/scoping/default.rb +10 -3
  137. data/lib/active_record/scoping/named.rb +10 -14
  138. data/lib/active_record/scoping.rb +9 -8
  139. data/lib/active_record/statement_cache.rb +32 -5
  140. data/lib/active_record/store.rb +39 -8
  141. data/lib/active_record/table_metadata.rb +1 -4
  142. data/lib/active_record/tasks/database_tasks.rb +89 -23
  143. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  146. data/lib/active_record/test_databases.rb +38 -0
  147. data/lib/active_record/test_fixtures.rb +224 -0
  148. data/lib/active_record/timestamp.rb +4 -6
  149. data/lib/active_record/transactions.rb +3 -22
  150. data/lib/active_record/translation.rb +1 -1
  151. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  152. data/lib/active_record/type.rb +3 -4
  153. data/lib/active_record/type_caster/connection.rb +1 -6
  154. data/lib/active_record/type_caster/map.rb +1 -4
  155. data/lib/active_record/validations/uniqueness.rb +13 -25
  156. data/lib/active_record.rb +2 -1
  157. data/lib/arel/alias_predication.rb +9 -0
  158. data/lib/arel/attributes/attribute.rb +37 -0
  159. data/lib/arel/attributes.rb +22 -0
  160. data/lib/arel/collectors/bind.rb +24 -0
  161. data/lib/arel/collectors/composite.rb +31 -0
  162. data/lib/arel/collectors/plain_string.rb +20 -0
  163. data/lib/arel/collectors/sql_string.rb +20 -0
  164. data/lib/arel/collectors/substitute_binds.rb +28 -0
  165. data/lib/arel/crud.rb +42 -0
  166. data/lib/arel/delete_manager.rb +18 -0
  167. data/lib/arel/errors.rb +9 -0
  168. data/lib/arel/expressions.rb +29 -0
  169. data/lib/arel/factory_methods.rb +49 -0
  170. data/lib/arel/insert_manager.rb +49 -0
  171. data/lib/arel/math.rb +45 -0
  172. data/lib/arel/nodes/and.rb +32 -0
  173. data/lib/arel/nodes/ascending.rb +23 -0
  174. data/lib/arel/nodes/binary.rb +52 -0
  175. data/lib/arel/nodes/bind_param.rb +36 -0
  176. data/lib/arel/nodes/case.rb +55 -0
  177. data/lib/arel/nodes/casted.rb +50 -0
  178. data/lib/arel/nodes/count.rb +12 -0
  179. data/lib/arel/nodes/delete_statement.rb +45 -0
  180. data/lib/arel/nodes/descending.rb +23 -0
  181. data/lib/arel/nodes/equality.rb +18 -0
  182. data/lib/arel/nodes/extract.rb +24 -0
  183. data/lib/arel/nodes/false.rb +16 -0
  184. data/lib/arel/nodes/full_outer_join.rb +8 -0
  185. data/lib/arel/nodes/function.rb +44 -0
  186. data/lib/arel/nodes/grouping.rb +8 -0
  187. data/lib/arel/nodes/in.rb +8 -0
  188. data/lib/arel/nodes/infix_operation.rb +80 -0
  189. data/lib/arel/nodes/inner_join.rb +8 -0
  190. data/lib/arel/nodes/insert_statement.rb +37 -0
  191. data/lib/arel/nodes/join_source.rb +20 -0
  192. data/lib/arel/nodes/matches.rb +18 -0
  193. data/lib/arel/nodes/named_function.rb +23 -0
  194. data/lib/arel/nodes/node.rb +50 -0
  195. data/lib/arel/nodes/node_expression.rb +13 -0
  196. data/lib/arel/nodes/outer_join.rb +8 -0
  197. data/lib/arel/nodes/over.rb +15 -0
  198. data/lib/arel/nodes/regexp.rb +16 -0
  199. data/lib/arel/nodes/right_outer_join.rb +8 -0
  200. data/lib/arel/nodes/select_core.rb +63 -0
  201. data/lib/arel/nodes/select_statement.rb +41 -0
  202. data/lib/arel/nodes/sql_literal.rb +16 -0
  203. data/lib/arel/nodes/string_join.rb +11 -0
  204. data/lib/arel/nodes/table_alias.rb +27 -0
  205. data/lib/arel/nodes/terminal.rb +16 -0
  206. data/lib/arel/nodes/true.rb +16 -0
  207. data/lib/arel/nodes/unary.rb +44 -0
  208. data/lib/arel/nodes/unary_operation.rb +20 -0
  209. data/lib/arel/nodes/unqualified_column.rb +22 -0
  210. data/lib/arel/nodes/update_statement.rb +41 -0
  211. data/lib/arel/nodes/values.rb +16 -0
  212. data/lib/arel/nodes/values_list.rb +24 -0
  213. data/lib/arel/nodes/window.rb +126 -0
  214. data/lib/arel/nodes/with.rb +11 -0
  215. data/lib/arel/nodes.rb +67 -0
  216. data/lib/arel/order_predications.rb +13 -0
  217. data/lib/arel/predications.rb +257 -0
  218. data/lib/arel/select_manager.rb +271 -0
  219. data/lib/arel/table.rb +110 -0
  220. data/lib/arel/tree_manager.rb +72 -0
  221. data/lib/arel/update_manager.rb +34 -0
  222. data/lib/arel/visitors/depth_first.rb +199 -0
  223. data/lib/arel/visitors/dot.rb +292 -0
  224. data/lib/arel/visitors/ibm_db.rb +21 -0
  225. data/lib/arel/visitors/informix.rb +56 -0
  226. data/lib/arel/visitors/mssql.rb +143 -0
  227. data/lib/arel/visitors/mysql.rb +83 -0
  228. data/lib/arel/visitors/oracle.rb +159 -0
  229. data/lib/arel/visitors/oracle12.rb +67 -0
  230. data/lib/arel/visitors/postgresql.rb +116 -0
  231. data/lib/arel/visitors/sqlite.rb +39 -0
  232. data/lib/arel/visitors/to_sql.rb +913 -0
  233. data/lib/arel/visitors/visitor.rb +42 -0
  234. data/lib/arel/visitors/where_sql.rb +23 -0
  235. data/lib/arel/visitors.rb +20 -0
  236. data/lib/arel/window_predications.rb +9 -0
  237. data/lib/arel.rb +44 -0
  238. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  239. data/lib/rails/generators/active_record/migration.rb +14 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  241. metadata +104 -26
@@ -17,48 +17,23 @@ module ActiveRecord
17
17
  "VALIDATE CONSTRAINT #{quote_column_name(name)}"
18
18
  end
19
19
 
20
- def visit_ChangeColumnDefinition(o)
21
- column = o.column
22
- column.sql_type = type_to_sql(column.type, column.options)
23
- quoted_column_name = quote_column_name(o.name)
24
-
25
- change_column_sql = "ALTER COLUMN #{quoted_column_name} TYPE #{column.sql_type}".dup
26
-
27
- options = column_options(column)
28
-
29
- if options[:collation]
30
- change_column_sql << " COLLATE \"#{options[:collation]}\""
31
- end
32
-
33
- if options[:using]
34
- change_column_sql << " USING #{options[:using]}"
35
- elsif options[:cast_as]
36
- cast_as_type = type_to_sql(options[:cast_as], options)
37
- change_column_sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
38
- end
39
-
40
- if options.key?(:default)
41
- if options[:default].nil?
42
- change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
43
- else
44
- quoted_default = quote_default_expression(options[:default], column)
45
- change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
46
- end
47
- end
48
-
49
- if options.key?(:null)
50
- change_column_sql << ", ALTER COLUMN #{quoted_column_name} #{options[:null] ? 'DROP' : 'SET'} NOT NULL"
51
- end
52
-
53
- change_column_sql
54
- end
55
-
56
20
  def add_column_options!(sql, options)
57
21
  if options[:collation]
58
22
  sql << " COLLATE \"#{options[:collation]}\""
59
23
  end
60
24
  super
61
25
  end
26
+
27
+ # Returns any SQL string to go between CREATE and TABLE. May be nil.
28
+ def table_modifier_in_create(o)
29
+ # A table cannot be both TEMPORARY and UNLOGGED, since all TEMPORARY
30
+ # tables are already UNLOGGED.
31
+ if o.temporary
32
+ " TEMPORARY"
33
+ elsif o.unlogged
34
+ " UNLOGGED"
35
+ end
36
+ end
62
37
  end
63
38
  end
64
39
  end
@@ -13,10 +13,10 @@ module ActiveRecord
13
13
  # t.timestamps
14
14
  # end
15
15
  #
16
- # By default, this will use the +gen_random_uuid()+ function from the
16
+ # By default, this will use the <tt>gen_random_uuid()</tt> function from the
17
17
  # +pgcrypto+ extension. As that extension is only available in
18
18
  # PostgreSQL 9.4+, for earlier versions an explicit default can be set
19
- # to use +uuid_generate_v4()+ from the +uuid-ossp+ extension instead:
19
+ # to use <tt>uuid_generate_v4()</tt> from the +uuid-ossp+ extension instead:
20
20
  #
21
21
  # create_table :stuffs, id: false do |t|
22
22
  # t.primary_key :id, :uuid, default: "uuid_generate_v4()"
@@ -175,6 +175,13 @@ module ActiveRecord
175
175
  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
176
176
  include ColumnMethods
177
177
 
178
+ attr_reader :unlogged
179
+
180
+ def initialize(*)
181
+ super
182
+ @unlogged = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables
183
+ end
184
+
178
185
  private
179
186
  def integer_like_primary_key_type(type, options)
180
187
  if type == :bigint || options[:limit] == 8
@@ -22,8 +22,8 @@ module ActiveRecord
22
22
  def create_database(name, options = {})
23
23
  options = { encoding: "utf8" }.merge!(options.symbolize_keys)
24
24
 
25
- option_string = options.inject("") do |memo, (key, value)|
26
- memo += case key
25
+ option_string = options.each_with_object(+"") do |(key, value), memo|
26
+ memo << case key
27
27
  when :owner
28
28
  " OWNER = \"#{value}\""
29
29
  when :template
@@ -68,7 +68,7 @@ module ActiveRecord
68
68
  table = quoted_scope(table_name)
69
69
  index = quoted_scope(index_name)
70
70
 
71
- query_value(<<-SQL, "SCHEMA").to_i > 0
71
+ query_value(<<~SQL, "SCHEMA").to_i > 0
72
72
  SELECT COUNT(*)
73
73
  FROM pg_class t
74
74
  INNER JOIN pg_index d ON t.oid = d.indrelid
@@ -85,7 +85,7 @@ module ActiveRecord
85
85
  def indexes(table_name) # :nodoc:
86
86
  scope = quoted_scope(table_name)
87
87
 
88
- result = query(<<-SQL, "SCHEMA")
88
+ result = query(<<~SQL, "SCHEMA")
89
89
  SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
90
90
  pg_catalog.obj_description(i.oid, 'pg_class') AS comment
91
91
  FROM pg_class t
@@ -115,7 +115,7 @@ module ActiveRecord
115
115
  if indkey.include?(0)
116
116
  columns = expressions
117
117
  else
118
- columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
118
+ columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
119
119
  SELECT a.attnum, a.attname
120
120
  FROM pg_attribute a
121
121
  WHERE a.attrelid = #{oid}
@@ -158,7 +158,7 @@ module ActiveRecord
158
158
  def table_comment(table_name) # :nodoc:
159
159
  scope = quoted_scope(table_name, type: "BASE TABLE")
160
160
  if scope[:name]
161
- query_value(<<-SQL.strip_heredoc, "SCHEMA")
161
+ query_value(<<~SQL, "SCHEMA")
162
162
  SELECT pg_catalog.obj_description(c.oid, 'pg_class')
163
163
  FROM pg_catalog.pg_class c
164
164
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -196,7 +196,7 @@ module ActiveRecord
196
196
 
197
197
  # Returns an array of schema names.
198
198
  def schema_names
199
- query_values(<<-SQL, "SCHEMA")
199
+ query_values(<<~SQL, "SCHEMA")
200
200
  SELECT nspname
201
201
  FROM pg_namespace
202
202
  WHERE nspname !~ '^pg_.*'
@@ -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
@@ -502,7 +502,7 @@ module ActiveRecord
502
502
 
503
503
  def foreign_keys(table_name)
504
504
  scope = quoted_scope(table_name)
505
- fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
505
+ fk_info = exec_query(<<~SQL, "SCHEMA")
506
506
  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
507
  FROM pg_constraint c
508
508
  JOIN pg_class t1 ON c.conrelid = t1.oid
@@ -683,20 +683,38 @@ module ActiveRecord
683
683
  end
684
684
  end
685
685
 
686
+ def change_column_sql(table_name, column_name, type, options = {})
687
+ quoted_column_name = quote_column_name(column_name)
688
+ sql_type = type_to_sql(type, options)
689
+ sql = +"ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
690
+ if options[:collation]
691
+ sql << " COLLATE \"#{options[:collation]}\""
692
+ end
693
+ if options[:using]
694
+ sql << " USING #{options[:using]}"
695
+ elsif options[:cast_as]
696
+ cast_as_type = type_to_sql(options[:cast_as], options)
697
+ sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
698
+ end
699
+
700
+ sql
701
+ end
702
+
686
703
  def add_column_for_alter(table_name, column_name, type, options = {})
687
704
  return super unless options.key?(:comment)
688
705
  [super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
689
706
  end
690
707
 
691
708
  def change_column_for_alter(table_name, column_name, type, options = {})
692
- td = create_table_definition(table_name)
693
- cd = td.new_column_definition(column_name, type, options)
694
- sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
709
+ sqls = [change_column_sql(table_name, column_name, type, options)]
710
+ sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default)
711
+ sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
695
712
  sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
696
713
  sqls
697
714
  end
698
715
 
699
- def change_column_default_for_alter(table_name, column_name, default_or_changes)
716
+ # Changes the default value of a table column.
717
+ def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc:
700
718
  column = column_for(table_name, column_name)
701
719
  return unless column
702
720
 
@@ -711,8 +729,8 @@ module ActiveRecord
711
729
  end
712
730
  end
713
731
 
714
- def change_column_null_for_alter(table_name, column_name, null, default = nil)
715
- "ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
732
+ def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc:
733
+ "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
716
734
  end
717
735
 
718
736
  def add_timestamps_for_alter(table_name, options = {})
@@ -739,7 +757,7 @@ module ActiveRecord
739
757
  scope = quoted_scope(name, type: type)
740
758
  scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
741
759
 
742
- sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
760
+ sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
743
761
  sql << " WHERE n.nspname = #{scope[:schema]}"
744
762
  sql << " AND c.relname = #{scope[:name]}" if scope[:name]
745
763
  sql << " AND c.relkind IN (#{scope[:type]})"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
+ # :stopdoc:
4
5
  module ConnectionAdapters
5
6
  class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
6
7
  undef to_yaml if method_defined?(:to_yaml)
@@ -16,7 +17,7 @@ module ActiveRecord
16
17
  end
17
18
 
18
19
  def sql_type
19
- super.gsub(/\[\]$/, "".freeze)
20
+ super.gsub(/\[\]$/, "")
20
21
  end
21
22
 
22
23
  def ==(other)
@@ -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?("does not exist")
50
+ raise ActiveRecord::NoDatabaseError
51
+ else
52
+ raise
53
+ end
49
54
  end
50
55
  end
51
56
 
@@ -78,7 +83,20 @@ module ActiveRecord
78
83
  # In addition, default connection parameters of libpq can be set per environment variables.
79
84
  # See https://www.postgresql.org/docs/current/static/libpq-envars.html .
80
85
  class PostgreSQLAdapter < AbstractAdapter
81
- ADAPTER_NAME = "PostgreSQL".freeze
86
+ ADAPTER_NAME = "PostgreSQL"
87
+
88
+ ##
89
+ # :singleton-method:
90
+ # PostgreSQL allows the creation of "unlogged" tables, which do not record
91
+ # data in the PostgreSQL Write-Ahead Log. This can make the tables faster,
92
+ # but significantly increases the risk of data loss if the database
93
+ # crashes. As a result, this should not be used in production
94
+ # environments. If you would like all created tables to be unlogged in
95
+ # the test environment you can add the following line to your test.rb
96
+ # file:
97
+ #
98
+ # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables = true
99
+ class_attribute :create_unlogged_tables, default: false
82
100
 
83
101
  NATIVE_DATABASE_TYPES = {
84
102
  primary_key: "bigserial primary key",
@@ -167,7 +185,7 @@ module ActiveRecord
167
185
  end
168
186
 
169
187
  def supports_json?
170
- postgresql_version >= 90200
188
+ true
171
189
  end
172
190
 
173
191
  def supports_comments?
@@ -220,15 +238,11 @@ module ActiveRecord
220
238
  @local_tz = nil
221
239
  @max_identifier_length = nil
222
240
 
223
- connect
241
+ configure_connection
224
242
  add_pg_encoders
225
243
  @statements = StatementPool.new @connection,
226
244
  self.class.type_cast_config_to_integer(config[:statement_limit])
227
245
 
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
246
  add_pg_decoders
233
247
 
234
248
  @type_map = Type::HashLookupTypeMap.new
@@ -318,22 +332,26 @@ module ActiveRecord
318
332
  end
319
333
 
320
334
  def supports_ranges?
321
- # Range datatypes weren't introduced until PostgreSQL 9.2
322
- postgresql_version >= 90200
335
+ true
323
336
  end
337
+ deprecate :supports_ranges?
324
338
 
325
339
  def supports_materialized_views?
326
- postgresql_version >= 90300
340
+ true
327
341
  end
328
342
 
329
343
  def supports_foreign_tables?
330
- postgresql_version >= 90300
344
+ true
331
345
  end
332
346
 
333
347
  def supports_pgcrypto_uuid?
334
348
  postgresql_version >= 90400
335
349
  end
336
350
 
351
+ def supports_lazy_transactions?
352
+ true
353
+ end
354
+
337
355
  def get_advisory_lock(lock_id) # :nodoc:
338
356
  unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
339
357
  raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
@@ -406,6 +424,12 @@ module ActiveRecord
406
424
  end
407
425
 
408
426
  private
427
+ def check_version
428
+ if postgresql_version < 90300
429
+ raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.3."
430
+ end
431
+ end
432
+
409
433
  # See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
410
434
  VALUE_LIMIT_VIOLATION = "22001"
411
435
  NUMERIC_VALUE_OUT_OF_RANGE = "22003"
@@ -417,34 +441,34 @@ module ActiveRecord
417
441
  LOCK_NOT_AVAILABLE = "55P03"
418
442
  QUERY_CANCELED = "57014"
419
443
 
420
- def translate_exception(exception, message)
444
+ def translate_exception(exception, message:, sql:, binds:)
421
445
  return exception unless exception.respond_to?(:result)
422
446
 
423
447
  case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
424
448
  when UNIQUE_VIOLATION
425
- RecordNotUnique.new(message)
449
+ RecordNotUnique.new(message, sql: sql, binds: binds)
426
450
  when FOREIGN_KEY_VIOLATION
427
- InvalidForeignKey.new(message)
451
+ InvalidForeignKey.new(message, sql: sql, binds: binds)
428
452
  when VALUE_LIMIT_VIOLATION
429
- ValueTooLong.new(message)
453
+ ValueTooLong.new(message, sql: sql, binds: binds)
430
454
  when NUMERIC_VALUE_OUT_OF_RANGE
431
- RangeError.new(message)
455
+ RangeError.new(message, sql: sql, binds: binds)
432
456
  when NOT_NULL_VIOLATION
433
- NotNullViolation.new(message)
457
+ NotNullViolation.new(message, sql: sql, binds: binds)
434
458
  when SERIALIZATION_FAILURE
435
- SerializationFailure.new(message)
459
+ SerializationFailure.new(message, sql: sql, binds: binds)
436
460
  when DEADLOCK_DETECTED
437
- Deadlocked.new(message)
461
+ Deadlocked.new(message, sql: sql, binds: binds)
438
462
  when LOCK_NOT_AVAILABLE
439
- LockWaitTimeout.new(message)
463
+ LockWaitTimeout.new(message, sql: sql, binds: binds)
440
464
  when QUERY_CANCELED
441
- QueryCanceled.new(message)
465
+ QueryCanceled.new(message, sql: sql, binds: binds)
442
466
  else
443
467
  super
444
468
  end
445
469
  end
446
470
 
447
- def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
471
+ def get_oid_type(oid, fmod, column_name, sql_type = "")
448
472
  if !type_map.key?(oid)
449
473
  load_additional_types([oid])
450
474
  end
@@ -533,13 +557,13 @@ module ActiveRecord
533
557
  # Quoted types
534
558
  when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
535
559
  # The default 'now'::date is CURRENT_DATE
536
- if $1 == "now".freeze && $2 == "date".freeze
560
+ if $1 == "now" && $2 == "date"
537
561
  nil
538
562
  else
539
- $1.gsub("''".freeze, "'".freeze)
563
+ $1.gsub("''", "'")
540
564
  end
541
565
  # Boolean types
542
- when "true".freeze, "false".freeze
566
+ when "true", "false"
543
567
  default
544
568
  # Numeric types
545
569
  when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
@@ -565,18 +589,11 @@ module ActiveRecord
565
589
  def load_additional_types(oids = nil)
566
590
  initializer = OID::TypeMapInitializer.new(type_map)
567
591
 
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
592
+ query = <<~SQL
593
+ SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
594
+ FROM pg_type as t
595
+ LEFT JOIN pg_range as r ON oid = rngtypid
596
+ SQL
580
597
 
581
598
  if oids
582
599
  query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
@@ -592,6 +609,10 @@ module ActiveRecord
592
609
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
593
610
 
594
611
  def execute_and_clear(sql, name, binds, prepare: false)
612
+ if preventing_writes? && write_query?(sql)
613
+ raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
614
+ end
615
+
595
616
  if without_prepared_statement?(binds)
596
617
  result = exec_no_cache(sql, name, [])
597
618
  elsif !prepare
@@ -605,6 +626,8 @@ module ActiveRecord
605
626
  end
606
627
 
607
628
  def exec_no_cache(sql, name, binds)
629
+ materialize_transactions
630
+
608
631
  type_casted_binds = type_casted_binds(binds)
609
632
  log(sql, name, binds, type_casted_binds) do
610
633
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -614,7 +637,9 @@ module ActiveRecord
614
637
  end
615
638
 
616
639
  def exec_cache(sql, name, binds)
617
- stmt_key = prepare_statement(sql)
640
+ materialize_transactions
641
+
642
+ stmt_key = prepare_statement(sql, binds)
618
643
  type_casted_binds = type_casted_binds(binds)
619
644
 
620
645
  log(sql, name, binds, type_casted_binds, stmt_key) do
@@ -647,7 +672,7 @@ module ActiveRecord
647
672
  #
648
673
  # Check here for more details:
649
674
  # 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
675
+ CACHED_PLAN_HEURISTIC = "cached plan must not change result type"
651
676
  def is_cached_plan_failure?(e)
652
677
  pgerror = e.cause
653
678
  code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
@@ -668,7 +693,7 @@ module ActiveRecord
668
693
 
669
694
  # Prepare the statement if it hasn't been prepared, return
670
695
  # the statement key.
671
- def prepare_statement(sql)
696
+ def prepare_statement(sql, binds)
672
697
  @lock.synchronize do
673
698
  sql_key = sql_key(sql)
674
699
  unless @statements.key? sql_key
@@ -676,7 +701,7 @@ module ActiveRecord
676
701
  begin
677
702
  @connection.prepare nextkey, sql
678
703
  rescue => e
679
- raise translate_exception_class(e, sql)
704
+ raise translate_exception_class(e, sql, binds)
680
705
  end
681
706
  # Clear the queue
682
707
  @connection.get_last_result
@@ -691,12 +716,6 @@ module ActiveRecord
691
716
  def connect
692
717
  @connection = PG.connect(@connection_parameters)
693
718
  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
700
719
  end
701
720
 
702
721
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
@@ -754,7 +773,7 @@ module ActiveRecord
754
773
  # - format_type includes the column size constraint, e.g. varchar(50)
755
774
  # - ::regclass is a function that gives the id for a table name
756
775
  def column_definitions(table_name)
757
- query(<<-end_sql, "SCHEMA")
776
+ query(<<~SQL, "SCHEMA")
758
777
  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
759
778
  pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
760
779
  c.collname, col_description(a.attrelid, a.attnum) AS comment
@@ -765,7 +784,7 @@ module ActiveRecord
765
784
  WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
766
785
  AND a.attnum > 0 AND NOT a.attisdropped
767
786
  ORDER BY a.attnum
768
- end_sql
787
+ SQL
769
788
  end
770
789
 
771
790
  def extract_table_ref_from_insert_sql(sql)
@@ -780,7 +799,7 @@ module ActiveRecord
780
799
  def can_perform_case_insensitive_comparison_for?(column)
781
800
  @case_insensitive_cache ||= {}
782
801
  @case_insensitive_cache[column.sql_type] ||= begin
783
- sql = <<-end_sql
802
+ sql = <<~SQL
784
803
  SELECT exists(
785
804
  SELECT * FROM pg_proc
786
805
  WHERE proname = 'lower'
@@ -792,7 +811,7 @@ module ActiveRecord
792
811
  WHERE proname = 'lower'
793
812
  AND castsource = #{quote column.sql_type}::regtype
794
813
  )
795
- end_sql
814
+ SQL
796
815
  execute_and_clear(sql, "SCHEMA", []) do |result|
797
816
  result.getvalue(0, 0)
798
817
  end
@@ -818,7 +837,7 @@ module ActiveRecord
818
837
  "bool" => PG::TextDecoder::Boolean,
819
838
  }
820
839
  known_coder_types = coders_by_name.keys.map { |n| quote(n) }
821
- query = <<-SQL % known_coder_types.join(", ")
840
+ query = <<~SQL % known_coder_types.join(", ")
822
841
  SELECT t.oid, t.typname
823
842
  FROM pg_type as t
824
843
  WHERE t.typname IN (%s)
@@ -77,6 +77,11 @@ module ActiveRecord
77
77
  }]
78
78
  end
79
79
 
80
+ # Checks whether the columns hash is already cached for a table.
81
+ def columns_hash?(table_name)
82
+ @columns_hash.key?(table_name)
83
+ end
84
+
80
85
  # Clears out internal caches
81
86
  def clear!
82
87
  @columns.clear
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  end
18
18
 
19
19
  def quote_column_name(name)
20
- @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
20
+ @quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}")
21
21
  end
22
22
 
23
23
  def quoted_time(value)
@@ -30,19 +30,19 @@ module ActiveRecord
30
30
  end
31
31
 
32
32
  def quoted_true
33
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "1".freeze : "'t'".freeze
33
+ "1"
34
34
  end
35
35
 
36
36
  def unquoted_true
37
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 1 : "t".freeze
37
+ 1
38
38
  end
39
39
 
40
40
  def quoted_false
41
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? "0".freeze : "'f'".freeze
41
+ "0"
42
42
  end
43
43
 
44
44
  def unquoted_false
45
- ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer ? 0 : "f".freeze
45
+ 0
46
46
  end
47
47
 
48
48
  private