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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +634 -2185
- data/README.rdoc +15 -10
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/attribute.rb +131 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- data/lib/active_record/migration.rb +22 -32
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +10 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +51 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,12 +1,12 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
|
4
|
-
module ReferentialIntegrity
|
5
|
-
def supports_disable_referential_integrity?
|
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
|
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
|
-
|
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
|
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
|
-
|
37
|
-
|
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.
|
60
|
-
|
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
|
-
|
105
|
-
return false unless
|
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
|
115
|
-
AND c.relname = '#{
|
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
|
-
|
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
|
194
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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)\
|
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
|