activerecord 4.1.0 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- 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 +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- 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 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -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 +79 -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 +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -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/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -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/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -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 +56 -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 +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -0,0 +1,152 @@
|
|
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 new_column_definition(name, type, options) # :nodoc:
|
135
|
+
column = super
|
136
|
+
column.array = options[:array]
|
137
|
+
column
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def create_column_definition(name, type)
|
143
|
+
PostgreSQL::ColumnDefinition.new name, type
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class Table < ActiveRecord::ConnectionAdapters::Table
|
148
|
+
include ColumnMethods
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -1,18 +1,12 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
|
3
|
+
module PostgreSQL
|
4
4
|
class SchemaCreation < AbstractAdapter::SchemaCreation
|
5
5
|
private
|
6
6
|
|
7
|
-
def visit_AddColumn(o)
|
8
|
-
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
|
9
|
-
sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
|
10
|
-
add_column_options!(sql, column_options(o))
|
11
|
-
end
|
12
|
-
|
13
7
|
def visit_ColumnDefinition(o)
|
14
8
|
sql = super
|
15
|
-
if o.primary_key? && o.type
|
9
|
+
if o.primary_key? && o.type != :primary_key
|
16
10
|
sql << " PRIMARY KEY "
|
17
11
|
add_column_options!(sql, column_options(o))
|
18
12
|
end
|
@@ -31,10 +25,14 @@ module ActiveRecord
|
|
31
25
|
super
|
32
26
|
end
|
33
27
|
end
|
34
|
-
end
|
35
28
|
|
36
|
-
|
37
|
-
|
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
|
38
36
|
end
|
39
37
|
|
40
38
|
module SchemaStatements
|
@@ -56,8 +54,8 @@ module ActiveRecord
|
|
56
54
|
def create_database(name, options = {})
|
57
55
|
options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
|
58
56
|
|
59
|
-
option_string = options.
|
60
|
-
case key
|
57
|
+
option_string = options.inject("") do |memo, (key, value)|
|
58
|
+
memo += case key
|
61
59
|
when :owner
|
62
60
|
" OWNER = \"#{value}\""
|
63
61
|
when :template
|
@@ -101,22 +99,23 @@ module ActiveRecord
|
|
101
99
|
# If the schema is not specified as part of +name+ then it will only find tables within
|
102
100
|
# the current schema search path (regardless of permissions to access tables in other schemas)
|
103
101
|
def table_exists?(name)
|
104
|
-
|
105
|
-
return false unless
|
106
|
-
|
107
|
-
binds = [[nil, table]]
|
108
|
-
binds << [nil, schema] if schema
|
102
|
+
name = Utils.extract_schema_qualified_name(name.to_s)
|
103
|
+
return false unless name.identifier
|
109
104
|
|
110
105
|
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
111
106
|
SELECT COUNT(*)
|
112
107
|
FROM pg_class c
|
113
108
|
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))'}
|
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))'}
|
117
112
|
SQL
|
118
113
|
end
|
119
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
|
+
|
120
119
|
# Returns true if schema exists.
|
121
120
|
def schema_exists?(name)
|
122
121
|
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
@@ -185,13 +184,17 @@ module ActiveRecord
|
|
185
184
|
def columns(table_name)
|
186
185
|
# Limit, precision, and scale are all handled by the superclass.
|
187
186
|
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
|
188
|
-
oid =
|
189
|
-
|
190
|
-
|
191
|
-
|
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)
|
192
191
|
end
|
193
192
|
end
|
194
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
|
+
|
195
198
|
# Returns the current database name.
|
196
199
|
def current_database
|
197
200
|
query('select current_database()', 'SCHEMA')[0][0]
|
@@ -276,9 +279,9 @@ module ActiveRecord
|
|
276
279
|
def default_sequence_name(table_name, pk = nil) #:nodoc:
|
277
280
|
result = serial_sequence(table_name, pk || 'id')
|
278
281
|
return nil unless result
|
279
|
-
|
282
|
+
Utils.extract_schema_qualified_name(result).to_s
|
280
283
|
rescue ActiveRecord::StatementInvalid
|
281
|
-
"#{table_name}_#{pk || 'id'}_seq"
|
284
|
+
PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
|
282
285
|
end
|
283
286
|
|
284
287
|
def serial_sequence(table, column)
|
@@ -288,6 +291,23 @@ module ActiveRecord
|
|
288
291
|
result.rows.first.first
|
289
292
|
end
|
290
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
|
+
|
291
311
|
# Resets the sequence of a table's primary key to the maximum value.
|
292
312
|
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
|
293
313
|
unless pk and sequence
|
@@ -315,24 +335,27 @@ module ActiveRecord
|
|
315
335
|
# First try looking for a sequence with a dependency on the
|
316
336
|
# given table's primary key.
|
317
337
|
result = query(<<-end_sql, 'SCHEMA')[0]
|
318
|
-
SELECT attr.attname, seq.relname
|
338
|
+
SELECT attr.attname, nsp.nspname, seq.relname
|
319
339
|
FROM pg_class seq,
|
320
340
|
pg_attribute attr,
|
321
341
|
pg_depend dep,
|
322
|
-
pg_constraint cons
|
342
|
+
pg_constraint cons,
|
343
|
+
pg_namespace nsp
|
323
344
|
WHERE seq.oid = dep.objid
|
324
345
|
AND seq.relkind = 'S'
|
325
346
|
AND attr.attrelid = dep.refobjid
|
326
347
|
AND attr.attnum = dep.refobjsubid
|
327
348
|
AND attr.attrelid = cons.conrelid
|
328
349
|
AND attr.attnum = cons.conkey[1]
|
350
|
+
AND seq.relnamespace = nsp.oid
|
329
351
|
AND cons.contype = 'p'
|
352
|
+
AND dep.classid = 'pg_class'::regclass
|
330
353
|
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
331
354
|
end_sql
|
332
355
|
|
333
356
|
if result.nil? or result.empty?
|
334
357
|
result = query(<<-end_sql, 'SCHEMA')[0]
|
335
|
-
SELECT attr.attname,
|
358
|
+
SELECT attr.attname, nsp.nspname,
|
336
359
|
CASE
|
337
360
|
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
338
361
|
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
|
@@ -344,13 +367,19 @@ module ActiveRecord
|
|
344
367
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
345
368
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
346
369
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
370
|
+
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
347
371
|
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
348
372
|
AND cons.contype = 'p'
|
349
373
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
350
374
|
end_sql
|
351
375
|
end
|
352
376
|
|
353
|
-
|
377
|
+
pk = result.shift
|
378
|
+
if result.last
|
379
|
+
[pk, PostgreSQL::Name.new(*result)]
|
380
|
+
else
|
381
|
+
[pk, nil]
|
382
|
+
end
|
354
383
|
rescue
|
355
384
|
nil
|
356
385
|
end
|
@@ -369,8 +398,8 @@ module ActiveRecord
|
|
369
398
|
end
|
370
399
|
|
371
400
|
# Renames a table.
|
372
|
-
# Also renames a table's primary key sequence if the sequence name
|
373
|
-
# Active Record default.
|
401
|
+
# Also renames a table's primary key sequence if the sequence name exists and
|
402
|
+
# matches the Active Record default.
|
374
403
|
#
|
375
404
|
# Example:
|
376
405
|
# rename_table('octopuses', 'octopi')
|
@@ -378,9 +407,12 @@ module ActiveRecord
|
|
378
407
|
clear_cache!
|
379
408
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
380
409
|
pk, seq = pk_and_sequence_for(new_name)
|
381
|
-
if seq == "#{table_name}_#{pk}_seq"
|
410
|
+
if seq && seq.identifier == "#{table_name}_#{pk}_seq"
|
382
411
|
new_seq = "#{new_name}_#{pk}_seq"
|
412
|
+
idx = "#{table_name}_pkey"
|
413
|
+
new_idx = "#{new_name}_pkey"
|
383
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)}"
|
384
416
|
end
|
385
417
|
|
386
418
|
rename_table_indexes(table_name, new_name)
|
@@ -399,7 +431,12 @@ module ActiveRecord
|
|
399
431
|
quoted_table_name = quote_table_name(table_name)
|
400
432
|
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
|
401
433
|
sql_type << "[]" if options[:array]
|
402
|
-
|
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
|
403
440
|
|
404
441
|
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
405
442
|
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
@@ -408,13 +445,24 @@ module ActiveRecord
|
|
408
445
|
# Changes the default value of a table column.
|
409
446
|
def change_column_default(table_name, column_name, default)
|
410
447
|
clear_cache!
|
411
|
-
|
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
|
412
459
|
end
|
413
460
|
|
414
461
|
def change_column_null(table_name, column_name, null, default = nil)
|
415
462
|
clear_cache!
|
416
463
|
unless null || default.nil?
|
417
|
-
|
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
|
418
466
|
end
|
419
467
|
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
420
468
|
end
|
@@ -436,9 +484,49 @@ module ActiveRecord
|
|
436
484
|
end
|
437
485
|
|
438
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
|
439
490
|
execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
|
440
491
|
end
|
441
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
|
+
|
442
530
|
def index_name_length
|
443
531
|
63
|
444
532
|
end
|
@@ -488,7 +576,8 @@ module ActiveRecord
|
|
488
576
|
# Convert Arel node to string
|
489
577
|
s = s.to_sql unless s.is_a?(String)
|
490
578
|
# Remove any ASC/DESC modifiers
|
491
|
-
s.gsub(/\s+(ASC|DESC)\
|
579
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, '')
|
580
|
+
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
|
492
581
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
493
582
|
|
494
583
|
[super, *order_columns].join(', ')
|
@@ -0,0 +1,77 @@
|
|
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
|
+
if schema
|
22
|
+
PGconn.quote_ident(schema) << SEPARATOR << PGconn.quote_ident(identifier)
|
23
|
+
else
|
24
|
+
PGconn.quote_ident(identifier)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(o)
|
29
|
+
o.class == self.class && o.parts == parts
|
30
|
+
end
|
31
|
+
alias_method :eql?, :==
|
32
|
+
|
33
|
+
def hash
|
34
|
+
parts.hash
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def unquote(part)
|
39
|
+
if part && part.start_with?('"')
|
40
|
+
part[1..-2]
|
41
|
+
else
|
42
|
+
part
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def parts
|
47
|
+
@parts ||= [@schema, @identifier].compact
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module Utils # :nodoc:
|
52
|
+
extend self
|
53
|
+
|
54
|
+
# Returns an instance of <tt>ActiveRecord::ConnectionAdapters::PostgreSQL::Name</tt>
|
55
|
+
# extracted from +string+.
|
56
|
+
# +schema+ is nil if not specified in +string+.
|
57
|
+
# +schema+ and +identifier+ exclude surrounding quotes (regardless of whether provided in +string+)
|
58
|
+
# +string+ supports the range of schema/table references understood by PostgreSQL, for example:
|
59
|
+
#
|
60
|
+
# * <tt>table_name</tt>
|
61
|
+
# * <tt>"table.name"</tt>
|
62
|
+
# * <tt>schema_name.table_name</tt>
|
63
|
+
# * <tt>schema_name."table.name"</tt>
|
64
|
+
# * <tt>"schema_name".table_name</tt>
|
65
|
+
# * <tt>"schema.name"."table name"</tt>
|
66
|
+
def extract_schema_qualified_name(string)
|
67
|
+
schema, table = string.scan(/[^".\s]+|"[^"]*"/)
|
68
|
+
if table.nil?
|
69
|
+
table = schema
|
70
|
+
schema = nil
|
71
|
+
end
|
72
|
+
PostgreSQL::Name.new(schema, table)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|