activerecord 4.2.0 → 5.0.0

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1537 -789
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +7 -8
  5. data/examples/performance.rb +2 -3
  6. data/examples/simple.rb +0 -1
  7. data/lib/active_record/aggregations.rb +37 -23
  8. data/lib/active_record/association_relation.rb +16 -3
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +23 -9
  11. data/lib/active_record/associations/association_scope.rb +74 -102
  12. data/lib/active_record/associations/belongs_to_association.rb +26 -29
  13. data/lib/active_record/associations/builder/association.rb +28 -34
  14. data/lib/active_record/associations/builder/belongs_to.rb +43 -18
  15. data/lib/active_record/associations/builder/collection_association.rb +12 -20
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +11 -6
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -10
  20. data/lib/active_record/associations/collection_association.rb +61 -33
  21. data/lib/active_record/associations/collection_proxy.rb +81 -35
  22. data/lib/active_record/associations/foreign_association.rb +11 -0
  23. data/lib/active_record/associations/has_many_association.rb +21 -57
  24. data/lib/active_record/associations/has_many_through_association.rb +15 -45
  25. data/lib/active_record/associations/has_one_association.rb +13 -5
  26. data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
  27. data/lib/active_record/associations/join_dependency.rb +37 -21
  28. data/lib/active_record/associations/preloader/association.rb +51 -53
  29. data/lib/active_record/associations/preloader/collection_association.rb +0 -6
  30. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  31. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  32. data/lib/active_record/associations/preloader/through_association.rb +27 -14
  33. data/lib/active_record/associations/preloader.rb +18 -8
  34. data/lib/active_record/associations/singular_association.rb +8 -8
  35. data/lib/active_record/associations/through_association.rb +22 -9
  36. data/lib/active_record/associations.rb +321 -212
  37. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  38. data/lib/active_record/attribute.rb +79 -15
  39. data/lib/active_record/attribute_assignment.rb +20 -141
  40. data/lib/active_record/attribute_decorators.rb +6 -5
  41. data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
  42. data/lib/active_record/attribute_methods/dirty.rb +51 -81
  43. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  44. data/lib/active_record/attribute_methods/query.rb +2 -2
  45. data/lib/active_record/attribute_methods/read.rb +31 -59
  46. data/lib/active_record/attribute_methods/serialization.rb +13 -16
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
  48. data/lib/active_record/attribute_methods/write.rb +14 -38
  49. data/lib/active_record/attribute_methods.rb +70 -45
  50. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  51. data/lib/active_record/attribute_set/builder.rb +37 -15
  52. data/lib/active_record/attribute_set.rb +34 -3
  53. data/lib/active_record/attributes.rb +199 -73
  54. data/lib/active_record/autosave_association.rb +73 -25
  55. data/lib/active_record/base.rb +35 -27
  56. data/lib/active_record/callbacks.rb +39 -43
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +20 -8
  59. data/lib/active_record/collection_cache_key.rb +40 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
  65. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
  66. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
  67. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
  68. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
  70. data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
  71. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
  72. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
  73. data/lib/active_record/connection_adapters/column.rb +28 -43
  74. data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
  75. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  78. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  79. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  81. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  82. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  83. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
  85. data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
  86. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
  87. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
  95. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
  98. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
  99. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
  101. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  102. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
  106. data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
  107. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
  108. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
  109. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
  111. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  112. data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
  113. data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
  114. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  115. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  116. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
  119. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  120. data/lib/active_record/connection_handling.rb +38 -15
  121. data/lib/active_record/core.rb +109 -114
  122. data/lib/active_record/counter_cache.rb +14 -25
  123. data/lib/active_record/dynamic_matchers.rb +1 -20
  124. data/lib/active_record/enum.rb +115 -79
  125. data/lib/active_record/errors.rb +88 -48
  126. data/lib/active_record/explain_registry.rb +1 -1
  127. data/lib/active_record/explain_subscriber.rb +2 -2
  128. data/lib/active_record/fixture_set/file.rb +26 -5
  129. data/lib/active_record/fixtures.rb +84 -46
  130. data/lib/active_record/gem_version.rb +2 -2
  131. data/lib/active_record/inheritance.rb +32 -40
  132. data/lib/active_record/integration.rb +4 -4
  133. data/lib/active_record/internal_metadata.rb +56 -0
  134. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  135. data/lib/active_record/locale/en.yml +3 -2
  136. data/lib/active_record/locking/optimistic.rb +27 -25
  137. data/lib/active_record/locking/pessimistic.rb +1 -1
  138. data/lib/active_record/log_subscriber.rb +43 -21
  139. data/lib/active_record/migration/command_recorder.rb +59 -18
  140. data/lib/active_record/migration/compatibility.rb +126 -0
  141. data/lib/active_record/migration.rb +372 -114
  142. data/lib/active_record/model_schema.rb +128 -38
  143. data/lib/active_record/nested_attributes.rb +71 -32
  144. data/lib/active_record/no_touching.rb +1 -1
  145. data/lib/active_record/null_relation.rb +16 -8
  146. data/lib/active_record/persistence.rb +124 -80
  147. data/lib/active_record/query_cache.rb +15 -18
  148. data/lib/active_record/querying.rb +10 -9
  149. data/lib/active_record/railtie.rb +28 -19
  150. data/lib/active_record/railties/controller_runtime.rb +1 -1
  151. data/lib/active_record/railties/databases.rake +67 -51
  152. data/lib/active_record/readonly_attributes.rb +1 -1
  153. data/lib/active_record/reflection.rb +318 -139
  154. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  155. data/lib/active_record/relation/batches.rb +139 -34
  156. data/lib/active_record/relation/calculations.rb +80 -102
  157. data/lib/active_record/relation/delegation.rb +7 -20
  158. data/lib/active_record/relation/finder_methods.rb +167 -97
  159. data/lib/active_record/relation/from_clause.rb +32 -0
  160. data/lib/active_record/relation/merger.rb +38 -41
  161. data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
  162. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  163. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  164. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  165. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  167. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  168. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  169. data/lib/active_record/relation/predicate_builder.rb +124 -82
  170. data/lib/active_record/relation/query_attribute.rb +19 -0
  171. data/lib/active_record/relation/query_methods.rb +323 -257
  172. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  173. data/lib/active_record/relation/spawn_methods.rb +11 -10
  174. data/lib/active_record/relation/where_clause.rb +174 -0
  175. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  176. data/lib/active_record/relation.rb +176 -115
  177. data/lib/active_record/result.rb +4 -3
  178. data/lib/active_record/runtime_registry.rb +1 -1
  179. data/lib/active_record/sanitization.rb +95 -66
  180. data/lib/active_record/schema.rb +26 -22
  181. data/lib/active_record/schema_dumper.rb +62 -38
  182. data/lib/active_record/schema_migration.rb +11 -17
  183. data/lib/active_record/scoping/default.rb +24 -9
  184. data/lib/active_record/scoping/named.rb +49 -28
  185. data/lib/active_record/scoping.rb +32 -15
  186. data/lib/active_record/secure_token.rb +38 -0
  187. data/lib/active_record/serialization.rb +2 -4
  188. data/lib/active_record/statement_cache.rb +16 -14
  189. data/lib/active_record/store.rb +8 -3
  190. data/lib/active_record/suppressor.rb +58 -0
  191. data/lib/active_record/table_metadata.rb +68 -0
  192. data/lib/active_record/tasks/database_tasks.rb +59 -42
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  196. data/lib/active_record/timestamp.rb +20 -9
  197. data/lib/active_record/touch_later.rb +58 -0
  198. data/lib/active_record/transactions.rb +159 -67
  199. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  200. data/lib/active_record/type/date.rb +2 -41
  201. data/lib/active_record/type/date_time.rb +2 -38
  202. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  203. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  204. data/lib/active_record/type/internal/timezone.rb +15 -0
  205. data/lib/active_record/type/serialized.rb +21 -14
  206. data/lib/active_record/type/time.rb +10 -16
  207. data/lib/active_record/type/type_map.rb +4 -4
  208. data/lib/active_record/type.rb +66 -17
  209. data/lib/active_record/type_caster/connection.rb +29 -0
  210. data/lib/active_record/type_caster/map.rb +19 -0
  211. data/lib/active_record/type_caster.rb +7 -0
  212. data/lib/active_record/validations/absence.rb +23 -0
  213. data/lib/active_record/validations/associated.rb +10 -3
  214. data/lib/active_record/validations/length.rb +24 -0
  215. data/lib/active_record/validations/presence.rb +11 -12
  216. data/lib/active_record/validations/uniqueness.rb +29 -18
  217. data/lib/active_record/validations.rb +33 -32
  218. data/lib/active_record.rb +9 -2
  219. data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
  220. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
  221. data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
  222. data/lib/rails/generators/active_record/migration.rb +7 -0
  223. data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
  224. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  225. data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
  226. metadata +60 -34
  227. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  228. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  229. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
  230. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  231. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  232. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
  233. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  234. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  235. data/lib/active_record/type/big_integer.rb +0 -13
  236. data/lib/active_record/type/binary.rb +0 -50
  237. data/lib/active_record/type/boolean.rb +0 -30
  238. data/lib/active_record/type/decimal.rb +0 -40
  239. data/lib/active_record/type/decimal_without_scale.rb +0 -11
  240. data/lib/active_record/type/decorator.rb +0 -14
  241. data/lib/active_record/type/float.rb +0 -19
  242. data/lib/active_record/type/integer.rb +0 -55
  243. data/lib/active_record/type/mutable.rb +0 -16
  244. data/lib/active_record/type/numeric.rb +0 -36
  245. data/lib/active_record/type/string.rb +0 -36
  246. data/lib/active_record/type/text.rb +0 -11
  247. data/lib/active_record/type/time_value.rb +0 -38
  248. data/lib/active_record/type/unsigned_integer.rb +0 -15
  249. data/lib/active_record/type/value.rb +0 -101
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/string/strip'
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module PostgreSQL
@@ -5,33 +7,15 @@ module ActiveRecord
5
7
  private
6
8
 
7
9
  def visit_ColumnDefinition(o)
8
- sql = super
9
- if o.primary_key? && o.type != :primary_key
10
- sql << " PRIMARY KEY "
11
- add_column_options!(sql, column_options(o))
12
- end
13
- sql
10
+ o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
11
+ super
14
12
  end
15
13
 
16
14
  def add_column_options!(sql, options)
17
- if options[:array] || options[:column].try(:array)
18
- sql << '[]'
19
- end
20
-
21
- column = options.fetch(:column) { return super }
22
- if column.type == :uuid && options[:default] =~ /\(\)/
23
- sql << " DEFAULT #{options[:default]}"
24
- else
25
- super
26
- end
27
- end
28
-
29
- def type_for_column(column)
30
- if column.array
31
- @conn.lookup_cast_type("#{column.sql_type}[]")
32
- else
33
- super
15
+ if options[:collation]
16
+ sql << " COLLATE \"#{options[:collation]}\""
34
17
  end
18
+ super
35
19
  end
36
20
  end
37
21
 
@@ -86,12 +70,24 @@ module ActiveRecord
86
70
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
87
71
  end
88
72
 
89
- # Returns the list of all tables in the schema search path or a specified schema.
73
+ # Returns the list of all tables in the schema search path.
90
74
  def tables(name = nil)
91
- query(<<-SQL, 'SCHEMA').map { |row| row[0] }
92
- SELECT tablename
93
- FROM pg_tables
94
- WHERE schemaname = ANY (current_schemas(false))
75
+ if name
76
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
77
+ Passing arguments to #tables is deprecated without replacement.
78
+ MSG
79
+ end
80
+
81
+ select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
82
+ end
83
+
84
+ def data_sources # :nodoc
85
+ select_values(<<-SQL, 'SCHEMA')
86
+ SELECT c.relname
87
+ FROM pg_class c
88
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
89
+ WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
90
+ AND n.nspname = ANY (current_schemas(false))
95
91
  SQL
96
92
  end
97
93
 
@@ -99,10 +95,20 @@ module ActiveRecord
99
95
  # If the schema is not specified as part of +name+ then it will only find tables within
100
96
  # the current schema search path (regardless of permissions to access tables in other schemas)
101
97
  def table_exists?(name)
98
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
99
+ #table_exists? currently checks both tables and views.
100
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
101
+ Use #data_source_exists? instead.
102
+ MSG
103
+
104
+ data_source_exists?(name)
105
+ end
106
+
107
+ def data_source_exists?(name)
102
108
  name = Utils.extract_schema_qualified_name(name.to_s)
103
109
  return false unless name.identifier
104
110
 
105
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
111
+ select_value(<<-SQL, 'SCHEMA').to_i > 0
106
112
  SELECT COUNT(*)
107
113
  FROM pg_class c
108
114
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -112,124 +118,169 @@ module ActiveRecord
112
118
  SQL
113
119
  end
114
120
 
115
- def drop_table(table_name, options = {})
116
- execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
121
+ def views # :nodoc:
122
+ select_values(<<-SQL, 'SCHEMA')
123
+ SELECT c.relname
124
+ FROM pg_class c
125
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
126
+ WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
127
+ AND n.nspname = ANY (current_schemas(false))
128
+ SQL
129
+ end
130
+
131
+ def view_exists?(view_name) # :nodoc:
132
+ name = Utils.extract_schema_qualified_name(view_name.to_s)
133
+ return false unless name.identifier
134
+
135
+ select_values(<<-SQL, 'SCHEMA').any?
136
+ SELECT c.relname
137
+ FROM pg_class c
138
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
139
+ WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
140
+ AND c.relname = '#{name.identifier}'
141
+ AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
142
+ SQL
143
+ end
144
+
145
+ def drop_table(table_name, options = {}) # :nodoc:
146
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
117
147
  end
118
148
 
119
149
  # Returns true if schema exists.
120
150
  def schema_exists?(name)
121
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
122
- SELECT COUNT(*)
123
- FROM pg_namespace
124
- WHERE nspname = '#{name}'
125
- SQL
151
+ select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
126
152
  end
127
153
 
154
+ # Verifies existence of an index with a given name.
128
155
  def index_name_exists?(table_name, index_name, default)
129
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
156
+ table = Utils.extract_schema_qualified_name(table_name.to_s)
157
+ index = Utils.extract_schema_qualified_name(index_name.to_s)
158
+
159
+ select_value(<<-SQL, 'SCHEMA').to_i > 0
130
160
  SELECT COUNT(*)
131
161
  FROM pg_class t
132
162
  INNER JOIN pg_index d ON t.oid = d.indrelid
133
163
  INNER JOIN pg_class i ON d.indexrelid = i.oid
164
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
134
165
  WHERE i.relkind = 'i'
135
- AND i.relname = '#{index_name}'
136
- AND t.relname = '#{table_name}'
137
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
166
+ AND i.relname = '#{index.identifier}'
167
+ AND t.relname = '#{table.identifier}'
168
+ AND n.nspname = #{index.schema ? "'#{index.schema}'" : 'ANY (current_schemas(false))'}
138
169
  SQL
139
170
  end
140
171
 
141
172
  # Returns an array of indexes for the given table.
142
173
  def indexes(table_name, name = nil)
143
- result = query(<<-SQL, 'SCHEMA')
144
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
145
- FROM pg_class t
146
- INNER JOIN pg_index d ON t.oid = d.indrelid
147
- INNER JOIN pg_class i ON d.indexrelid = i.oid
148
- WHERE i.relkind = 'i'
149
- AND d.indisprimary = 'f'
150
- AND t.relname = '#{table_name}'
151
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
174
+ table = Utils.extract_schema_qualified_name(table_name.to_s)
175
+
176
+ result = query(<<-SQL, 'SCHEMA')
177
+ SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
178
+ pg_catalog.obj_description(i.oid, 'pg_class') AS comment,
179
+ (SELECT COUNT(*) FROM pg_opclass o
180
+ JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
181
+ ON o.oid = c.oid WHERE o.opcdefault = 'f')
182
+ FROM pg_class t
183
+ INNER JOIN pg_index d ON t.oid = d.indrelid
184
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
185
+ LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
186
+ WHERE i.relkind = 'i'
187
+ AND d.indisprimary = 'f'
188
+ AND t.relname = '#{table.identifier}'
189
+ AND n.nspname = #{table.schema ? "'#{table.schema}'" : 'ANY (current_schemas(false))'}
152
190
  ORDER BY i.relname
153
191
  SQL
154
192
 
155
193
  result.map do |row|
156
194
  index_name = row[0]
157
- unique = row[1] == 't'
158
- indkey = row[2].split(" ")
195
+ unique = row[1]
196
+ indkey = row[2].split(" ").map(&:to_i)
159
197
  inddef = row[3]
160
198
  oid = row[4]
199
+ comment = row[5]
200
+ opclass = row[6]
161
201
 
162
- columns = Hash[query(<<-SQL, "SCHEMA")]
163
- SELECT a.attnum, a.attname
164
- FROM pg_attribute a
165
- WHERE a.attrelid = #{oid}
166
- AND a.attnum IN (#{indkey.join(",")})
167
- SQL
202
+ using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
168
203
 
169
- column_names = columns.values_at(*indkey).compact
204
+ if indkey.include?(0) || opclass > 0
205
+ columns = expressions
206
+ else
207
+ columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
208
+ SELECT a.attnum, a.attname
209
+ FROM pg_attribute a
210
+ WHERE a.attrelid = #{oid}
211
+ AND a.attnum IN (#{indkey.join(",")})
212
+ SQL
170
213
 
171
- unless column_names.empty?
172
214
  # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
173
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
174
- orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
175
- where = inddef.scan(/WHERE (.+)$/).flatten[0]
176
- using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
177
-
178
- IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
215
+ orders = Hash[
216
+ expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] }
217
+ ]
179
218
  end
219
+
220
+ IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment.presence)
180
221
  end.compact
181
222
  end
182
223
 
183
224
  # Returns the list of all column definitions for a table.
184
- def columns(table_name)
185
- # Limit, precision, and scale are all handled by the superclass.
186
- column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
187
- oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type)
188
- default_value = extract_value_from_default(oid, default)
225
+ def columns(table_name) # :nodoc:
226
+ table_name = table_name.to_s
227
+ column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation, comment|
228
+ oid = oid.to_i
229
+ fmod = fmod.to_i
230
+ type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
231
+ default_value = extract_value_from_default(default)
189
232
  default_function = extract_default_function(default_value, default)
190
- new_column(column_name, default_value, oid, type, notnull == 'f', default_function)
233
+ new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation, comment: comment.presence)
191
234
  end
192
235
  end
193
236
 
194
- def new_column(name, default, cast_type, sql_type = nil, null = true, default_function = nil) # :nodoc:
195
- PostgreSQLColumn.new(name, default, cast_type, sql_type, null, default_function)
237
+ def new_column(*args) # :nodoc:
238
+ PostgreSQLColumn.new(*args)
239
+ end
240
+
241
+ # Returns a comment stored in database for given table
242
+ def table_comment(table_name) # :nodoc:
243
+ name = Utils.extract_schema_qualified_name(table_name.to_s)
244
+ if name.identifier
245
+ select_value(<<-SQL.strip_heredoc, 'SCHEMA')
246
+ SELECT pg_catalog.obj_description(c.oid, 'pg_class')
247
+ FROM pg_catalog.pg_class c
248
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
249
+ WHERE c.relname = #{quote(name.identifier)}
250
+ AND c.relkind IN ('r') -- (r)elation/table
251
+ AND n.nspname = #{name.schema ? quote(name.schema) : 'ANY (current_schemas(false))'}
252
+ SQL
253
+ end
196
254
  end
197
255
 
198
256
  # Returns the current database name.
199
257
  def current_database
200
- query('select current_database()', 'SCHEMA')[0][0]
258
+ select_value('select current_database()', 'SCHEMA')
201
259
  end
202
260
 
203
261
  # Returns the current schema name.
204
262
  def current_schema
205
- query('SELECT current_schema', 'SCHEMA')[0][0]
263
+ select_value('SELECT current_schema', 'SCHEMA')
206
264
  end
207
265
 
208
266
  # Returns the current database encoding format.
209
267
  def encoding
210
- query(<<-end_sql, 'SCHEMA')[0][0]
211
- SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
212
- WHERE pg_database.datname LIKE '#{current_database}'
213
- end_sql
268
+ select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
214
269
  end
215
270
 
216
271
  # Returns the current database collation.
217
272
  def collation
218
- query(<<-end_sql, 'SCHEMA')[0][0]
219
- SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
220
- end_sql
273
+ select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
221
274
  end
222
275
 
223
276
  # Returns the current database ctype.
224
277
  def ctype
225
- query(<<-end_sql, 'SCHEMA')[0][0]
226
- SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
227
- end_sql
278
+ select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
228
279
  end
229
280
 
230
281
  # Returns an array of schema names.
231
282
  def schema_names
232
- query(<<-SQL, 'SCHEMA').flatten
283
+ select_values(<<-SQL, 'SCHEMA')
233
284
  SELECT nspname
234
285
  FROM pg_namespace
235
286
  WHERE nspname !~ '^pg_.*'
@@ -240,12 +291,12 @@ module ActiveRecord
240
291
 
241
292
  # Creates a schema for the given schema name.
242
293
  def create_schema schema_name
243
- execute "CREATE SCHEMA #{schema_name}"
294
+ execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
244
295
  end
245
296
 
246
297
  # Drops the schema for the given schema name.
247
- def drop_schema schema_name
248
- execute "DROP SCHEMA #{schema_name} CASCADE"
298
+ def drop_schema(schema_name, options = {})
299
+ execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
249
300
  end
250
301
 
251
302
  # Sets the schema search path to a string of comma-separated schema names.
@@ -262,12 +313,12 @@ module ActiveRecord
262
313
 
263
314
  # Returns the active schema search path.
264
315
  def schema_search_path
265
- @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
316
+ @schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
266
317
  end
267
318
 
268
319
  # Returns the current client message level.
269
320
  def client_min_messages
270
- query('SHOW client_min_messages', 'SCHEMA')[0][0]
321
+ select_value('SHOW client_min_messages', 'SCHEMA')
271
322
  end
272
323
 
273
324
  # Set the client message level.
@@ -285,10 +336,7 @@ module ActiveRecord
285
336
  end
286
337
 
287
338
  def serial_sequence(table, column)
288
- result = exec_query(<<-eosql, 'SCHEMA')
289
- SELECT pg_get_serial_sequence('#{table}', '#{column}')
290
- eosql
291
- result.rows.first.first
339
+ select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", 'SCHEMA')
292
340
  end
293
341
 
294
342
  # Sets the sequence of a table's primary key to the specified value.
@@ -299,11 +347,9 @@ module ActiveRecord
299
347
  if sequence
300
348
  quoted_sequence = quote_table_name(sequence)
301
349
 
302
- select_value <<-end_sql, 'SCHEMA'
303
- SELECT setval('#{quoted_sequence}', #{value})
304
- end_sql
350
+ select_value("SELECT setval('#{quoted_sequence}', #{value})", 'SCHEMA')
305
351
  else
306
- @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
352
+ @logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
307
353
  end
308
354
  end
309
355
  end
@@ -318,13 +364,13 @@ module ActiveRecord
318
364
  end
319
365
 
320
366
  if @logger && pk && !sequence
321
- @logger.warn "#{table} has primary key #{pk} with no default sequence"
367
+ @logger.warn "#{table} has primary key #{pk} with no default sequence."
322
368
  end
323
369
 
324
370
  if pk && sequence
325
371
  quoted_sequence = quote_table_name(sequence)
326
372
 
327
- select_value <<-end_sql, 'SCHEMA'
373
+ select_value(<<-end_sql, 'SCHEMA')
328
374
  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)
329
375
  end_sql
330
376
  end
@@ -384,17 +430,19 @@ module ActiveRecord
384
430
  nil
385
431
  end
386
432
 
387
- # Returns just a table's primary key
388
- def primary_key(table)
389
- row = exec_query(<<-end_sql, 'SCHEMA').rows.first
390
- SELECT attr.attname
391
- FROM pg_attribute attr
392
- INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
393
- WHERE cons.contype = 'p'
394
- AND cons.conrelid = '#{quote_table_name(table)}'::regclass
395
- end_sql
396
-
397
- row && row.first
433
+ def primary_keys(table_name) # :nodoc:
434
+ select_values(<<-SQL.strip_heredoc, 'SCHEMA')
435
+ WITH pk_constraint AS (
436
+ SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
437
+ WHERE contype = 'p'
438
+ AND conrelid = '#{quote_table_name(table_name)}'::regclass
439
+ ), cons AS (
440
+ SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
441
+ )
442
+ SELECT attr.attname FROM pg_attribute attr
443
+ INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
444
+ ORDER BY cons.rownum
445
+ SQL
398
446
  end
399
447
 
400
448
  # Renames a table.
@@ -411,82 +459,122 @@ module ActiveRecord
411
459
  new_seq = "#{new_name}_#{pk}_seq"
412
460
  idx = "#{table_name}_pkey"
413
461
  new_idx = "#{new_name}_pkey"
414
- execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
462
+ execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
415
463
  execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
416
464
  end
417
465
 
418
466
  rename_table_indexes(table_name, new_name)
419
467
  end
420
468
 
421
- # Adds a new column to the named table.
422
- # See TableDefinition#column for details of the options you can use.
423
- def add_column(table_name, column_name, type, options = {})
469
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
424
470
  clear_cache!
425
471
  super
472
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
426
473
  end
427
474
 
428
- # Changes the column of a table.
429
- def change_column(table_name, column_name, type, options = {})
475
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
430
476
  clear_cache!
431
477
  quoted_table_name = quote_table_name(table_name)
432
- sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
433
- sql_type << "[]" if options[:array]
434
- sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
435
- sql << " USING #{options[:using]}" if options[:using]
436
- if options[:cast_as]
437
- sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
478
+ quoted_column_name = quote_column_name(column_name)
479
+ sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale], options[:array])
480
+ sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
481
+ if options[:collation]
482
+ sql << " COLLATE \"#{options[:collation]}\""
483
+ end
484
+ if options[:using]
485
+ sql << " USING #{options[:using]}"
486
+ elsif options[:cast_as]
487
+ cast_as_type = type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale], options[:array])
488
+ sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
438
489
  end
439
490
  execute sql
440
491
 
441
492
  change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
442
493
  change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
494
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
443
495
  end
444
496
 
445
497
  # Changes the default value of a table column.
446
- def change_column_default(table_name, column_name, default)
498
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
447
499
  clear_cache!
448
500
  column = column_for(table_name, column_name)
449
501
  return unless column
450
502
 
503
+ default = extract_new_default_value(default_or_changes)
451
504
  alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
452
505
  if default.nil?
453
506
  # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
454
507
  # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
455
508
  execute alter_column_query % "DROP DEFAULT"
456
509
  else
457
- execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
510
+ execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
458
511
  end
459
512
  end
460
513
 
461
- def change_column_null(table_name, column_name, null, default = nil)
514
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
462
515
  clear_cache!
463
516
  unless null || default.nil?
464
517
  column = column_for(table_name, column_name)
465
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
518
+ 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
466
519
  end
467
520
  execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
468
521
  end
469
522
 
523
+ # Adds comment for given table column or drops it if +comment+ is a +nil+
524
+ def change_column_comment(table_name, column_name, comment) # :nodoc:
525
+ clear_cache!
526
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
527
+ end
528
+
529
+ # Adds comment for given table or drops it if +comment+ is a +nil+
530
+ def change_table_comment(table_name, comment) # :nodoc:
531
+ clear_cache!
532
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
533
+ end
534
+
470
535
  # Renames a column in a table.
471
- def rename_column(table_name, column_name, new_column_name)
536
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
472
537
  clear_cache!
473
538
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
474
539
  rename_column_indexes(table_name, column_name, new_column_name)
475
540
  end
476
541
 
477
542
  def add_index(table_name, column_name, options = {}) #:nodoc:
478
- index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
479
- execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
543
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
544
+ 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
545
+ execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
546
+ end
480
547
  end
481
548
 
482
- def remove_index!(table_name, index_name) #:nodoc:
483
- execute "DROP INDEX #{quote_table_name(index_name)}"
549
+ def remove_index(table_name, options = {}) #:nodoc:
550
+ table = Utils.extract_schema_qualified_name(table_name.to_s)
551
+
552
+ if options.is_a?(Hash) && options.key?(:name)
553
+ provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
554
+
555
+ options[:name] = provided_index.identifier
556
+ table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
557
+
558
+ if provided_index.schema.present? && table.schema != provided_index.schema
559
+ raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
560
+ end
561
+ end
562
+
563
+ index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, options))
564
+ algorithm =
565
+ if options.is_a?(Hash) && options.key?(:algorithm)
566
+ index_algorithms.fetch(options[:algorithm]) do
567
+ raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
568
+ end
569
+ end
570
+ execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
484
571
  end
485
572
 
573
+ # Renames an index of a table. Raises error if length of new
574
+ # index name is greater than allowed limit.
486
575
  def rename_index(table_name, old_name, new_name)
487
- if new_name.length > allowed_index_name_length
488
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
489
- end
576
+ validate_index_length!(table_name, new_name)
577
+
490
578
  execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
491
579
  end
492
580
 
@@ -532,41 +620,35 @@ module ActiveRecord
532
620
  end
533
621
 
534
622
  # Maps logical Rails types to PostgreSQL-specific data types.
535
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
536
- case type.to_s
623
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil, array = nil)
624
+ sql = case type.to_s
537
625
  when 'binary'
538
626
  # PostgreSQL doesn't support limits on binary (bytea) columns.
539
- # The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
627
+ # The hard limit is 1GB, because of a 32-bit size field, and TOAST.
540
628
  case limit
541
629
  when nil, 0..0x3fffffff; super(type)
542
630
  else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
543
631
  end
544
632
  when 'text'
545
633
  # PostgreSQL doesn't support limits on text columns.
546
- # The hard limit is 1Gb, according to section 8.3 in the manual.
634
+ # The hard limit is 1GB, according to section 8.3 in the manual.
547
635
  case limit
548
636
  when nil, 0..0x3fffffff; super(type)
549
637
  else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
550
638
  end
551
639
  when 'integer'
552
- return 'integer' unless limit
553
-
554
640
  case limit
555
- when 1, 2; 'smallint'
556
- when 3, 4; 'integer'
557
- when 5..8; 'bigint'
558
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
559
- end
560
- when 'datetime'
561
- return super unless precision
562
-
563
- case precision
564
- when 0..6; "timestamp(#{precision})"
565
- else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
641
+ when 1, 2; 'smallint'
642
+ when nil, 3, 4; 'integer'
643
+ when 5..8; 'bigint'
644
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
566
645
  end
567
646
  else
568
- super
647
+ super(type, limit, precision, scale)
569
648
  end
649
+
650
+ sql << '[]' if array && type != :primary_key
651
+ sql
570
652
  end
571
653
 
572
654
  # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
@@ -582,6 +664,18 @@ module ActiveRecord
582
664
 
583
665
  [super, *order_columns].join(', ')
584
666
  end
667
+
668
+ def fetch_type_metadata(column_name, sql_type, oid, fmod)
669
+ cast_type = get_oid_type(oid, fmod, column_name, sql_type)
670
+ simple_type = SqlTypeMetadata.new(
671
+ sql_type: sql_type,
672
+ type: cast_type.type,
673
+ limit: cast_type.limit,
674
+ precision: cast_type.precision,
675
+ scale: cast_type.scale,
676
+ )
677
+ PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
678
+ end
585
679
  end
586
680
  end
587
681
  end