activerecord 4.2.11.3 → 5.0.7.2
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 +1638 -1132
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +7 -2
- data/lib/active_record/aggregations.rb +34 -21
- data/lib/active_record/association_relation.rb +7 -4
- data/lib/active_record/associations.rb +347 -218
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +22 -10
- data/lib/active_record/associations/association_scope.rb +75 -104
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +13 -11
- data/lib/active_record/associations/collection_association.rb +85 -69
- data/lib/active_record/associations/collection_proxy.rb +104 -46
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +21 -78
- data/lib/active_record/associations/has_many_through_association.rb +6 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +38 -22
- data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +52 -71
- data/lib/active_record/associations/preloader/collection_association.rb +0 -7
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +0 -1
- data/lib/active_record/associations/preloader/through_association.rb +36 -17
- data/lib/active_record/associations/singular_association.rb +13 -1
- data/lib/active_record/associations/through_association.rb +12 -4
- data/lib/active_record/attribute.rb +69 -19
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +69 -44
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +16 -3
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +32 -3
- data/lib/active_record/attribute_set/builder.rb +42 -16
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +54 -17
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +92 -108
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +116 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain.rb +20 -9
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +77 -41
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +17 -14
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +48 -24
- data/lib/active_record/migration.rb +362 -111
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +270 -73
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +152 -90
- data/lib/active_record/query_cache.rb +18 -23
- data/lib/active_record/querying.rb +12 -11
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +52 -41
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +302 -115
- data/lib/active_record/relation.rb +187 -120
- data/lib/active_record/relation/batches.rb +141 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +92 -117
- data/lib/active_record/relation/delegation.rb +8 -20
- data/lib/active_record/relation/finder_methods.rb +173 -89
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/result.rb +11 -4
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +105 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +54 -37
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +34 -16
- data/lib/active_record/scoping/default.rb +28 -10
- data/lib/active_record/scoping/named.rb +59 -26
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +3 -5
- data/lib/active_record/statement_cache.rb +17 -15
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +69 -0
- data/lib/active_record/tasks/database_tasks.rb +66 -49
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +63 -0
- data/lib/active_record/transactions.rb +139 -57
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +33 -33
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +58 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -8,20 +8,39 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
def disable_referential_integrity # :nodoc:
|
10
10
|
if supports_disable_referential_integrity?
|
11
|
+
original_exception = nil
|
12
|
+
|
11
13
|
begin
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
transaction(requires_new: true) do
|
15
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
|
16
|
+
end
|
17
|
+
rescue ActiveRecord::ActiveRecordError => e
|
18
|
+
original_exception = e
|
15
19
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
|
21
|
+
begin
|
22
|
+
yield
|
23
|
+
rescue ActiveRecord::InvalidForeignKey => e
|
24
|
+
warn <<-WARNING
|
25
|
+
WARNING: Rails was not able to disable referential integrity.
|
26
|
+
|
27
|
+
This is most likely caused due to missing permissions.
|
28
|
+
Rails needs superuser privileges to disable referential integrity.
|
29
|
+
|
30
|
+
cause: #{original_exception.try(:message)}
|
31
|
+
|
32
|
+
WARNING
|
33
|
+
raise e
|
34
|
+
end
|
35
|
+
|
20
36
|
begin
|
21
|
-
|
22
|
-
|
23
|
-
|
37
|
+
transaction(requires_new: true) do
|
38
|
+
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
|
39
|
+
end
|
40
|
+
rescue ActiveRecord::ActiveRecordError
|
24
41
|
end
|
42
|
+
else
|
43
|
+
yield
|
25
44
|
end
|
26
45
|
end
|
27
46
|
end
|
@@ -2,90 +2,153 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
3
3
|
module PostgreSQL
|
4
4
|
module ColumnMethods
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# Defines the primary key field.
|
6
|
+
# Use of the native PostgreSQL UUID type is supported, and can be used
|
7
|
+
# by defining your tables as such:
|
8
|
+
#
|
9
|
+
# create_table :stuffs, id: :uuid do |t|
|
10
|
+
# t.string :content
|
11
|
+
# t.timestamps
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# By default, this will use the +uuid_generate_v4()+ function from the
|
15
|
+
# +uuid-ossp+ extension, which MUST be enabled on your database. To enable
|
16
|
+
# the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
|
17
|
+
# migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
|
18
|
+
# set the +:default+ option to +nil+:
|
19
|
+
#
|
20
|
+
# create_table :stuffs, id: false do |t|
|
21
|
+
# t.primary_key :id, :uuid, default: nil
|
22
|
+
# t.uuid :foo_id
|
23
|
+
# t.timestamps
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# You may also pass a different UUID generation function from +uuid-ossp+
|
27
|
+
# or another library.
|
28
|
+
#
|
29
|
+
# Note that setting the UUID primary key default value to +nil+ will
|
30
|
+
# require you to assure that you always provide a UUID value before saving
|
31
|
+
# a record (as primary keys cannot be +nil+). This might be done via the
|
32
|
+
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
|
33
|
+
def primary_key(name, type = :primary_key, **options)
|
34
|
+
options[:default] = options.fetch(:default, 'uuid_generate_v4()') if type == :uuid
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def bigserial(*args, **options)
|
39
|
+
args.each { |name| column(name, :bigserial, options) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def bit(*args, **options)
|
43
|
+
args.each { |name| column(name, :bit, options) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def bit_varying(*args, **options)
|
47
|
+
args.each { |name| column(name, :bit_varying, options) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def cidr(*args, **options)
|
51
|
+
args.each { |name| column(name, :cidr, options) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def citext(*args, **options)
|
55
|
+
args.each { |name| column(name, :citext, options) }
|
8
56
|
end
|
9
57
|
|
10
|
-
def
|
11
|
-
options
|
12
|
-
column(args[0], :tsvector, options)
|
58
|
+
def daterange(*args, **options)
|
59
|
+
args.each { |name| column(name, :daterange, options) }
|
13
60
|
end
|
14
61
|
|
15
|
-
def
|
16
|
-
column(name, :
|
62
|
+
def hstore(*args, **options)
|
63
|
+
args.each { |name| column(name, :hstore, options) }
|
17
64
|
end
|
18
65
|
|
19
|
-
def
|
20
|
-
column(name, :
|
66
|
+
def inet(*args, **options)
|
67
|
+
args.each { |name| column(name, :inet, options) }
|
21
68
|
end
|
22
69
|
|
23
|
-
def
|
24
|
-
column(name, :
|
70
|
+
def int4range(*args, **options)
|
71
|
+
args.each { |name| column(name, :int4range, options) }
|
25
72
|
end
|
26
73
|
|
27
|
-
def
|
28
|
-
column(name, :
|
74
|
+
def int8range(*args, **options)
|
75
|
+
args.each { |name| column(name, :int8range, options) }
|
29
76
|
end
|
30
77
|
|
31
|
-
def
|
32
|
-
column(name, :
|
78
|
+
def json(*args, **options)
|
79
|
+
args.each { |name| column(name, :json, options) }
|
33
80
|
end
|
34
81
|
|
35
|
-
def
|
36
|
-
column(name, :
|
82
|
+
def jsonb(*args, **options)
|
83
|
+
args.each { |name| column(name, :jsonb, options) }
|
37
84
|
end
|
38
85
|
|
39
|
-
def
|
40
|
-
column(name, :
|
86
|
+
def ltree(*args, **options)
|
87
|
+
args.each { |name| column(name, :ltree, options) }
|
41
88
|
end
|
42
89
|
|
43
|
-
def
|
44
|
-
column(name, :
|
90
|
+
def macaddr(*args, **options)
|
91
|
+
args.each { |name| column(name, :macaddr, options) }
|
45
92
|
end
|
46
93
|
|
47
|
-
def
|
48
|
-
column(name, :
|
94
|
+
def money(*args, **options)
|
95
|
+
args.each { |name| column(name, :money, options) }
|
49
96
|
end
|
50
97
|
|
51
|
-
def
|
52
|
-
column(name, :
|
98
|
+
def numrange(*args, **options)
|
99
|
+
args.each { |name| column(name, :numrange, options) }
|
53
100
|
end
|
54
101
|
|
55
|
-
def
|
56
|
-
column(name, :
|
102
|
+
def point(*args, **options)
|
103
|
+
args.each { |name| column(name, :point, options) }
|
57
104
|
end
|
58
105
|
|
59
|
-
def
|
60
|
-
column(name, :
|
106
|
+
def line(*args, **options)
|
107
|
+
args.each { |name| column(name, :line, options) }
|
61
108
|
end
|
62
109
|
|
63
|
-
def
|
64
|
-
column(name, :
|
110
|
+
def lseg(*args, **options)
|
111
|
+
args.each { |name| column(name, :lseg, options) }
|
65
112
|
end
|
66
113
|
|
67
|
-
def
|
68
|
-
column(name, :
|
114
|
+
def box(*args, **options)
|
115
|
+
args.each { |name| column(name, :box, options) }
|
69
116
|
end
|
70
117
|
|
71
|
-
def
|
72
|
-
column(name, :
|
118
|
+
def path(*args, **options)
|
119
|
+
args.each { |name| column(name, :path, options) }
|
73
120
|
end
|
74
121
|
|
75
|
-
def
|
76
|
-
column(name, :
|
122
|
+
def polygon(*args, **options)
|
123
|
+
args.each { |name| column(name, :polygon, options) }
|
77
124
|
end
|
78
125
|
|
79
|
-
def
|
80
|
-
column(name, :
|
126
|
+
def circle(*args, **options)
|
127
|
+
args.each { |name| column(name, :circle, options) }
|
81
128
|
end
|
82
129
|
|
83
|
-
def
|
84
|
-
column(name, :
|
130
|
+
def serial(*args, **options)
|
131
|
+
args.each { |name| column(name, :serial, options) }
|
85
132
|
end
|
86
133
|
|
87
|
-
def
|
88
|
-
column(name, :
|
134
|
+
def tsrange(*args, **options)
|
135
|
+
args.each { |name| column(name, :tsrange, options) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def tstzrange(*args, **options)
|
139
|
+
args.each { |name| column(name, :tstzrange, options) }
|
140
|
+
end
|
141
|
+
|
142
|
+
def tsvector(*args, **options)
|
143
|
+
args.each { |name| column(name, :tsvector, options) }
|
144
|
+
end
|
145
|
+
|
146
|
+
def uuid(*args, **options)
|
147
|
+
args.each { |name| column(name, :uuid, options) }
|
148
|
+
end
|
149
|
+
|
150
|
+
def xml(*args, **options)
|
151
|
+
args.each { |name| column(name, :xml, options) }
|
89
152
|
end
|
90
153
|
end
|
91
154
|
|
@@ -96,41 +159,6 @@ module ActiveRecord
|
|
96
159
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
97
160
|
include ColumnMethods
|
98
161
|
|
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
162
|
def new_column_definition(name, type, options) # :nodoc:
|
135
163
|
column = super
|
136
164
|
column.array = options[:array]
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module ColumnDumper
|
5
|
+
def column_spec_for_primary_key(column)
|
6
|
+
spec = super
|
7
|
+
if schema_type(column) == :uuid
|
8
|
+
spec[:default] ||= 'nil'
|
9
|
+
end
|
10
|
+
spec
|
11
|
+
end
|
12
|
+
|
13
|
+
# Adds +:array+ option to the default set
|
14
|
+
def prepare_column_options(column)
|
15
|
+
spec = super
|
16
|
+
spec[:array] = 'true' if column.array?
|
17
|
+
spec
|
18
|
+
end
|
19
|
+
|
20
|
+
# Adds +:array+ as a valid migration key
|
21
|
+
def migration_keys
|
22
|
+
super + [:array]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def default_primary_key?(column)
|
28
|
+
schema_type(column) == :serial
|
29
|
+
end
|
30
|
+
|
31
|
+
def schema_type(column)
|
32
|
+
return super unless column.serial?
|
33
|
+
|
34
|
+
if column.bigint?
|
35
|
+
:bigserial
|
36
|
+
else
|
37
|
+
:serial
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def schema_expression(column)
|
42
|
+
super unless column.serial?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext/string/strip'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
@@ -5,33 +7,15 @@ module ActiveRecord
|
|
5
7
|
private
|
6
8
|
|
7
9
|
def visit_ColumnDefinition(o)
|
8
|
-
|
9
|
-
|
10
|
-
sql << " PRIMARY KEY "
|
11
|
-
add_column_options!(sql, column_options(o))
|
12
|
-
end
|
13
|
-
sql
|
10
|
+
o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
|
11
|
+
super
|
14
12
|
end
|
15
13
|
|
16
14
|
def add_column_options!(sql, options)
|
17
|
-
if options[:
|
18
|
-
sql <<
|
19
|
-
end
|
20
|
-
|
21
|
-
column = options.fetch(:column) { return super }
|
22
|
-
if column.type == :uuid && options[:default] =~ /\(\)/
|
23
|
-
sql << " DEFAULT #{options[:default]}"
|
24
|
-
else
|
25
|
-
super
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def type_for_column(column)
|
30
|
-
if column.array
|
31
|
-
@conn.lookup_cast_type("#{column.sql_type}[]")
|
32
|
-
else
|
33
|
-
super
|
15
|
+
if options[:collation]
|
16
|
+
sql << " COLLATE \"#{options[:collation]}\""
|
34
17
|
end
|
18
|
+
super
|
35
19
|
end
|
36
20
|
end
|
37
21
|
|
@@ -88,11 +72,13 @@ module ActiveRecord
|
|
88
72
|
|
89
73
|
# Returns the list of all tables in the schema search path.
|
90
74
|
def tables(name = nil)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
75
|
+
if name
|
76
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
77
|
+
Passing arguments to #tables is deprecated without replacement.
|
78
|
+
MSG
|
79
|
+
end
|
80
|
+
|
81
|
+
select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
|
96
82
|
end
|
97
83
|
|
98
84
|
def data_sources # :nodoc
|
@@ -109,10 +95,20 @@ module ActiveRecord
|
|
109
95
|
# If the schema is not specified as part of +name+ then it will only find tables within
|
110
96
|
# the current schema search path (regardless of permissions to access tables in other schemas)
|
111
97
|
def table_exists?(name)
|
98
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
99
|
+
#table_exists? currently checks both tables and views.
|
100
|
+
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
101
|
+
Use #data_source_exists? instead.
|
102
|
+
MSG
|
103
|
+
|
104
|
+
data_source_exists?(name)
|
105
|
+
end
|
106
|
+
|
107
|
+
def data_source_exists?(name)
|
112
108
|
name = Utils.extract_schema_qualified_name(name.to_s)
|
113
109
|
return false unless name.identifier
|
114
110
|
|
115
|
-
|
111
|
+
select_value(<<-SQL, 'SCHEMA').to_i > 0
|
116
112
|
SELECT COUNT(*)
|
117
113
|
FROM pg_class c
|
118
114
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
@@ -121,126 +117,176 @@ module ActiveRecord
|
|
121
117
|
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
122
118
|
SQL
|
123
119
|
end
|
124
|
-
alias data_source_exists? table_exists?
|
125
120
|
|
126
|
-
def
|
127
|
-
|
121
|
+
def views # :nodoc:
|
122
|
+
select_values(<<-SQL, 'SCHEMA')
|
123
|
+
SELECT c.relname
|
124
|
+
FROM pg_class c
|
125
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
126
|
+
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
127
|
+
AND n.nspname = ANY (current_schemas(false))
|
128
|
+
SQL
|
129
|
+
end
|
130
|
+
|
131
|
+
def view_exists?(view_name) # :nodoc:
|
132
|
+
name = Utils.extract_schema_qualified_name(view_name.to_s)
|
133
|
+
return false unless name.identifier
|
134
|
+
|
135
|
+
select_values(<<-SQL, 'SCHEMA').any?
|
136
|
+
SELECT c.relname
|
137
|
+
FROM pg_class c
|
138
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
139
|
+
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
140
|
+
AND c.relname = '#{name.identifier}'
|
141
|
+
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
142
|
+
SQL
|
143
|
+
end
|
144
|
+
|
145
|
+
def drop_table(table_name, options = {}) # :nodoc:
|
146
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
128
147
|
end
|
129
148
|
|
130
149
|
# Returns true if schema exists.
|
131
150
|
def schema_exists?(name)
|
132
|
-
|
133
|
-
SELECT COUNT(*)
|
134
|
-
FROM pg_namespace
|
135
|
-
WHERE nspname = '#{name}'
|
136
|
-
SQL
|
151
|
+
select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
|
137
152
|
end
|
138
153
|
|
154
|
+
# Verifies existence of an index with a given name.
|
139
155
|
def index_name_exists?(table_name, index_name, default)
|
140
|
-
|
156
|
+
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
157
|
+
index = Utils.extract_schema_qualified_name(index_name.to_s)
|
158
|
+
|
159
|
+
select_value(<<-SQL, 'SCHEMA').to_i > 0
|
141
160
|
SELECT COUNT(*)
|
142
161
|
FROM pg_class t
|
143
162
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
144
163
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
164
|
+
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
145
165
|
WHERE i.relkind = 'i'
|
146
|
-
AND i.relname = '#{
|
147
|
-
AND t.relname = '#{
|
148
|
-
AND
|
166
|
+
AND i.relname = '#{index.identifier}'
|
167
|
+
AND t.relname = '#{table.identifier}'
|
168
|
+
AND n.nspname = #{index.schema ? "'#{index.schema}'" : 'ANY (current_schemas(false))'}
|
149
169
|
SQL
|
150
170
|
end
|
151
171
|
|
152
172
|
# Returns an array of indexes for the given table.
|
153
173
|
def indexes(table_name, name = nil)
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
174
|
+
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
175
|
+
|
176
|
+
result = query(<<-SQL, 'SCHEMA')
|
177
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
|
178
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment,
|
179
|
+
(SELECT COUNT(*) FROM pg_opclass o
|
180
|
+
JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
|
181
|
+
ON o.oid = c.oid WHERE o.opcdefault = 'f')
|
182
|
+
FROM pg_class t
|
183
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
184
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
185
|
+
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
186
|
+
WHERE i.relkind = 'i'
|
187
|
+
AND d.indisprimary = 'f'
|
188
|
+
AND t.relname = '#{table.identifier}'
|
189
|
+
AND n.nspname = #{table.schema ? "'#{table.schema}'" : 'ANY (current_schemas(false))'}
|
163
190
|
ORDER BY i.relname
|
164
191
|
SQL
|
165
192
|
|
166
193
|
result.map do |row|
|
167
194
|
index_name = row[0]
|
168
|
-
unique = row[1]
|
169
|
-
indkey = row[2].split(" ")
|
195
|
+
unique = row[1]
|
196
|
+
indkey = row[2].split(" ").map(&:to_i)
|
170
197
|
inddef = row[3]
|
171
198
|
oid = row[4]
|
199
|
+
comment = row[5]
|
200
|
+
opclass = row[6]
|
172
201
|
|
173
|
-
|
174
|
-
SELECT a.attnum, a.attname
|
175
|
-
FROM pg_attribute a
|
176
|
-
WHERE a.attrelid = #{oid}
|
177
|
-
AND a.attnum IN (#{indkey.join(",")})
|
178
|
-
SQL
|
202
|
+
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
|
179
203
|
|
180
|
-
|
204
|
+
if indkey.include?(0) || opclass > 0
|
205
|
+
columns = expressions
|
206
|
+
else
|
207
|
+
columns = Hash[query(<<-SQL.strip_heredoc, "SCHEMA")].values_at(*indkey).compact
|
208
|
+
SELECT a.attnum, a.attname
|
209
|
+
FROM pg_attribute a
|
210
|
+
WHERE a.attrelid = #{oid}
|
211
|
+
AND a.attnum IN (#{indkey.join(",")})
|
212
|
+
SQL
|
181
213
|
|
182
|
-
unless column_names.empty?
|
183
214
|
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
188
|
-
|
189
|
-
IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
|
215
|
+
orders = Hash[
|
216
|
+
expressions.scan(/(\w+) DESC/).flatten.map { |order_column| [order_column, :desc] }
|
217
|
+
]
|
190
218
|
end
|
219
|
+
|
220
|
+
IndexDefinition.new(table_name, index_name, unique, columns, [], orders, where, nil, using.to_sym, comment.presence)
|
191
221
|
end.compact
|
192
222
|
end
|
193
223
|
|
194
224
|
# Returns the list of all column definitions for a table.
|
195
|
-
def columns(table_name)
|
196
|
-
|
197
|
-
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
|
198
|
-
oid =
|
199
|
-
|
225
|
+
def columns(table_name) # :nodoc:
|
226
|
+
table_name = table_name.to_s
|
227
|
+
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation, comment|
|
228
|
+
oid = oid.to_i
|
229
|
+
fmod = fmod.to_i
|
230
|
+
type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
|
231
|
+
default_value = extract_value_from_default(default)
|
200
232
|
default_function = extract_default_function(default_value, default)
|
201
|
-
new_column(column_name, default_value,
|
233
|
+
new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation, comment: comment.presence, max_identifier_length: max_identifier_length)
|
202
234
|
end
|
203
235
|
end
|
204
236
|
|
205
|
-
def new_column(
|
206
|
-
PostgreSQLColumn.new(
|
237
|
+
def new_column(*args) # :nodoc:
|
238
|
+
PostgreSQLColumn.new(*args)
|
239
|
+
end
|
240
|
+
|
241
|
+
def table_options(table_name) # :nodoc:
|
242
|
+
if comment = table_comment(table_name)
|
243
|
+
{ comment: comment }
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Returns a comment stored in database for given table
|
248
|
+
def table_comment(table_name) # :nodoc:
|
249
|
+
name = Utils.extract_schema_qualified_name(table_name.to_s)
|
250
|
+
if name.identifier
|
251
|
+
select_value(<<-SQL.strip_heredoc, 'SCHEMA')
|
252
|
+
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
|
253
|
+
FROM pg_catalog.pg_class c
|
254
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
255
|
+
WHERE c.relname = #{quote(name.identifier)}
|
256
|
+
AND c.relkind IN ('r') -- (r)elation/table
|
257
|
+
AND n.nspname = #{name.schema ? quote(name.schema) : 'ANY (current_schemas(false))'}
|
258
|
+
SQL
|
259
|
+
end
|
207
260
|
end
|
208
261
|
|
209
262
|
# Returns the current database name.
|
210
263
|
def current_database
|
211
|
-
|
264
|
+
select_value('select current_database()', 'SCHEMA')
|
212
265
|
end
|
213
266
|
|
214
267
|
# Returns the current schema name.
|
215
268
|
def current_schema
|
216
|
-
|
269
|
+
select_value('SELECT current_schema', 'SCHEMA')
|
217
270
|
end
|
218
271
|
|
219
272
|
# Returns the current database encoding format.
|
220
273
|
def encoding
|
221
|
-
|
222
|
-
SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
|
223
|
-
WHERE pg_database.datname LIKE '#{current_database}'
|
224
|
-
end_sql
|
274
|
+
select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
225
275
|
end
|
226
276
|
|
227
277
|
# Returns the current database collation.
|
228
278
|
def collation
|
229
|
-
|
230
|
-
SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
231
|
-
end_sql
|
279
|
+
select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
232
280
|
end
|
233
281
|
|
234
282
|
# Returns the current database ctype.
|
235
283
|
def ctype
|
236
|
-
|
237
|
-
SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
238
|
-
end_sql
|
284
|
+
select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
239
285
|
end
|
240
286
|
|
241
287
|
# Returns an array of schema names.
|
242
288
|
def schema_names
|
243
|
-
|
289
|
+
select_values(<<-SQL, 'SCHEMA')
|
244
290
|
SELECT nspname
|
245
291
|
FROM pg_namespace
|
246
292
|
WHERE nspname !~ '^pg_.*'
|
@@ -251,12 +297,12 @@ module ActiveRecord
|
|
251
297
|
|
252
298
|
# Creates a schema for the given schema name.
|
253
299
|
def create_schema schema_name
|
254
|
-
execute "CREATE SCHEMA #{schema_name}"
|
300
|
+
execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
|
255
301
|
end
|
256
302
|
|
257
303
|
# Drops the schema for the given schema name.
|
258
|
-
def drop_schema
|
259
|
-
execute "DROP SCHEMA #{schema_name} CASCADE"
|
304
|
+
def drop_schema(schema_name, options = {})
|
305
|
+
execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
|
260
306
|
end
|
261
307
|
|
262
308
|
# Sets the schema search path to a string of comma-separated schema names.
|
@@ -273,12 +319,12 @@ module ActiveRecord
|
|
273
319
|
|
274
320
|
# Returns the active schema search path.
|
275
321
|
def schema_search_path
|
276
|
-
@schema_search_path ||=
|
322
|
+
@schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
|
277
323
|
end
|
278
324
|
|
279
325
|
# Returns the current client message level.
|
280
326
|
def client_min_messages
|
281
|
-
|
327
|
+
select_value('SHOW client_min_messages', 'SCHEMA')
|
282
328
|
end
|
283
329
|
|
284
330
|
# Set the client message level.
|
@@ -296,10 +342,7 @@ module ActiveRecord
|
|
296
342
|
end
|
297
343
|
|
298
344
|
def serial_sequence(table, column)
|
299
|
-
|
300
|
-
SELECT pg_get_serial_sequence('#{table}', '#{column}')
|
301
|
-
eosql
|
302
|
-
result.rows.first.first
|
345
|
+
select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", 'SCHEMA')
|
303
346
|
end
|
304
347
|
|
305
348
|
# Sets the sequence of a table's primary key to the specified value.
|
@@ -310,11 +353,9 @@ module ActiveRecord
|
|
310
353
|
if sequence
|
311
354
|
quoted_sequence = quote_table_name(sequence)
|
312
355
|
|
313
|
-
select_value
|
314
|
-
SELECT setval('#{quoted_sequence}', #{value})
|
315
|
-
end_sql
|
356
|
+
select_value("SELECT setval('#{quoted_sequence}', #{value})", 'SCHEMA')
|
316
357
|
else
|
317
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
|
358
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
|
318
359
|
end
|
319
360
|
end
|
320
361
|
end
|
@@ -329,14 +370,22 @@ module ActiveRecord
|
|
329
370
|
end
|
330
371
|
|
331
372
|
if @logger && pk && !sequence
|
332
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence"
|
373
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence."
|
333
374
|
end
|
334
375
|
|
335
376
|
if pk && sequence
|
336
377
|
quoted_sequence = quote_table_name(sequence)
|
378
|
+
max_pk = select_value("select MAX(#{quote_column_name pk}) from #{quote_table_name(table)}")
|
379
|
+
if max_pk.nil?
|
380
|
+
if postgresql_version >= 100000
|
381
|
+
minvalue = select_value("SELECT seqmin from pg_sequence where seqrelid = '#{quoted_sequence}'::regclass")
|
382
|
+
else
|
383
|
+
minvalue = select_value("SELECT min_value FROM #{quoted_sequence}")
|
384
|
+
end
|
385
|
+
end
|
337
386
|
|
338
|
-
select_value
|
339
|
-
SELECT setval('#{quoted_sequence}',
|
387
|
+
select_value(<<-end_sql, "SCHEMA")
|
388
|
+
SELECT setval('#{quoted_sequence}', #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})
|
340
389
|
end_sql
|
341
390
|
end
|
342
391
|
end
|
@@ -395,17 +444,19 @@ module ActiveRecord
|
|
395
444
|
nil
|
396
445
|
end
|
397
446
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
447
|
+
def primary_keys(table_name) # :nodoc:
|
448
|
+
select_values(<<-SQL.strip_heredoc, 'SCHEMA')
|
449
|
+
WITH pk_constraint AS (
|
450
|
+
SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
|
451
|
+
WHERE contype = 'p'
|
452
|
+
AND conrelid = '#{quote_table_name(table_name)}'::regclass
|
453
|
+
), cons AS (
|
454
|
+
SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
|
455
|
+
)
|
456
|
+
SELECT attr.attname FROM pg_attribute attr
|
457
|
+
INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
|
458
|
+
ORDER BY cons.rownum
|
459
|
+
SQL
|
409
460
|
end
|
410
461
|
|
411
462
|
# Renames a table.
|
@@ -422,7 +473,7 @@ module ActiveRecord
|
|
422
473
|
new_seq = "#{new_name}_#{pk}_seq"
|
423
474
|
idx = "#{table_name}_pkey"
|
424
475
|
new_idx = "#{new_name}_pkey"
|
425
|
-
execute "ALTER TABLE #{
|
476
|
+
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
426
477
|
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
427
478
|
end
|
428
479
|
|
@@ -432,50 +483,69 @@ module ActiveRecord
|
|
432
483
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
433
484
|
clear_cache!
|
434
485
|
super
|
486
|
+
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
435
487
|
end
|
436
488
|
|
437
|
-
|
438
|
-
def change_column(table_name, column_name, type, options = {})
|
489
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
439
490
|
clear_cache!
|
440
491
|
quoted_table_name = quote_table_name(table_name)
|
441
|
-
|
442
|
-
sql_type
|
443
|
-
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{
|
444
|
-
|
445
|
-
|
446
|
-
|
492
|
+
quoted_column_name = quote_column_name(column_name)
|
493
|
+
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale], options[:array])
|
494
|
+
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
|
495
|
+
if options[:collation]
|
496
|
+
sql << " COLLATE \"#{options[:collation]}\""
|
497
|
+
end
|
498
|
+
if options[:using]
|
499
|
+
sql << " USING #{options[:using]}"
|
500
|
+
elsif options[:cast_as]
|
501
|
+
cast_as_type = type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale], options[:array])
|
502
|
+
sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
447
503
|
end
|
448
504
|
execute sql
|
449
505
|
|
450
506
|
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
451
507
|
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
508
|
+
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
452
509
|
end
|
453
510
|
|
454
511
|
# Changes the default value of a table column.
|
455
|
-
def change_column_default(table_name, column_name,
|
512
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
456
513
|
clear_cache!
|
457
514
|
column = column_for(table_name, column_name)
|
458
515
|
return unless column
|
459
516
|
|
517
|
+
default = extract_new_default_value(default_or_changes)
|
460
518
|
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
461
519
|
if default.nil?
|
462
520
|
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
463
521
|
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
464
522
|
execute alter_column_query % "DROP DEFAULT"
|
465
523
|
else
|
466
|
-
execute alter_column_query % "SET DEFAULT #{
|
524
|
+
execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
467
525
|
end
|
468
526
|
end
|
469
527
|
|
470
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
528
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
471
529
|
clear_cache!
|
472
530
|
unless null || default.nil?
|
473
531
|
column = column_for(table_name, column_name)
|
474
|
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{
|
532
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
|
475
533
|
end
|
476
534
|
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
477
535
|
end
|
478
536
|
|
537
|
+
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
538
|
+
def change_column_comment(table_name, column_name, comment) # :nodoc:
|
539
|
+
clear_cache!
|
540
|
+
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
|
541
|
+
end
|
542
|
+
|
543
|
+
# Adds comment for given table or drops it if +comment+ is a +nil+
|
544
|
+
def change_table_comment(table_name, comment) # :nodoc:
|
545
|
+
clear_cache!
|
546
|
+
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
|
547
|
+
end
|
548
|
+
|
479
549
|
# Renames a column in a table.
|
480
550
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
481
551
|
clear_cache!
|
@@ -484,14 +554,38 @@ module ActiveRecord
|
|
484
554
|
end
|
485
555
|
|
486
556
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
487
|
-
index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
|
488
|
-
execute
|
557
|
+
index_name, index_type, index_columns, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
|
558
|
+
execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}").tap do
|
559
|
+
execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
|
560
|
+
end
|
489
561
|
end
|
490
562
|
|
491
|
-
def remove_index
|
492
|
-
|
563
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
564
|
+
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
565
|
+
|
566
|
+
if options.is_a?(Hash) && options.key?(:name)
|
567
|
+
provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
|
568
|
+
|
569
|
+
options[:name] = provided_index.identifier
|
570
|
+
table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
|
571
|
+
|
572
|
+
if provided_index.schema.present? && table.schema != provided_index.schema
|
573
|
+
raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, options))
|
578
|
+
algorithm =
|
579
|
+
if options.is_a?(Hash) && options.key?(:algorithm)
|
580
|
+
index_algorithms.fetch(options[:algorithm]) do
|
581
|
+
raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
|
582
|
+
end
|
583
|
+
end
|
584
|
+
execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
|
493
585
|
end
|
494
586
|
|
587
|
+
# Renames an index of a table. Raises error if length of new
|
588
|
+
# index name is greater than allowed limit.
|
495
589
|
def rename_index(table_name, old_name, new_name)
|
496
590
|
validate_index_length!(table_name, new_name)
|
497
591
|
|
@@ -499,7 +593,7 @@ module ActiveRecord
|
|
499
593
|
end
|
500
594
|
|
501
595
|
def foreign_keys(table_name)
|
502
|
-
fk_info = select_all
|
596
|
+
fk_info = select_all(<<-SQL.strip_heredoc, 'SCHEMA')
|
503
597
|
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
598
|
FROM pg_constraint c
|
505
599
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
@@ -535,46 +629,36 @@ module ActiveRecord
|
|
535
629
|
end
|
536
630
|
end
|
537
631
|
|
538
|
-
def index_name_length
|
539
|
-
63
|
540
|
-
end
|
541
|
-
|
542
632
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
543
|
-
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
544
|
-
case type.to_s
|
633
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil, array = nil)
|
634
|
+
sql = case type.to_s
|
545
635
|
when 'binary'
|
546
636
|
# PostgreSQL doesn't support limits on binary (bytea) columns.
|
547
|
-
# The hard limit is
|
637
|
+
# The hard limit is 1GB, because of a 32-bit size field, and TOAST.
|
548
638
|
case limit
|
549
639
|
when nil, 0..0x3fffffff; super(type)
|
550
640
|
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
|
551
641
|
end
|
552
642
|
when 'text'
|
553
643
|
# PostgreSQL doesn't support limits on text columns.
|
554
|
-
# The hard limit is
|
644
|
+
# The hard limit is 1GB, according to section 8.3 in the manual.
|
555
645
|
case limit
|
556
646
|
when nil, 0..0x3fffffff; super(type)
|
557
647
|
else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
|
558
648
|
end
|
559
649
|
when 'integer'
|
560
|
-
return 'integer' unless limit
|
561
|
-
|
562
650
|
case limit
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
end
|
568
|
-
when 'datetime'
|
569
|
-
return super unless precision
|
570
|
-
|
571
|
-
case precision
|
572
|
-
when 0..6; "timestamp(#{precision})"
|
573
|
-
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
651
|
+
when 1, 2; 'smallint'
|
652
|
+
when nil, 3, 4; 'integer'
|
653
|
+
when 5..8; 'bigint'
|
654
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
|
574
655
|
end
|
575
656
|
else
|
576
|
-
super
|
657
|
+
super(type, limit, precision, scale)
|
577
658
|
end
|
659
|
+
|
660
|
+
sql << '[]' if array && type != :primary_key
|
661
|
+
sql
|
578
662
|
end
|
579
663
|
|
580
664
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
@@ -590,6 +674,18 @@ module ActiveRecord
|
|
590
674
|
|
591
675
|
[super, *order_columns].join(', ')
|
592
676
|
end
|
677
|
+
|
678
|
+
def fetch_type_metadata(column_name, sql_type, oid, fmod)
|
679
|
+
cast_type = get_oid_type(oid, fmod, column_name, sql_type)
|
680
|
+
simple_type = SqlTypeMetadata.new(
|
681
|
+
sql_type: sql_type,
|
682
|
+
type: cast_type.type,
|
683
|
+
limit: cast_type.limit,
|
684
|
+
precision: cast_type.precision,
|
685
|
+
scale: cast_type.scale,
|
686
|
+
)
|
687
|
+
PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
|
688
|
+
end
|
593
689
|
end
|
594
690
|
end
|
595
691
|
end
|