activerecord 5.1.0 → 5.2.0.rc1

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 (260) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +410 -530
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record/aggregations.rb +6 -5
  8. data/lib/active_record/association_relation.rb +4 -2
  9. data/lib/active_record/associations/alias_tracker.rb +23 -32
  10. data/lib/active_record/associations/association.rb +20 -21
  11. data/lib/active_record/associations/association_scope.rb +49 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +12 -10
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -6
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +2 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +50 -41
  22. data/lib/active_record/associations/collection_proxy.rb +22 -39
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +4 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +12 -18
  26. data/lib/active_record/associations/has_one_association.rb +5 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +8 -7
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -64
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  31. data/lib/active_record/associations/join_dependency.rb +27 -44
  32. data/lib/active_record/associations/preloader/association.rb +53 -92
  33. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -10
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +68 -76
  38. data/lib/active_record/attribute_assignment.rb +2 -0
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  41. data/lib/active_record/attribute_methods/dirty.rb +24 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +8 -2
  45. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  47. data/lib/active_record/attribute_methods/write.rb +22 -19
  48. data/lib/active_record/attribute_methods.rb +48 -12
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +8 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -6
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +14 -10
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -24
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +58 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +165 -85
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +118 -180
  69. data/lib/active_record/connection_adapters/column.rb +4 -2
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -17
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +269 -126
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +64 -85
  116. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  122. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  123. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  124. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +92 -95
  125. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  126. data/lib/active_record/connection_handling.rb +4 -2
  127. data/lib/active_record/core.rb +39 -60
  128. data/lib/active_record/counter_cache.rb +3 -2
  129. data/lib/active_record/define_callbacks.rb +5 -3
  130. data/lib/active_record/dynamic_matchers.rb +9 -9
  131. data/lib/active_record/enum.rb +17 -13
  132. data/lib/active_record/errors.rb +42 -3
  133. data/lib/active_record/explain.rb +3 -1
  134. data/lib/active_record/explain_registry.rb +2 -0
  135. data/lib/active_record/explain_subscriber.rb +2 -0
  136. data/lib/active_record/fixture_set/file.rb +2 -0
  137. data/lib/active_record/fixtures.rb +67 -60
  138. data/lib/active_record/gem_version.rb +4 -2
  139. data/lib/active_record/inheritance.rb +9 -9
  140. data/lib/active_record/integration.rb +58 -19
  141. data/lib/active_record/internal_metadata.rb +2 -0
  142. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  143. data/lib/active_record/locking/optimistic.rb +8 -6
  144. data/lib/active_record/locking/pessimistic.rb +9 -6
  145. data/lib/active_record/log_subscriber.rb +46 -4
  146. data/lib/active_record/migration/command_recorder.rb +11 -9
  147. data/lib/active_record/migration/compatibility.rb +74 -22
  148. data/lib/active_record/migration/join_table.rb +2 -0
  149. data/lib/active_record/migration.rb +181 -137
  150. data/lib/active_record/model_schema.rb +73 -58
  151. data/lib/active_record/nested_attributes.rb +18 -6
  152. data/lib/active_record/no_touching.rb +3 -1
  153. data/lib/active_record/null_relation.rb +2 -0
  154. data/lib/active_record/persistence.rb +153 -18
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +4 -2
  157. data/lib/active_record/railtie.rb +61 -3
  158. data/lib/active_record/railties/console_sandbox.rb +2 -0
  159. data/lib/active_record/railties/controller_runtime.rb +2 -0
  160. data/lib/active_record/railties/databases.rake +47 -37
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +131 -204
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  164. data/lib/active_record/relation/batches.rb +32 -17
  165. data/lib/active_record/relation/calculations.rb +58 -20
  166. data/lib/active_record/relation/delegation.rb +10 -29
  167. data/lib/active_record/relation/finder_methods.rb +74 -85
  168. data/lib/active_record/relation/from_clause.rb +2 -8
  169. data/lib/active_record/relation/merger.rb +51 -20
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  172. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  173. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  175. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  176. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  177. data/lib/active_record/relation/predicate_builder.rb +53 -78
  178. data/lib/active_record/relation/query_attribute.rb +9 -2
  179. data/lib/active_record/relation/query_methods.rb +101 -95
  180. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  181. data/lib/active_record/relation/spawn_methods.rb +3 -1
  182. data/lib/active_record/relation/where_clause.rb +65 -67
  183. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  184. data/lib/active_record/relation.rb +99 -202
  185. data/lib/active_record/result.rb +2 -0
  186. data/lib/active_record/runtime_registry.rb +2 -0
  187. data/lib/active_record/sanitization.rb +129 -121
  188. data/lib/active_record/schema.rb +4 -2
  189. data/lib/active_record/schema_dumper.rb +36 -26
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping/default.rb +10 -7
  192. data/lib/active_record/scoping/named.rb +38 -12
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/secure_token.rb +2 -0
  195. data/lib/active_record/serialization.rb +2 -0
  196. data/lib/active_record/statement_cache.rb +22 -12
  197. data/lib/active_record/store.rb +3 -1
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +12 -3
  200. data/lib/active_record/tasks/database_tasks.rb +37 -25
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -5
  205. data/lib/active_record/touch_later.rb +2 -0
  206. data/lib/active_record/transactions.rb +9 -7
  207. data/lib/active_record/translation.rb +2 -0
  208. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  209. data/lib/active_record/type/date.rb +2 -0
  210. data/lib/active_record/type/date_time.rb +2 -0
  211. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  212. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  213. data/lib/active_record/type/internal/timezone.rb +2 -0
  214. data/lib/active_record/type/json.rb +30 -0
  215. data/lib/active_record/type/serialized.rb +2 -0
  216. data/lib/active_record/type/text.rb +2 -0
  217. data/lib/active_record/type/time.rb +2 -0
  218. data/lib/active_record/type/type_map.rb +2 -0
  219. data/lib/active_record/type/unsigned_integer.rb +2 -0
  220. data/lib/active_record/type.rb +4 -1
  221. data/lib/active_record/type_caster/connection.rb +2 -0
  222. data/lib/active_record/type_caster/map.rb +3 -1
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/validations/absence.rb +2 -0
  225. data/lib/active_record/validations/associated.rb +2 -0
  226. data/lib/active_record/validations/length.rb +2 -0
  227. data/lib/active_record/validations/presence.rb +2 -0
  228. data/lib/active_record/validations/uniqueness.rb +35 -5
  229. data/lib/active_record/validations.rb +2 -0
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/active_record.rb +11 -4
  232. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  233. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  235. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration.rb +2 -0
  238. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  239. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record.rb +3 -1
  242. metadata +25 -37
  243. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  244. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  245. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  246. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  247. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  248. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  249. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -124
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/attribute_set.rb +0 -113
  256. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  257. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  258. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  259. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  260. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,4 +1,4 @@
1
- require "active_support/core_ext/string/strip"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
@@ -38,7 +38,7 @@ module ActiveRecord
38
38
  " TABLESPACE = \"#{value}\""
39
39
  when :connection_limit
40
40
  " CONNECTION LIMIT = #{value}"
41
- else
41
+ else
42
42
  ""
43
43
  end
44
44
  end
@@ -60,20 +60,15 @@ module ActiveRecord
60
60
 
61
61
  # Returns true if schema exists.
62
62
  def schema_exists?(name)
63
- select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
63
+ query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
64
64
  end
65
65
 
66
66
  # Verifies existence of an index with a given name.
67
- def index_name_exists?(table_name, index_name, default = nil)
68
- unless default.nil?
69
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
70
- Passing default to #index_name_exists? is deprecated without replacement.
71
- MSG
72
- end
67
+ def index_name_exists?(table_name, index_name)
73
68
  table = quoted_scope(table_name)
74
69
  index = quoted_scope(index_name)
75
70
 
76
- select_value(<<-SQL, "SCHEMA").to_i > 0
71
+ query_value(<<-SQL, "SCHEMA").to_i > 0
77
72
  SELECT COUNT(*)
78
73
  FROM pg_class t
79
74
  INNER JOIN pg_index d ON t.oid = d.indrelid
@@ -87,21 +82,12 @@ module ActiveRecord
87
82
  end
88
83
 
89
84
  # Returns an array of indexes for the given table.
90
- def indexes(table_name, name = nil) # :nodoc:
91
- if name
92
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
93
- Passing name to #indexes is deprecated without replacement.
94
- MSG
95
- end
96
-
85
+ def indexes(table_name) # :nodoc:
97
86
  scope = quoted_scope(table_name)
98
87
 
99
88
  result = query(<<-SQL, "SCHEMA")
100
89
  SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
101
- pg_catalog.obj_description(i.oid, 'pg_class') AS comment,
102
- (SELECT COUNT(*) FROM pg_opclass o
103
- JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
104
- ON o.oid = c.oid WHERE o.opcdefault = 'f')
90
+ pg_catalog.obj_description(i.oid, 'pg_class') AS comment
105
91
  FROM pg_class t
106
92
  INNER JOIN pg_index d ON t.oid = d.indrelid
107
93
  INNER JOIN pg_class i ON d.indexrelid = i.oid
@@ -120,11 +106,13 @@ module ActiveRecord
120
106
  inddef = row[3]
121
107
  oid = row[4]
122
108
  comment = row[5]
123
- opclass = row[6]
124
109
 
125
110
  using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
126
111
 
127
- if indkey.include?(0) || opclass > 0
112
+ orders = {}
113
+ opclasses = {}
114
+
115
+ if indkey.include?(0)
128
116
  columns = expressions
129
117
  else
130
118
  columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
@@ -134,33 +122,30 @@ module ActiveRecord
134
122
  AND a.attnum IN (#{indkey.join(",")})
135
123
  SQL
136
124
 
137
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
138
- orders = Hash[
139
- expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] }
140
- ]
125
+ # add info on sort order (only desc order is explicitly specified, asc is the default)
126
+ # and non-default opclasses
127
+ expressions.scan(/(?<column>\w+)\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
128
+ opclasses[column] = opclass.to_sym if opclass
129
+ if nulls
130
+ orders[column] = [desc, nulls].compact.join(" ")
131
+ else
132
+ orders[column] = :desc if desc
133
+ end
134
+ end
141
135
  end
142
136
 
143
- IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment.presence)
144
- end.compact
145
- end
146
-
147
- def new_column_from_field(table_name, field) # :nondoc:
148
- column_name, type, default, notnull, oid, fmod, collation, comment = field
149
- oid = oid.to_i
150
- fmod = fmod.to_i
151
- type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
152
- default_value = extract_value_from_default(default)
153
- default_function = extract_default_function(default_value, default)
154
- PostgreSQLColumn.new(
155
- column_name,
156
- default_value,
157
- type_metadata,
158
- !notnull,
159
- table_name,
160
- default_function,
161
- collation,
162
- comment: comment.presence
163
- )
137
+ IndexDefinition.new(
138
+ table_name,
139
+ index_name,
140
+ unique,
141
+ columns,
142
+ orders: orders,
143
+ opclasses: opclasses,
144
+ where: where,
145
+ using: using.to_sym,
146
+ comment: comment.presence
147
+ )
148
+ end
164
149
  end
165
150
 
166
151
  def table_options(table_name) # :nodoc:
@@ -173,7 +158,7 @@ module ActiveRecord
173
158
  def table_comment(table_name) # :nodoc:
174
159
  scope = quoted_scope(table_name, type: "BASE TABLE")
175
160
  if scope[:name]
176
- select_value(<<-SQL.strip_heredoc, "SCHEMA")
161
+ query_value(<<-SQL.strip_heredoc, "SCHEMA")
177
162
  SELECT pg_catalog.obj_description(c.oid, 'pg_class')
178
163
  FROM pg_catalog.pg_class c
179
164
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -186,32 +171,32 @@ module ActiveRecord
186
171
 
187
172
  # Returns the current database name.
188
173
  def current_database
189
- select_value("SELECT current_database()", "SCHEMA")
174
+ query_value("SELECT current_database()", "SCHEMA")
190
175
  end
191
176
 
192
177
  # Returns the current schema name.
193
178
  def current_schema
194
- select_value("SELECT current_schema", "SCHEMA")
179
+ query_value("SELECT current_schema", "SCHEMA")
195
180
  end
196
181
 
197
182
  # Returns the current database encoding format.
198
183
  def encoding
199
- select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
184
+ query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
200
185
  end
201
186
 
202
187
  # Returns the current database collation.
203
188
  def collation
204
- select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
189
+ query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
205
190
  end
206
191
 
207
192
  # Returns the current database ctype.
208
193
  def ctype
209
- select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
194
+ query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
210
195
  end
211
196
 
212
197
  # Returns an array of schema names.
213
198
  def schema_names
214
- select_values(<<-SQL, "SCHEMA")
199
+ query_values(<<-SQL, "SCHEMA")
215
200
  SELECT nspname
216
201
  FROM pg_namespace
217
202
  WHERE nspname !~ '^pg_.*'
@@ -232,7 +217,7 @@ module ActiveRecord
232
217
 
233
218
  # Sets the schema search path to a string of comma-separated schema names.
234
219
  # Names beginning with $ have to be quoted (e.g. $user => '$user').
235
- # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
220
+ # See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
236
221
  #
237
222
  # This should be not be called manually but set in database.yml.
238
223
  def schema_search_path=(schema_csv)
@@ -244,12 +229,12 @@ module ActiveRecord
244
229
 
245
230
  # Returns the active schema search path.
246
231
  def schema_search_path
247
- @schema_search_path ||= select_value("SHOW search_path", "SCHEMA")
232
+ @schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
248
233
  end
249
234
 
250
235
  # Returns the current client message level.
251
236
  def client_min_messages
252
- select_value("SHOW client_min_messages", "SCHEMA")
237
+ query_value("SHOW client_min_messages", "SCHEMA")
253
238
  end
254
239
 
255
240
  # Set the client message level.
@@ -267,7 +252,7 @@ module ActiveRecord
267
252
  end
268
253
 
269
254
  def serial_sequence(table, column)
270
- select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", "SCHEMA")
255
+ query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
271
256
  end
272
257
 
273
258
  # Sets the sequence of a table's primary key to the specified value.
@@ -278,7 +263,7 @@ module ActiveRecord
278
263
  if sequence
279
264
  quoted_sequence = quote_table_name(sequence)
280
265
 
281
- select_value("SELECT setval('#{quoted_sequence}', #{value})", "SCHEMA")
266
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
282
267
  else
283
268
  @logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
284
269
  end
@@ -300,10 +285,16 @@ module ActiveRecord
300
285
 
301
286
  if pk && sequence
302
287
  quoted_sequence = quote_table_name(sequence)
288
+ max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
289
+ if max_pk.nil?
290
+ if postgresql_version >= 100000
291
+ minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
292
+ else
293
+ minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
294
+ end
295
+ end
303
296
 
304
- select_value(<<-end_sql, "SCHEMA")
305
- SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
306
- end_sql
297
+ query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
307
298
  end
308
299
  end
309
300
 
@@ -327,7 +318,7 @@ module ActiveRecord
327
318
  AND seq.relnamespace = nsp.oid
328
319
  AND cons.contype = 'p'
329
320
  AND dep.classid = 'pg_class'::regclass
330
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
321
+ AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
331
322
  end_sql
332
323
 
333
324
  if result.nil? || result.empty?
@@ -345,7 +336,7 @@ module ActiveRecord
345
336
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
346
337
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
347
338
  JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
348
- WHERE t.oid = '#{quote_table_name(table)}'::regclass
339
+ WHERE t.oid = #{quote(quote_table_name(table))}::regclass
349
340
  AND cons.contype = 'p'
350
341
  AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
351
342
  end_sql
@@ -362,7 +353,7 @@ module ActiveRecord
362
353
  end
363
354
 
364
355
  def primary_keys(table_name) # :nodoc:
365
- select_values(<<-SQL.strip_heredoc, "SCHEMA")
356
+ query_values(<<-SQL.strip_heredoc, "SCHEMA")
366
357
  SELECT a.attname
367
358
  FROM (
368
359
  SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
@@ -377,6 +368,31 @@ module ActiveRecord
377
368
  SQL
378
369
  end
379
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
+
380
396
  # Renames a table.
381
397
  # Also renames a table's primary key sequence if the sequence name exists and
382
398
  # matches the Active Record default.
@@ -387,14 +403,15 @@ module ActiveRecord
387
403
  clear_cache!
388
404
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
389
405
  pk, seq = pk_and_sequence_for(new_name)
390
- if seq && seq.identifier == "#{table_name}_#{pk}_seq"
391
- new_seq = "#{new_name}_#{pk}_seq"
406
+ if pk
392
407
  idx = "#{table_name}_pkey"
393
408
  new_idx = "#{new_name}_pkey"
394
- execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
395
409
  execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
410
+ if seq && seq.identifier == "#{table_name}_#{pk}_seq"
411
+ new_seq = "#{new_name}_#{pk}_seq"
412
+ execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
413
+ end
396
414
  end
397
-
398
415
  rename_table_indexes(table_name, new_name)
399
416
  end
400
417
 
@@ -406,50 +423,23 @@ module ActiveRecord
406
423
 
407
424
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
408
425
  clear_cache!
409
- quoted_table_name = quote_table_name(table_name)
410
- quoted_column_name = quote_column_name(column_name)
411
- sql_type = type_to_sql(type, options)
412
- sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
413
- if options[:collation]
414
- sql << " COLLATE \"#{options[:collation]}\""
415
- end
416
- if options[:using]
417
- sql << " USING #{options[:using]}"
418
- elsif options[:cast_as]
419
- cast_as_type = type_to_sql(options[:cast_as], options)
420
- sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
421
- end
422
- execute sql
423
-
424
- change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
425
- change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
426
- change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
426
+ sqls, procs = Array(change_column_for_alter(table_name, column_name, type, options)).partition { |v| v.is_a?(String) }
427
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
428
+ procs.each(&:call)
427
429
  end
428
430
 
429
431
  # Changes the default value of a table column.
430
432
  def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
431
- clear_cache!
432
- column = column_for(table_name, column_name)
433
- return unless column
434
-
435
- default = extract_new_default_value(default_or_changes)
436
- alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
437
- if default.nil?
438
- # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
439
- # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
440
- execute alter_column_query % "DROP DEFAULT"
441
- else
442
- execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
443
- end
433
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
444
434
  end
445
435
 
446
436
  def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
447
437
  clear_cache!
448
438
  unless null || default.nil?
449
439
  column = column_for(table_name, column_name)
450
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
440
+ execute "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL" if column
451
441
  end
452
- execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
442
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
453
443
  end
454
444
 
455
445
  # Adds comment for given table column or drops it if +comment+ is a +nil+
@@ -472,8 +462,8 @@ module ActiveRecord
472
462
  end
473
463
 
474
464
  def add_index(table_name, column_name, options = {}) #:nodoc:
475
- index_name, index_type, index_columns, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
476
- execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}").tap do
465
+ index_name, index_type, index_columns_and_opclasses, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
466
+ 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
477
467
  execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
478
468
  end
479
469
  end
@@ -512,8 +502,8 @@ module ActiveRecord
512
502
 
513
503
  def foreign_keys(table_name)
514
504
  scope = quoted_scope(table_name)
515
- fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
516
- 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
505
+ fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
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
517
507
  FROM pg_constraint c
518
508
  JOIN pg_class t1 ON c.conrelid = t1.oid
519
509
  JOIN pg_class t2 ON c.confrelid = t2.oid
@@ -535,17 +525,18 @@ module ActiveRecord
535
525
 
536
526
  options[:on_delete] = extract_foreign_key_action(row["on_delete"])
537
527
  options[:on_update] = extract_foreign_key_action(row["on_update"])
528
+ options[:validate] = row["valid"]
538
529
 
539
530
  ForeignKeyDefinition.new(table_name, row["to_table"], options)
540
531
  end
541
532
  end
542
533
 
543
- def extract_foreign_key_action(specifier) # :nodoc:
544
- case specifier
545
- when "c"; :cascade
546
- when "n"; :nullify
547
- when "r"; :restrict
548
- end
534
+ def foreign_tables
535
+ query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
536
+ end
537
+
538
+ def foreign_table_exists?(table_name)
539
+ query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
549
540
  end
550
541
 
551
542
  # Maps logical Rails types to PostgreSQL-specific data types.
@@ -577,7 +568,7 @@ module ActiveRecord
577
568
  super
578
569
  end
579
570
 
580
- sql << "[]" if array && type != :primary_key
571
+ sql = "#{sql}[]" if array && type != :primary_key
581
572
  sql
582
573
  end
583
574
 
@@ -595,24 +586,174 @@ module ActiveRecord
595
586
  [super, *order_columns].join(", ")
596
587
  end
597
588
 
598
- def fetch_type_metadata(column_name, sql_type, oid, fmod)
599
- cast_type = get_oid_type(oid, fmod, column_name, sql_type)
600
- simple_type = SqlTypeMetadata.new(
601
- sql_type: sql_type,
602
- type: cast_type.type,
603
- limit: cast_type.limit,
604
- precision: cast_type.precision,
605
- scale: cast_type.scale,
606
- )
607
- PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
589
+ def update_table_definition(table_name, base) # :nodoc:
590
+ PostgreSQL::Table.new(table_name, base)
591
+ end
592
+
593
+ def create_schema_dumper(options) # :nodoc:
594
+ PostgreSQL::SchemaDumper.create(self, options)
595
+ end
596
+
597
+ # Validates the given constraint.
598
+ #
599
+ # Validates the constraint named +constraint_name+ on +accounts+.
600
+ #
601
+ # validate_constraint :accounts, :constraint_name
602
+ def validate_constraint(table_name, constraint_name)
603
+ return unless supports_validate_constraints?
604
+
605
+ at = create_alter_table table_name
606
+ at.validate_constraint constraint_name
607
+
608
+ execute schema_creation.accept(at)
609
+ end
610
+
611
+ # Validates the given foreign key.
612
+ #
613
+ # Validates the foreign key on +accounts.branch_id+.
614
+ #
615
+ # validate_foreign_key :accounts, :branches
616
+ #
617
+ # Validates the foreign key on +accounts.owner_id+.
618
+ #
619
+ # validate_foreign_key :accounts, column: :owner_id
620
+ #
621
+ # Validates the foreign key named +special_fk_name+ on the +accounts+ table.
622
+ #
623
+ # validate_foreign_key :accounts, name: :special_fk_name
624
+ #
625
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
626
+ def validate_foreign_key(from_table, options_or_to_table = {})
627
+ return unless supports_validate_constraints?
628
+
629
+ fk_name_to_validate = foreign_key_for!(from_table, options_or_to_table).name
630
+
631
+ validate_constraint from_table, fk_name_to_validate
608
632
  end
609
633
 
610
634
  private
635
+ def schema_creation
636
+ PostgreSQL::SchemaCreation.new(self)
637
+ end
638
+
639
+ def create_table_definition(*args)
640
+ PostgreSQL::TableDefinition.new(*args)
641
+ end
642
+
643
+ def create_alter_table(name)
644
+ PostgreSQL::AlterTable.new create_table_definition(name)
645
+ end
646
+
647
+ def new_column_from_field(table_name, field)
648
+ column_name, type, default, notnull, oid, fmod, collation, comment = field
649
+ type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
650
+ default_value = extract_value_from_default(default)
651
+ default_function = extract_default_function(default_value, default)
652
+
653
+ PostgreSQLColumn.new(
654
+ column_name,
655
+ default_value,
656
+ type_metadata,
657
+ !notnull,
658
+ table_name,
659
+ default_function,
660
+ collation,
661
+ comment: comment.presence,
662
+ max_identifier_length: max_identifier_length
663
+ )
664
+ end
665
+
666
+ def fetch_type_metadata(column_name, sql_type, oid, fmod)
667
+ cast_type = get_oid_type(oid, fmod, column_name, sql_type)
668
+ simple_type = SqlTypeMetadata.new(
669
+ sql_type: sql_type,
670
+ type: cast_type.type,
671
+ limit: cast_type.limit,
672
+ precision: cast_type.precision,
673
+ scale: cast_type.scale,
674
+ )
675
+ PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
676
+ end
677
+
678
+ def extract_foreign_key_action(specifier)
679
+ case specifier
680
+ when "c"; :cascade
681
+ when "n"; :nullify
682
+ when "r"; :restrict
683
+ end
684
+ end
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}".dup
690
+ if options[:collation]
691
+ sql << " COLLATE \"#{options[:collation]}\""
692
+ end
693
+ if options[:using]
694
+ sql << " USING #{options[:using]}"
695
+ elsif options[:cast_as]
696
+ cast_as_type = type_to_sql(options[:cast_as], options)
697
+ sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
698
+ end
699
+
700
+ sql
701
+ end
702
+
703
+ def change_column_for_alter(table_name, column_name, type, options = {})
704
+ sqls = [change_column_sql(table_name, column_name, type, options)]
705
+ sqls << change_column_default_for_alter(table_name, column_name, options[:default]) if options.key?(:default)
706
+ sqls << change_column_null_for_alter(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
707
+ sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
708
+ sqls
709
+ end
710
+
711
+
712
+ # Changes the default value of a table column.
713
+ def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc:
714
+ column = column_for(table_name, column_name)
715
+ return unless column
716
+
717
+ default = extract_new_default_value(default_or_changes)
718
+ alter_column_query = "ALTER COLUMN #{quote_column_name(column_name)} %s"
719
+ if default.nil?
720
+ # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
721
+ # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
722
+ alter_column_query % "DROP DEFAULT"
723
+ else
724
+ alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
725
+ end
726
+ end
727
+
728
+ def change_column_null_for_alter(table_name, column_name, null, default = nil) #:nodoc:
729
+ "ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
730
+ end
731
+
732
+ def add_timestamps_for_alter(table_name, options = {})
733
+ [add_column_for_alter(table_name, :created_at, :datetime, options), add_column_for_alter(table_name, :updated_at, :datetime, options)]
734
+ end
735
+
736
+ def remove_timestamps_for_alter(table_name, options = {})
737
+ [remove_column_for_alter(table_name, :updated_at), remove_column_for_alter(table_name, :created_at)]
738
+ end
739
+
740
+ def add_index_opclass(quoted_columns, **options)
741
+ opclasses = options_for_index_columns(options[:opclass])
742
+ quoted_columns.each do |name, column|
743
+ column << " #{opclasses[name]}" if opclasses[name].present?
744
+ end
745
+ end
746
+
747
+ def add_options_for_index_columns(quoted_columns, **options)
748
+ quoted_columns = add_index_opclass(quoted_columns, options)
749
+ super
750
+ end
751
+
611
752
  def data_source_sql(name = nil, type: nil)
612
753
  scope = quoted_scope(name, type: type)
613
- scope[:type] ||= "'r','v','m'" # (r)elation/table, (v)iew, (m)aterialized view
754
+ scope[:type] ||= "'r','v','m','f'" # (r)elation/table, (v)iew, (m)aterialized view, (f)oreign table
614
755
 
615
- sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
756
+ sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
616
757
  sql << " WHERE n.nspname = #{scope[:schema]}"
617
758
  sql << " AND c.relname = #{scope[:name]}" if scope[:name]
618
759
  sql << " AND c.relkind IN (#{scope[:type]})"
@@ -627,6 +768,8 @@ module ActiveRecord
627
768
  "'r'"
628
769
  when "VIEW"
629
770
  "'v','m'"
771
+ when "FOREIGN TABLE"
772
+ "'f'"
630
773
  end
631
774
  scope = {}
632
775
  scope[:schema] = schema ? quote(schema) : "ANY (current_schemas(false))"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL