activerecord 4.2.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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,588 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ class SchemaCreation < AbstractAdapter::SchemaCreation
5
+ private
6
+
7
+ 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
14
+ end
15
+
16
+ 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
34
+ end
35
+ end
36
+ end
37
+
38
+ module SchemaStatements
39
+ # Drops the database specified on the +name+ attribute
40
+ # and creates it again using the provided +options+.
41
+ def recreate_database(name, options = {}) #:nodoc:
42
+ drop_database(name)
43
+ create_database(name, options)
44
+ end
45
+
46
+ # Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
47
+ # <tt>:encoding</tt> (defaults to utf8), <tt>:collation</tt>, <tt>:ctype</tt>,
48
+ # <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
49
+ # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
50
+ #
51
+ # Example:
52
+ # create_database config[:database], config
53
+ # create_database 'foo_development', encoding: 'unicode'
54
+ def create_database(name, options = {})
55
+ options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
56
+
57
+ option_string = options.inject("") do |memo, (key, value)|
58
+ memo += case key
59
+ when :owner
60
+ " OWNER = \"#{value}\""
61
+ when :template
62
+ " TEMPLATE = \"#{value}\""
63
+ when :encoding
64
+ " ENCODING = '#{value}'"
65
+ when :collation
66
+ " LC_COLLATE = '#{value}'"
67
+ when :ctype
68
+ " LC_CTYPE = '#{value}'"
69
+ when :tablespace
70
+ " TABLESPACE = \"#{value}\""
71
+ when :connection_limit
72
+ " CONNECTION LIMIT = #{value}"
73
+ else
74
+ ""
75
+ end
76
+ end
77
+
78
+ execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
79
+ end
80
+
81
+ # Drops a PostgreSQL database.
82
+ #
83
+ # Example:
84
+ # drop_database 'matt_development'
85
+ def drop_database(name) #:nodoc:
86
+ execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
87
+ end
88
+
89
+ # Returns the list of all tables in the schema search path or a specified schema.
90
+ 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))
95
+ SQL
96
+ end
97
+
98
+ # Returns true if table exists.
99
+ # If the schema is not specified as part of +name+ then it will only find tables within
100
+ # the current schema search path (regardless of permissions to access tables in other schemas)
101
+ def table_exists?(name)
102
+ name = Utils.extract_schema_qualified_name(name.to_s)
103
+ return false unless name.identifier
104
+
105
+ exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
106
+ SELECT COUNT(*)
107
+ FROM pg_class c
108
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
109
+ WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
110
+ AND c.relname = '#{name.identifier}'
111
+ AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
112
+ SQL
113
+ end
114
+
115
+ def drop_table(table_name, options = {})
116
+ execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
117
+ end
118
+
119
+ # Returns true if schema exists.
120
+ 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
126
+ end
127
+
128
+ def index_name_exists?(table_name, index_name, default)
129
+ exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
130
+ SELECT COUNT(*)
131
+ FROM pg_class t
132
+ INNER JOIN pg_index d ON t.oid = d.indrelid
133
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
134
+ 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)) )
138
+ SQL
139
+ end
140
+
141
+ # Returns an array of indexes for the given table.
142
+ 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)) )
152
+ ORDER BY i.relname
153
+ SQL
154
+
155
+ result.map do |row|
156
+ index_name = row[0]
157
+ unique = row[1] == 't'
158
+ indkey = row[2].split(" ")
159
+ inddef = row[3]
160
+ oid = row[4]
161
+
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
168
+
169
+ column_names = columns.values_at(*indkey).compact
170
+
171
+ unless column_names.empty?
172
+ # 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)
179
+ end
180
+ end.compact
181
+ end
182
+
183
+ # 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)
189
+ default_function = extract_default_function(default_value, default)
190
+ new_column(column_name, default_value, oid, type, notnull == 'f', default_function)
191
+ end
192
+ end
193
+
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)
196
+ end
197
+
198
+ # Returns the current database name.
199
+ def current_database
200
+ query('select current_database()', 'SCHEMA')[0][0]
201
+ end
202
+
203
+ # Returns the current schema name.
204
+ def current_schema
205
+ query('SELECT current_schema', 'SCHEMA')[0][0]
206
+ end
207
+
208
+ # Returns the current database encoding format.
209
+ 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
214
+ end
215
+
216
+ # Returns the current database collation.
217
+ 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
221
+ end
222
+
223
+ # Returns the current database ctype.
224
+ 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
228
+ end
229
+
230
+ # Returns an array of schema names.
231
+ def schema_names
232
+ query(<<-SQL, 'SCHEMA').flatten
233
+ SELECT nspname
234
+ FROM pg_namespace
235
+ WHERE nspname !~ '^pg_.*'
236
+ AND nspname NOT IN ('information_schema')
237
+ ORDER by nspname;
238
+ SQL
239
+ end
240
+
241
+ # Creates a schema for the given schema name.
242
+ def create_schema schema_name
243
+ execute "CREATE SCHEMA #{schema_name}"
244
+ end
245
+
246
+ # Drops the schema for the given schema name.
247
+ def drop_schema schema_name
248
+ execute "DROP SCHEMA #{schema_name} CASCADE"
249
+ end
250
+
251
+ # Sets the schema search path to a string of comma-separated schema names.
252
+ # Names beginning with $ have to be quoted (e.g. $user => '$user').
253
+ # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
254
+ #
255
+ # This should be not be called manually but set in database.yml.
256
+ def schema_search_path=(schema_csv)
257
+ if schema_csv
258
+ execute("SET search_path TO #{schema_csv}", 'SCHEMA')
259
+ @schema_search_path = schema_csv
260
+ end
261
+ end
262
+
263
+ # Returns the active schema search path.
264
+ def schema_search_path
265
+ @schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
266
+ end
267
+
268
+ # Returns the current client message level.
269
+ def client_min_messages
270
+ query('SHOW client_min_messages', 'SCHEMA')[0][0]
271
+ end
272
+
273
+ # Set the client message level.
274
+ def client_min_messages=(level)
275
+ execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
276
+ end
277
+
278
+ # Returns the sequence name for a table's primary key or some other specified key.
279
+ def default_sequence_name(table_name, pk = nil) #:nodoc:
280
+ result = serial_sequence(table_name, pk || 'id')
281
+ return nil unless result
282
+ Utils.extract_schema_qualified_name(result).to_s
283
+ rescue ActiveRecord::StatementInvalid
284
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
285
+ end
286
+
287
+ 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
292
+ end
293
+
294
+ # Sets the sequence of a table's primary key to the specified value.
295
+ def set_pk_sequence!(table, value) #:nodoc:
296
+ pk, sequence = pk_and_sequence_for(table)
297
+
298
+ if pk
299
+ if sequence
300
+ quoted_sequence = quote_table_name(sequence)
301
+
302
+ select_value <<-end_sql, 'SCHEMA'
303
+ SELECT setval('#{quoted_sequence}', #{value})
304
+ end_sql
305
+ else
306
+ @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
307
+ end
308
+ end
309
+ end
310
+
311
+ # Resets the sequence of a table's primary key to the maximum value.
312
+ def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
313
+ unless pk and sequence
314
+ default_pk, default_sequence = pk_and_sequence_for(table)
315
+
316
+ pk ||= default_pk
317
+ sequence ||= default_sequence
318
+ end
319
+
320
+ if @logger && pk && !sequence
321
+ @logger.warn "#{table} has primary key #{pk} with no default sequence"
322
+ end
323
+
324
+ if pk && sequence
325
+ quoted_sequence = quote_table_name(sequence)
326
+
327
+ select_value <<-end_sql, 'SCHEMA'
328
+ 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
+ end_sql
330
+ end
331
+ end
332
+
333
+ # Returns a table's primary key and belonging sequence.
334
+ def pk_and_sequence_for(table) #:nodoc:
335
+ # First try looking for a sequence with a dependency on the
336
+ # given table's primary key.
337
+ result = query(<<-end_sql, 'SCHEMA')[0]
338
+ SELECT attr.attname, nsp.nspname, seq.relname
339
+ FROM pg_class seq,
340
+ pg_attribute attr,
341
+ pg_depend dep,
342
+ pg_constraint cons,
343
+ pg_namespace nsp
344
+ WHERE seq.oid = dep.objid
345
+ AND seq.relkind = 'S'
346
+ AND attr.attrelid = dep.refobjid
347
+ AND attr.attnum = dep.refobjsubid
348
+ AND attr.attrelid = cons.conrelid
349
+ AND attr.attnum = cons.conkey[1]
350
+ AND seq.relnamespace = nsp.oid
351
+ AND cons.contype = 'p'
352
+ AND dep.classid = 'pg_class'::regclass
353
+ AND dep.refobjid = '#{quote_table_name(table)}'::regclass
354
+ end_sql
355
+
356
+ if result.nil? or result.empty?
357
+ result = query(<<-end_sql, 'SCHEMA')[0]
358
+ SELECT attr.attname, nsp.nspname,
359
+ CASE
360
+ WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
361
+ WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
362
+ substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
363
+ strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
364
+ ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
365
+ END
366
+ FROM pg_class t
367
+ JOIN pg_attribute attr ON (t.oid = attrelid)
368
+ JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
369
+ JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
370
+ JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
371
+ WHERE t.oid = '#{quote_table_name(table)}'::regclass
372
+ AND cons.contype = 'p'
373
+ AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
374
+ end_sql
375
+ end
376
+
377
+ pk = result.shift
378
+ if result.last
379
+ [pk, PostgreSQL::Name.new(*result)]
380
+ else
381
+ [pk, nil]
382
+ end
383
+ rescue
384
+ nil
385
+ end
386
+
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
398
+ end
399
+
400
+ # Renames a table.
401
+ # Also renames a table's primary key sequence if the sequence name exists and
402
+ # matches the Active Record default.
403
+ #
404
+ # Example:
405
+ # rename_table('octopuses', 'octopi')
406
+ def rename_table(table_name, new_name)
407
+ clear_cache!
408
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
409
+ pk, seq = pk_and_sequence_for(new_name)
410
+ if seq && seq.identifier == "#{table_name}_#{pk}_seq"
411
+ new_seq = "#{new_name}_#{pk}_seq"
412
+ idx = "#{table_name}_pkey"
413
+ new_idx = "#{new_name}_pkey"
414
+ execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
415
+ execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
416
+ end
417
+
418
+ rename_table_indexes(table_name, new_name)
419
+ end
420
+
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 = {})
424
+ clear_cache!
425
+ super
426
+ end
427
+
428
+ # Changes the column of a table.
429
+ def change_column(table_name, column_name, type, options = {})
430
+ clear_cache!
431
+ 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])})"
438
+ end
439
+ execute sql
440
+
441
+ change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
442
+ change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
443
+ end
444
+
445
+ # Changes the default value of a table column.
446
+ def change_column_default(table_name, column_name, default)
447
+ clear_cache!
448
+ column = column_for(table_name, column_name)
449
+ return unless column
450
+
451
+ alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
452
+ if default.nil?
453
+ # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
454
+ # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
455
+ execute alter_column_query % "DROP DEFAULT"
456
+ else
457
+ execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
458
+ end
459
+ end
460
+
461
+ def change_column_null(table_name, column_name, null, default = nil)
462
+ clear_cache!
463
+ unless null || default.nil?
464
+ 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
466
+ end
467
+ execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
468
+ end
469
+
470
+ # Renames a column in a table.
471
+ def rename_column(table_name, column_name, new_column_name)
472
+ clear_cache!
473
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
474
+ rename_column_indexes(table_name, column_name, new_column_name)
475
+ end
476
+
477
+ 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}"
480
+ end
481
+
482
+ def remove_index!(table_name, index_name) #:nodoc:
483
+ execute "DROP INDEX #{quote_table_name(index_name)}"
484
+ end
485
+
486
+ 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
490
+ execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
491
+ end
492
+
493
+ def foreign_keys(table_name)
494
+ fk_info = select_all <<-SQL.strip_heredoc
495
+ 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
496
+ FROM pg_constraint c
497
+ JOIN pg_class t1 ON c.conrelid = t1.oid
498
+ JOIN pg_class t2 ON c.confrelid = t2.oid
499
+ JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
500
+ JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
501
+ JOIN pg_namespace t3 ON c.connamespace = t3.oid
502
+ WHERE c.contype = 'f'
503
+ AND t1.relname = #{quote(table_name)}
504
+ AND t3.nspname = ANY (current_schemas(false))
505
+ ORDER BY c.conname
506
+ SQL
507
+
508
+ fk_info.map do |row|
509
+ options = {
510
+ column: row['column'],
511
+ name: row['name'],
512
+ primary_key: row['primary_key']
513
+ }
514
+
515
+ options[:on_delete] = extract_foreign_key_action(row['on_delete'])
516
+ options[:on_update] = extract_foreign_key_action(row['on_update'])
517
+
518
+ ForeignKeyDefinition.new(table_name, row['to_table'], options)
519
+ end
520
+ end
521
+
522
+ def extract_foreign_key_action(specifier) # :nodoc:
523
+ case specifier
524
+ when 'c'; :cascade
525
+ when 'n'; :nullify
526
+ when 'r'; :restrict
527
+ end
528
+ end
529
+
530
+ def index_name_length
531
+ 63
532
+ end
533
+
534
+ # 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
537
+ when 'binary'
538
+ # PostgreSQL doesn't support limits on binary (bytea) columns.
539
+ # The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
540
+ case limit
541
+ when nil, 0..0x3fffffff; super(type)
542
+ else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
543
+ end
544
+ when 'text'
545
+ # PostgreSQL doesn't support limits on text columns.
546
+ # The hard limit is 1Gb, according to section 8.3 in the manual.
547
+ case limit
548
+ when nil, 0..0x3fffffff; super(type)
549
+ else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
550
+ end
551
+ when 'integer'
552
+ return 'integer' unless limit
553
+
554
+ 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")
566
+ end
567
+ else
568
+ super
569
+ end
570
+ end
571
+
572
+ # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
573
+ # requires that the ORDER BY include the distinct column.
574
+ def columns_for_distinct(columns, orders) #:nodoc:
575
+ order_columns = orders.reject(&:blank?).map{ |s|
576
+ # Convert Arel node to string
577
+ s = s.to_sql unless s.is_a?(String)
578
+ # Remove any ASC/DESC modifiers
579
+ s.gsub(/\s+(?:ASC|DESC)\b/i, '')
580
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
581
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
582
+
583
+ [super, *order_columns].join(', ')
584
+ end
585
+ end
586
+ end
587
+ end
588
+ end