activerecord 4.1.8 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +1165 -1591
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +84 -43
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- 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/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -14
- 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 +87 -30
- data/lib/active_record/associations/collection_proxy.rb +33 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -12
- data/lib/active_record/associations/preloader/association.rb +14 -10
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +37 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +16 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +20 -12
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -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 +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -28
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +57 -95
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +30 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +85 -53
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +139 -57
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +271 -74
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -60
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +295 -141
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +17 -33
- data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -145
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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 +19 -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 +109 -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 -385
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- 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 +134 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -40
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +10 -12
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +62 -74
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- 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 +79 -47
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +18 -8
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +48 -27
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +19 -14
- data/lib/active_record/railties/databases.rake +55 -56
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +281 -117
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +71 -48
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +42 -12
- data/lib/active_record/relation/query_methods.rb +130 -73
- data/lib/active_record/relation/spawn_methods.rb +10 -3
- data/lib/active_record/relation.rb +57 -25
- 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 +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- 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 +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -8
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +54 -28
- 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 +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -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 +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -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 +110 -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 +5 -3
- data/lib/active_record/validations/uniqueness.rb +24 -20
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +5 -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 +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- 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,15 +1,9 @@
|
|
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
9
|
if o.primary_key? && o.type != :primary_key
|
@@ -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
|
@@ -88,7 +86,7 @@ module ActiveRecord
|
|
88
86
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
89
87
|
end
|
90
88
|
|
91
|
-
# Returns the list of all tables in the schema search path
|
89
|
+
# Returns the list of all tables in the schema search path.
|
92
90
|
def tables(name = nil)
|
93
91
|
query(<<-SQL, 'SCHEMA').map { |row| row[0] }
|
94
92
|
SELECT tablename
|
@@ -97,25 +95,37 @@ module ActiveRecord
|
|
97
95
|
SQL
|
98
96
|
end
|
99
97
|
|
98
|
+
def data_sources # :nodoc
|
99
|
+
select_values(<<-SQL, 'SCHEMA')
|
100
|
+
SELECT c.relname
|
101
|
+
FROM pg_class c
|
102
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
103
|
+
WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
104
|
+
AND n.nspname = ANY (current_schemas(false))
|
105
|
+
SQL
|
106
|
+
end
|
107
|
+
|
100
108
|
# Returns true if table exists.
|
101
109
|
# If the schema is not specified as part of +name+ then it will only find tables within
|
102
110
|
# the current schema search path (regardless of permissions to access tables in other schemas)
|
103
111
|
def table_exists?(name)
|
104
|
-
|
105
|
-
return false unless
|
106
|
-
|
107
|
-
binds = [[nil, table]]
|
108
|
-
binds << [nil, schema] if schema
|
112
|
+
name = Utils.extract_schema_qualified_name(name.to_s)
|
113
|
+
return false unless name.identifier
|
109
114
|
|
110
115
|
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
111
116
|
SELECT COUNT(*)
|
112
117
|
FROM pg_class c
|
113
118
|
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))'}
|
119
|
+
WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
120
|
+
AND c.relname = '#{name.identifier}'
|
121
|
+
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
117
122
|
SQL
|
118
123
|
end
|
124
|
+
alias data_source_exists? table_exists?
|
125
|
+
|
126
|
+
def drop_table(table_name, options = {})
|
127
|
+
execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
128
|
+
end
|
119
129
|
|
120
130
|
# Returns true if schema exists.
|
121
131
|
def schema_exists?(name)
|
@@ -185,13 +195,15 @@ module ActiveRecord
|
|
185
195
|
def columns(table_name)
|
186
196
|
# Limit, precision, and scale are all handled by the superclass.
|
187
197
|
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
|
-
|
198
|
+
oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type)
|
199
|
+
default_value = extract_value_from_default(oid, default)
|
200
|
+
default_function = extract_default_function(default_value, default)
|
201
|
+
new_column(column_name, default_value, oid, type, notnull == 'f', default_function)
|
190
202
|
end
|
191
203
|
end
|
192
204
|
|
193
|
-
def
|
194
|
-
|
205
|
+
def new_column(name, default, cast_type, sql_type = nil, null = true, default_function = nil) # :nodoc:
|
206
|
+
PostgreSQLColumn.new(name, default, cast_type, sql_type, null, default_function)
|
195
207
|
end
|
196
208
|
|
197
209
|
# Returns the current database name.
|
@@ -278,9 +290,9 @@ module ActiveRecord
|
|
278
290
|
def default_sequence_name(table_name, pk = nil) #:nodoc:
|
279
291
|
result = serial_sequence(table_name, pk || 'id')
|
280
292
|
return nil unless result
|
281
|
-
|
293
|
+
Utils.extract_schema_qualified_name(result).to_s
|
282
294
|
rescue ActiveRecord::StatementInvalid
|
283
|
-
"#{table_name}_#{pk || 'id'}_seq"
|
295
|
+
PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
|
284
296
|
end
|
285
297
|
|
286
298
|
def serial_sequence(table, column)
|
@@ -290,6 +302,23 @@ module ActiveRecord
|
|
290
302
|
result.rows.first.first
|
291
303
|
end
|
292
304
|
|
305
|
+
# Sets the sequence of a table's primary key to the specified value.
|
306
|
+
def set_pk_sequence!(table, value) #:nodoc:
|
307
|
+
pk, sequence = pk_and_sequence_for(table)
|
308
|
+
|
309
|
+
if pk
|
310
|
+
if sequence
|
311
|
+
quoted_sequence = quote_table_name(sequence)
|
312
|
+
|
313
|
+
select_value <<-end_sql, 'SCHEMA'
|
314
|
+
SELECT setval('#{quoted_sequence}', #{value})
|
315
|
+
end_sql
|
316
|
+
else
|
317
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
293
322
|
# Resets the sequence of a table's primary key to the maximum value.
|
294
323
|
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
|
295
324
|
unless pk and sequence
|
@@ -317,24 +346,27 @@ module ActiveRecord
|
|
317
346
|
# First try looking for a sequence with a dependency on the
|
318
347
|
# given table's primary key.
|
319
348
|
result = query(<<-end_sql, 'SCHEMA')[0]
|
320
|
-
SELECT attr.attname, seq.relname
|
349
|
+
SELECT attr.attname, nsp.nspname, seq.relname
|
321
350
|
FROM pg_class seq,
|
322
351
|
pg_attribute attr,
|
323
352
|
pg_depend dep,
|
324
|
-
pg_constraint cons
|
353
|
+
pg_constraint cons,
|
354
|
+
pg_namespace nsp
|
325
355
|
WHERE seq.oid = dep.objid
|
326
356
|
AND seq.relkind = 'S'
|
327
357
|
AND attr.attrelid = dep.refobjid
|
328
358
|
AND attr.attnum = dep.refobjsubid
|
329
359
|
AND attr.attrelid = cons.conrelid
|
330
360
|
AND attr.attnum = cons.conkey[1]
|
361
|
+
AND seq.relnamespace = nsp.oid
|
331
362
|
AND cons.contype = 'p'
|
363
|
+
AND dep.classid = 'pg_class'::regclass
|
332
364
|
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
|
333
365
|
end_sql
|
334
366
|
|
335
367
|
if result.nil? or result.empty?
|
336
368
|
result = query(<<-end_sql, 'SCHEMA')[0]
|
337
|
-
SELECT attr.attname,
|
369
|
+
SELECT attr.attname, nsp.nspname,
|
338
370
|
CASE
|
339
371
|
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
340
372
|
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
|
@@ -346,33 +378,39 @@ module ActiveRecord
|
|
346
378
|
JOIN pg_attribute attr ON (t.oid = attrelid)
|
347
379
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
348
380
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
381
|
+
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
349
382
|
WHERE t.oid = '#{quote_table_name(table)}'::regclass
|
350
383
|
AND cons.contype = 'p'
|
351
384
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
352
385
|
end_sql
|
353
386
|
end
|
354
387
|
|
355
|
-
|
388
|
+
pk = result.shift
|
389
|
+
if result.last
|
390
|
+
[pk, PostgreSQL::Name.new(*result)]
|
391
|
+
else
|
392
|
+
[pk, nil]
|
393
|
+
end
|
356
394
|
rescue
|
357
395
|
nil
|
358
396
|
end
|
359
397
|
|
360
398
|
# Returns just a table's primary key
|
361
399
|
def primary_key(table)
|
362
|
-
|
400
|
+
pks = exec_query(<<-end_sql, 'SCHEMA').rows
|
363
401
|
SELECT attr.attname
|
364
402
|
FROM pg_attribute attr
|
365
|
-
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey
|
403
|
+
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey)
|
366
404
|
WHERE cons.contype = 'p'
|
367
405
|
AND cons.conrelid = '#{quote_table_name(table)}'::regclass
|
368
406
|
end_sql
|
369
|
-
|
370
|
-
|
407
|
+
return nil unless pks.count == 1
|
408
|
+
pks[0][0]
|
371
409
|
end
|
372
410
|
|
373
411
|
# Renames a table.
|
374
|
-
# Also renames a table's primary key sequence if the sequence name
|
375
|
-
# Active Record default.
|
412
|
+
# Also renames a table's primary key sequence if the sequence name exists and
|
413
|
+
# matches the Active Record default.
|
376
414
|
#
|
377
415
|
# Example:
|
378
416
|
# rename_table('octopuses', 'octopi')
|
@@ -380,17 +418,18 @@ module ActiveRecord
|
|
380
418
|
clear_cache!
|
381
419
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
382
420
|
pk, seq = pk_and_sequence_for(new_name)
|
383
|
-
if seq == "#{table_name}_#{pk}_seq"
|
421
|
+
if seq && seq.identifier == "#{table_name}_#{pk}_seq"
|
384
422
|
new_seq = "#{new_name}_#{pk}_seq"
|
423
|
+
idx = "#{table_name}_pkey"
|
424
|
+
new_idx = "#{new_name}_pkey"
|
385
425
|
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
|
426
|
+
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
386
427
|
end
|
387
428
|
|
388
429
|
rename_table_indexes(table_name, new_name)
|
389
430
|
end
|
390
431
|
|
391
|
-
|
392
|
-
# See TableDefinition#column for details of the options you can use.
|
393
|
-
def add_column(table_name, column_name, type, options = {})
|
432
|
+
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
394
433
|
clear_cache!
|
395
434
|
super
|
396
435
|
end
|
@@ -401,7 +440,12 @@ module ActiveRecord
|
|
401
440
|
quoted_table_name = quote_table_name(table_name)
|
402
441
|
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
|
403
442
|
sql_type << "[]" if options[:array]
|
404
|
-
|
443
|
+
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
|
444
|
+
sql << " USING #{options[:using]}" if options[:using]
|
445
|
+
if options[:cast_as]
|
446
|
+
sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
|
447
|
+
end
|
448
|
+
execute sql
|
405
449
|
|
406
450
|
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
407
451
|
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
@@ -411,8 +455,16 @@ module ActiveRecord
|
|
411
455
|
def change_column_default(table_name, column_name, default)
|
412
456
|
clear_cache!
|
413
457
|
column = column_for(table_name, column_name)
|
458
|
+
return unless column
|
414
459
|
|
415
|
-
|
460
|
+
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
461
|
+
if default.nil?
|
462
|
+
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
463
|
+
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
464
|
+
execute alter_column_query % "DROP DEFAULT"
|
465
|
+
else
|
466
|
+
execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
|
467
|
+
end
|
416
468
|
end
|
417
469
|
|
418
470
|
def change_column_null(table_name, column_name, null, default = nil)
|
@@ -425,7 +477,7 @@ module ActiveRecord
|
|
425
477
|
end
|
426
478
|
|
427
479
|
# Renames a column in a table.
|
428
|
-
def rename_column(table_name, column_name, new_column_name)
|
480
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
429
481
|
clear_cache!
|
430
482
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
431
483
|
rename_column_indexes(table_name, column_name, new_column_name)
|
@@ -441,9 +493,48 @@ module ActiveRecord
|
|
441
493
|
end
|
442
494
|
|
443
495
|
def rename_index(table_name, old_name, new_name)
|
496
|
+
validate_index_length!(table_name, new_name)
|
497
|
+
|
444
498
|
execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
|
445
499
|
end
|
446
500
|
|
501
|
+
def foreign_keys(table_name)
|
502
|
+
fk_info = select_all <<-SQL.strip_heredoc
|
503
|
+
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
|
504
|
+
FROM pg_constraint c
|
505
|
+
JOIN pg_class t1 ON c.conrelid = t1.oid
|
506
|
+
JOIN pg_class t2 ON c.confrelid = t2.oid
|
507
|
+
JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
|
508
|
+
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
509
|
+
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
510
|
+
WHERE c.contype = 'f'
|
511
|
+
AND t1.relname = #{quote(table_name)}
|
512
|
+
AND t3.nspname = ANY (current_schemas(false))
|
513
|
+
ORDER BY c.conname
|
514
|
+
SQL
|
515
|
+
|
516
|
+
fk_info.map do |row|
|
517
|
+
options = {
|
518
|
+
column: row['column'],
|
519
|
+
name: row['name'],
|
520
|
+
primary_key: row['primary_key']
|
521
|
+
}
|
522
|
+
|
523
|
+
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
|
524
|
+
options[:on_update] = extract_foreign_key_action(row['on_update'])
|
525
|
+
|
526
|
+
ForeignKeyDefinition.new(table_name, row['to_table'], options)
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
def extract_foreign_key_action(specifier) # :nodoc:
|
531
|
+
case specifier
|
532
|
+
when 'c'; :cascade
|
533
|
+
when 'n'; :nullify
|
534
|
+
when 'r'; :restrict
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
447
538
|
def index_name_length
|
448
539
|
63
|
449
540
|
end
|
@@ -472,7 +563,7 @@ module ActiveRecord
|
|
472
563
|
when 1, 2; 'smallint'
|
473
564
|
when 3, 4; 'integer'
|
474
565
|
when 5..8; 'bigint'
|
475
|
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with
|
566
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
|
476
567
|
end
|
477
568
|
when 'datetime'
|
478
569
|
return super unless precision
|
@@ -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
|