activerecord 4.1.16 → 4.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2185
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +2 -1
  5. data/lib/active_record/aggregations.rb +12 -8
  6. data/lib/active_record/associations.rb +58 -33
  7. data/lib/active_record/associations/association.rb +1 -1
  8. data/lib/active_record/associations/association_scope.rb +53 -21
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +16 -5
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  13. data/lib/active_record/associations/builder/has_one.rb +2 -2
  14. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  15. data/lib/active_record/associations/collection_association.rb +32 -44
  16. data/lib/active_record/associations/collection_proxy.rb +1 -10
  17. data/lib/active_record/associations/has_many_association.rb +60 -14
  18. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  19. data/lib/active_record/associations/has_one_association.rb +0 -1
  20. data/lib/active_record/associations/join_dependency.rb +7 -9
  21. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/preloader/association.rb +9 -5
  24. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  25. data/lib/active_record/associations/singular_association.rb +16 -1
  26. data/lib/active_record/associations/through_association.rb +6 -22
  27. data/lib/active_record/attribute.rb +131 -0
  28. data/lib/active_record/attribute_assignment.rb +19 -11
  29. data/lib/active_record/attribute_decorators.rb +66 -0
  30. data/lib/active_record/attribute_methods.rb +53 -90
  31. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  32. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  33. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  34. data/lib/active_record/attribute_methods/read.rb +14 -57
  35. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  36. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  37. data/lib/active_record/attribute_methods/write.rb +8 -23
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attribute_set/builder.rb +32 -0
  40. data/lib/active_record/attributes.rb +122 -0
  41. data/lib/active_record/autosave_association.rb +11 -21
  42. data/lib/active_record/base.rb +9 -19
  43. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  44. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  53. data/lib/active_record/connection_adapters/column.rb +13 -244
  54. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  55. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  56. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  57. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  58. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  60. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  61. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  90. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  92. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  94. data/lib/active_record/connection_handling.rb +1 -1
  95. data/lib/active_record/core.rb +119 -22
  96. data/lib/active_record/counter_cache.rb +60 -6
  97. data/lib/active_record/enum.rb +9 -10
  98. data/lib/active_record/errors.rb +27 -26
  99. data/lib/active_record/explain.rb +1 -1
  100. data/lib/active_record/fixtures.rb +52 -45
  101. data/lib/active_record/gem_version.rb +3 -3
  102. data/lib/active_record/inheritance.rb +33 -8
  103. data/lib/active_record/integration.rb +4 -4
  104. data/lib/active_record/locking/optimistic.rb +34 -16
  105. data/lib/active_record/migration.rb +22 -32
  106. data/lib/active_record/migration/command_recorder.rb +19 -2
  107. data/lib/active_record/migration/join_table.rb +1 -1
  108. data/lib/active_record/model_schema.rb +39 -48
  109. data/lib/active_record/nested_attributes.rb +8 -18
  110. data/lib/active_record/persistence.rb +39 -22
  111. data/lib/active_record/query_cache.rb +3 -3
  112. data/lib/active_record/querying.rb +1 -8
  113. data/lib/active_record/railtie.rb +17 -10
  114. data/lib/active_record/railties/databases.rake +47 -42
  115. data/lib/active_record/readonly_attributes.rb +0 -1
  116. data/lib/active_record/reflection.rb +225 -92
  117. data/lib/active_record/relation.rb +35 -11
  118. data/lib/active_record/relation/batches.rb +0 -2
  119. data/lib/active_record/relation/calculations.rb +28 -32
  120. data/lib/active_record/relation/delegation.rb +1 -1
  121. data/lib/active_record/relation/finder_methods.rb +42 -20
  122. data/lib/active_record/relation/merger.rb +0 -1
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  125. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  126. data/lib/active_record/relation/query_methods.rb +98 -62
  127. data/lib/active_record/relation/spawn_methods.rb +6 -7
  128. data/lib/active_record/result.rb +16 -9
  129. data/lib/active_record/sanitization.rb +8 -1
  130. data/lib/active_record/schema.rb +0 -1
  131. data/lib/active_record/schema_dumper.rb +51 -9
  132. data/lib/active_record/schema_migration.rb +4 -0
  133. data/lib/active_record/scoping/default.rb +5 -4
  134. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  135. data/lib/active_record/statement_cache.rb +79 -5
  136. data/lib/active_record/store.rb +5 -5
  137. data/lib/active_record/tasks/database_tasks.rb +37 -5
  138. data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  140. data/lib/active_record/timestamp.rb +9 -7
  141. data/lib/active_record/transactions.rb +35 -21
  142. data/lib/active_record/type.rb +20 -0
  143. data/lib/active_record/type/binary.rb +40 -0
  144. data/lib/active_record/type/boolean.rb +19 -0
  145. data/lib/active_record/type/date.rb +46 -0
  146. data/lib/active_record/type/date_time.rb +43 -0
  147. data/lib/active_record/type/decimal.rb +40 -0
  148. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  149. data/lib/active_record/type/float.rb +19 -0
  150. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  151. data/lib/active_record/type/integer.rb +23 -0
  152. data/lib/active_record/type/mutable.rb +16 -0
  153. data/lib/active_record/type/numeric.rb +36 -0
  154. data/lib/active_record/type/serialized.rb +51 -0
  155. data/lib/active_record/type/string.rb +36 -0
  156. data/lib/active_record/type/text.rb +11 -0
  157. data/lib/active_record/type/time.rb +26 -0
  158. data/lib/active_record/type/time_value.rb +38 -0
  159. data/lib/active_record/type/type_map.rb +48 -0
  160. data/lib/active_record/type/value.rb +101 -0
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record/validations/uniqueness.rb +9 -23
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,12 +1,12 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
- class PostgreSQLAdapter < AbstractAdapter
4
- module ReferentialIntegrity
5
- def supports_disable_referential_integrity? #:nodoc:
3
+ module PostgreSQL
4
+ module ReferentialIntegrity # :nodoc:
5
+ def supports_disable_referential_integrity? # :nodoc:
6
6
  true
7
7
  end
8
8
 
9
- def disable_referential_integrity #:nodoc:
9
+ def disable_referential_integrity # :nodoc:
10
10
  if supports_disable_referential_integrity?
11
11
  begin
12
12
  execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
@@ -0,0 +1,154 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module ColumnMethods
5
+ def xml(*args)
6
+ options = args.extract_options!
7
+ column(args[0], :xml, options)
8
+ end
9
+
10
+ def tsvector(*args)
11
+ options = args.extract_options!
12
+ column(args[0], :tsvector, options)
13
+ end
14
+
15
+ def int4range(name, options = {})
16
+ column(name, :int4range, options)
17
+ end
18
+
19
+ def int8range(name, options = {})
20
+ column(name, :int8range, options)
21
+ end
22
+
23
+ def tsrange(name, options = {})
24
+ column(name, :tsrange, options)
25
+ end
26
+
27
+ def tstzrange(name, options = {})
28
+ column(name, :tstzrange, options)
29
+ end
30
+
31
+ def numrange(name, options = {})
32
+ column(name, :numrange, options)
33
+ end
34
+
35
+ def daterange(name, options = {})
36
+ column(name, :daterange, options)
37
+ end
38
+
39
+ def hstore(name, options = {})
40
+ column(name, :hstore, options)
41
+ end
42
+
43
+ def ltree(name, options = {})
44
+ column(name, :ltree, options)
45
+ end
46
+
47
+ def inet(name, options = {})
48
+ column(name, :inet, options)
49
+ end
50
+
51
+ def cidr(name, options = {})
52
+ column(name, :cidr, options)
53
+ end
54
+
55
+ def macaddr(name, options = {})
56
+ column(name, :macaddr, options)
57
+ end
58
+
59
+ def uuid(name, options = {})
60
+ column(name, :uuid, options)
61
+ end
62
+
63
+ def json(name, options = {})
64
+ column(name, :json, options)
65
+ end
66
+
67
+ def jsonb(name, options = {})
68
+ column(name, :jsonb, options)
69
+ end
70
+
71
+ def citext(name, options = {})
72
+ column(name, :citext, options)
73
+ end
74
+
75
+ def point(name, options = {})
76
+ column(name, :point, options)
77
+ end
78
+
79
+ def bit(name, options)
80
+ column(name, :bit, options)
81
+ end
82
+
83
+ def bit_varying(name, options)
84
+ column(name, :bit_varying, options)
85
+ end
86
+
87
+ def money(name, options)
88
+ column(name, :money, options)
89
+ end
90
+ end
91
+
92
+ class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
93
+ attr_accessor :array
94
+ end
95
+
96
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
97
+ include ColumnMethods
98
+
99
+ # Defines the primary key field.
100
+ # Use of the native PostgreSQL UUID type is supported, and can be used
101
+ # by defining your tables as such:
102
+ #
103
+ # create_table :stuffs, id: :uuid do |t|
104
+ # t.string :content
105
+ # t.timestamps
106
+ # end
107
+ #
108
+ # By default, this will use the +uuid_generate_v4()+ function from the
109
+ # +uuid-ossp+ extension, which MUST be enabled on your database. To enable
110
+ # the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
111
+ # migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
112
+ # set the +:default+ option to +nil+:
113
+ #
114
+ # create_table :stuffs, id: false do |t|
115
+ # t.primary_key :id, :uuid, default: nil
116
+ # t.uuid :foo_id
117
+ # t.timestamps
118
+ # end
119
+ #
120
+ # You may also pass a different UUID generation function from +uuid-ossp+
121
+ # or another library.
122
+ #
123
+ # Note that setting the UUID primary key default value to +nil+ will
124
+ # require you to assure that you always provide a UUID value before saving
125
+ # a record (as primary keys cannot be +nil+). This might be done via the
126
+ # +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
127
+ def primary_key(name, type = :primary_key, options = {})
128
+ return super unless type == :uuid
129
+ options[:default] = options.fetch(:default, 'uuid_generate_v4()')
130
+ options[:primary_key] = true
131
+ column name, type, options
132
+ end
133
+
134
+ def column(name, type = nil, options = {})
135
+ super
136
+ column = self[name]
137
+ column.array = options[:array]
138
+
139
+ self
140
+ end
141
+
142
+ private
143
+
144
+ def create_column_definition(name, type)
145
+ PostgreSQL::ColumnDefinition.new name, type
146
+ end
147
+ end
148
+
149
+ class Table < ActiveRecord::ConnectionAdapters::Table
150
+ include ColumnMethods
151
+ end
152
+ end
153
+ end
154
+ end
@@ -1,11 +1,11 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
- class PostgreSQLAdapter < AbstractAdapter
3
+ module PostgreSQL
4
4
  class SchemaCreation < AbstractAdapter::SchemaCreation
5
5
  private
6
6
 
7
7
  def visit_AddColumn(o)
8
- sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
8
+ sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale)
9
9
  sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
10
10
  add_column_options!(sql, column_options(o))
11
11
  end
@@ -31,10 +31,14 @@ module ActiveRecord
31
31
  super
32
32
  end
33
33
  end
34
- end
35
34
 
36
- def schema_creation
37
- SchemaCreation.new self
35
+ def type_for_column(column)
36
+ if column.array
37
+ @conn.lookup_cast_type("#{column.sql_type}[]")
38
+ else
39
+ super
40
+ end
41
+ end
38
42
  end
39
43
 
40
44
  module SchemaStatements
@@ -56,8 +60,8 @@ module ActiveRecord
56
60
  def create_database(name, options = {})
57
61
  options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
58
62
 
59
- option_string = options.inject("") do |memo, (key, value)|
60
- memo += case key
63
+ option_string = options.sum do |key, value|
64
+ case key
61
65
  when :owner
62
66
  " OWNER = \"#{value}\""
63
67
  when :template
@@ -101,19 +105,16 @@ module ActiveRecord
101
105
  # If the schema is not specified as part of +name+ then it will only find tables within
102
106
  # the current schema search path (regardless of permissions to access tables in other schemas)
103
107
  def table_exists?(name)
104
- schema, table = Utils.extract_schema_and_table(name.to_s)
105
- return false unless table
106
-
107
- binds = [[nil, table]]
108
- binds << [nil, schema] if schema
108
+ name = Utils.extract_schema_qualified_name(name.to_s)
109
+ return false unless name.identifier
109
110
 
110
111
  exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
111
112
  SELECT COUNT(*)
112
113
  FROM pg_class c
113
114
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
114
- WHERE c.relkind in ('v','r')
115
- AND c.relname = '#{table.gsub(/(^"|"$)/,'')}'
116
- AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
115
+ WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
116
+ AND c.relname = '#{name.identifier}'
117
+ AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
117
118
  SQL
118
119
  end
119
120
 
@@ -185,13 +186,15 @@ module ActiveRecord
185
186
  def columns(table_name)
186
187
  # Limit, precision, and scale are all handled by the superclass.
187
188
  column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
188
- oid = get_oid_type(oid.to_i, fmod.to_i, column_name)
189
- PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
189
+ oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type)
190
+ default_value = extract_value_from_default(oid, default)
191
+ default_function = extract_default_function(default_value, default)
192
+ new_column(column_name, default_value, oid, type, notnull == 'f', default_function)
190
193
  end
191
194
  end
192
195
 
193
- def column_for(table_name, column_name) #:nodoc:
194
- columns(table_name).detect { |c| c.name == column_name.to_s }
196
+ def new_column(name, default, cast_type, sql_type = nil, null = true, default_function = nil) # :nodoc:
197
+ PostgreSQLColumn.new(name, default, cast_type, sql_type, null, default_function)
195
198
  end
196
199
 
197
200
  # Returns the current database name.
@@ -278,9 +281,9 @@ module ActiveRecord
278
281
  def default_sequence_name(table_name, pk = nil) #:nodoc:
279
282
  result = serial_sequence(table_name, pk || 'id')
280
283
  return nil unless result
281
- result.split('.').last
284
+ Utils.extract_schema_qualified_name(result)
282
285
  rescue ActiveRecord::StatementInvalid
283
- "#{table_name}_#{pk || 'id'}_seq"
286
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq")
284
287
  end
285
288
 
286
289
  def serial_sequence(table, column)
@@ -317,24 +320,27 @@ module ActiveRecord
317
320
  # First try looking for a sequence with a dependency on the
318
321
  # given table's primary key.
319
322
  result = query(<<-end_sql, 'SCHEMA')[0]
320
- SELECT attr.attname, seq.relname
323
+ SELECT attr.attname, nsp.nspname, seq.relname
321
324
  FROM pg_class seq,
322
325
  pg_attribute attr,
323
326
  pg_depend dep,
324
- pg_constraint cons
327
+ pg_constraint cons,
328
+ pg_namespace nsp
325
329
  WHERE seq.oid = dep.objid
326
330
  AND seq.relkind = 'S'
327
331
  AND attr.attrelid = dep.refobjid
328
332
  AND attr.attnum = dep.refobjsubid
329
333
  AND attr.attrelid = cons.conrelid
330
334
  AND attr.attnum = cons.conkey[1]
335
+ AND seq.relnamespace = nsp.oid
331
336
  AND cons.contype = 'p'
337
+ AND dep.classid = 'pg_class'::regclass
332
338
  AND dep.refobjid = '#{quote_table_name(table)}'::regclass
333
339
  end_sql
334
340
 
335
341
  if result.nil? or result.empty?
336
342
  result = query(<<-end_sql, 'SCHEMA')[0]
337
- SELECT attr.attname,
343
+ SELECT attr.attname, nsp.nspname,
338
344
  CASE
339
345
  WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
340
346
  WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
@@ -346,13 +352,19 @@ module ActiveRecord
346
352
  JOIN pg_attribute attr ON (t.oid = attrelid)
347
353
  JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
348
354
  JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
355
+ JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
349
356
  WHERE t.oid = '#{quote_table_name(table)}'::regclass
350
357
  AND cons.contype = 'p'
351
358
  AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
352
359
  end_sql
353
360
  end
354
361
 
355
- [result.first, result.last]
362
+ pk = result.shift
363
+ if result.last
364
+ [pk, PostgreSQL::Name.new(*result)]
365
+ else
366
+ [pk, nil]
367
+ end
356
368
  rescue
357
369
  nil
358
370
  end
@@ -371,8 +383,8 @@ module ActiveRecord
371
383
  end
372
384
 
373
385
  # Renames a table.
374
- # Also renames a table's primary key sequence if the sequence name matches the
375
- # Active Record default.
386
+ # Also renames a table's primary key sequence if the sequence name exists and
387
+ # matches the Active Record default.
376
388
  #
377
389
  # Example:
378
390
  # rename_table('octopuses', 'octopi')
@@ -380,12 +392,9 @@ module ActiveRecord
380
392
  clear_cache!
381
393
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
382
394
  pk, seq = pk_and_sequence_for(new_name)
383
- if seq == "#{table_name}_#{pk}_seq"
395
+ if seq && seq.identifier == "#{table_name}_#{pk}_seq"
384
396
  new_seq = "#{new_name}_#{pk}_seq"
385
- idx = "#{table_name}_pkey"
386
- new_idx = "#{new_name}_pkey"
387
397
  execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
388
- execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
389
398
  end
390
399
 
391
400
  rename_table_indexes(table_name, new_name)
@@ -414,8 +423,16 @@ module ActiveRecord
414
423
  def change_column_default(table_name, column_name, default)
415
424
  clear_cache!
416
425
  column = column_for(table_name, column_name)
426
+ return unless column
417
427
 
418
- execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}" if column
428
+ alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
429
+ if default.nil?
430
+ # <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
431
+ # cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
432
+ execute alter_column_query % "DROP DEFAULT"
433
+ else
434
+ execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
435
+ end
419
436
  end
420
437
 
421
438
  def change_column_null(table_name, column_name, null, default = nil)
@@ -447,6 +464,42 @@ module ActiveRecord
447
464
  execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
448
465
  end
449
466
 
467
+ def foreign_keys(table_name)
468
+ fk_info = select_all <<-SQL.strip_heredoc
469
+ SELECT t2.relname 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
470
+ FROM pg_constraint c
471
+ JOIN pg_class t1 ON c.conrelid = t1.oid
472
+ JOIN pg_class t2 ON c.confrelid = t2.oid
473
+ JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
474
+ JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
475
+ JOIN pg_namespace t3 ON c.connamespace = t3.oid
476
+ WHERE c.contype = 'f'
477
+ AND t1.relname = #{quote(table_name)}
478
+ AND t3.nspname = ANY (current_schemas(false))
479
+ ORDER BY c.conname
480
+ SQL
481
+
482
+ fk_info.map do |row|
483
+ options = {
484
+ column: row['column'],
485
+ name: row['name'],
486
+ primary_key: row['primary_key']
487
+ }
488
+
489
+ options[:on_delete] = extract_foreign_key_action(row['on_delete'])
490
+ options[:on_update] = extract_foreign_key_action(row['on_update'])
491
+ ForeignKeyDefinition.new(table_name, row['to_table'], options)
492
+ end
493
+ end
494
+
495
+ def extract_foreign_key_action(specifier) # :nodoc:
496
+ case specifier
497
+ when 'c'; :cascade
498
+ when 'n'; :nullify
499
+ when 'r'; :restrict
500
+ end
501
+ end
502
+
450
503
  def index_name_length
451
504
  63
452
505
  end
@@ -496,8 +549,7 @@ module ActiveRecord
496
549
  # Convert Arel node to string
497
550
  s = s.to_sql unless s.is_a?(String)
498
551
  # Remove any ASC/DESC modifiers
499
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
500
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
552
+ s.gsub(/\s+(?:ASC|DESC)?\s*(?:NULLS\s+(?:FIRST|LAST)\s*)?/i, '')
501
553
  }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
502
554
 
503
555
  [super, *order_columns].join(', ')
@@ -0,0 +1,66 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ # Value Object to hold a schema qualified name.
5
+ # This is usually the name of a PostgreSQL relation but it can also represent
6
+ # schema qualified type names. +schema+ and +identifier+ are unquoted to prevent
7
+ # double quoting.
8
+ class Name # :nodoc:
9
+ SEPARATOR = "."
10
+ attr_reader :schema, :identifier
11
+
12
+ def initialize(schema, identifier)
13
+ @schema, @identifier = unquote(schema), unquote(identifier)
14
+ end
15
+
16
+ def to_s
17
+ parts.join SEPARATOR
18
+ end
19
+
20
+ def quoted
21
+ parts.map { |p| PGconn.quote_ident(p) }.join SEPARATOR
22
+ end
23
+
24
+ def ==(o)
25
+ o.class == self.class && o.parts == parts
26
+ end
27
+ alias_method :eql?, :==
28
+
29
+ def hash
30
+ parts.hash
31
+ end
32
+
33
+ protected
34
+ def unquote(part)
35
+ return unless part
36
+ part.gsub(/(^"|"$)/,'')
37
+ end
38
+
39
+ def parts
40
+ @parts ||= [@schema, @identifier].compact
41
+ end
42
+ end
43
+
44
+ module Utils # :nodoc:
45
+ extend self
46
+
47
+ # Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
48
+ # extracted from +string+.
49
+ # +schema+ is nil if not specified in +string+.
50
+ # +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
51
+ # +string+ supports the range of schema/table references understood by PostgreSQL, for example:
52
+ #
53
+ # * <tt>table_name</tt>
54
+ # * <tt>"table.name"</tt>
55
+ # * <tt>schema_name.table_name</tt>
56
+ # * <tt>schema_name."table.name"</tt>
57
+ # * <tt>"schema_name".table_name</tt>
58
+ # * <tt>"schema.name"."table name"</tt>
59
+ def extract_schema_qualified_name(string)
60
+ table, schema = string.scan(/[^".\s]+|"[^"]*"/)[0..1].reverse
61
+ PostgreSQL::Name.new(schema, table)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end