activerecord 4.2.11.3 → 5.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1029 -1349
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/examples/performance.rb +2 -2
- data/lib/active_record.rb +7 -3
- data/lib/active_record/aggregations.rb +35 -25
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations.rb +305 -204
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +10 -8
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +20 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +41 -18
- data/lib/active_record/associations/builder/collection_association.rb +8 -24
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +10 -5
- data/lib/active_record/associations/builder/singular_association.rb +2 -9
- data/lib/active_record/associations/collection_association.rb +40 -43
- data/lib/active_record/associations/collection_proxy.rb +55 -29
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +20 -71
- data/lib/active_record/associations/has_many_through_association.rb +8 -52
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +28 -18
- data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
- data/lib/active_record/associations/preloader.rb +13 -4
- data/lib/active_record/associations/preloader/association.rb +45 -51
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- 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/through_association.rb +5 -4
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +61 -17
- data/lib/active_record/attribute/user_provided_default.rb +23 -0
- data/lib/active_record/attribute_assignment.rb +27 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +79 -26
- 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 +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +26 -42
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
- data/lib/active_record/attribute_methods/write.rb +13 -24
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attributes.rb +194 -81
- data/lib/active_record/autosave_association.rb +33 -15
- data/lib/active_record/base.rb +30 -18
- data/lib/active_record/callbacks.rb +36 -40
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
- data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
- data/lib/active_record/connection_adapters/column.rb +27 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
- data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -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 -2
- 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 +23 -16
- 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 +18 -11
- 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 +54 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
- 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/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
- data/lib/active_record/connection_handling.rb +5 -5
- data/lib/active_record/core.rb +72 -104
- data/lib/active_record/counter_cache.rb +9 -20
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +110 -76
- data/lib/active_record/errors.rb +72 -47
- 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 +19 -4
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +27 -40
- data/lib/active_record/integration.rb +4 -4
- 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 +10 -14
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +40 -22
- data/lib/active_record/migration.rb +304 -133
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +90 -0
- data/lib/active_record/model_schema.rb +92 -40
- data/lib/active_record/nested_attributes.rb +45 -34
- data/lib/active_record/null_relation.rb +15 -7
- data/lib/active_record/persistence.rb +112 -72
- data/lib/active_record/querying.rb +6 -5
- data/lib/active_record/railtie.rb +20 -13
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +47 -38
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +182 -57
- data/lib/active_record/relation.rb +152 -100
- data/lib/active_record/relation/batches.rb +133 -33
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +80 -101
- data/lib/active_record/relation/delegation.rb +6 -19
- data/lib/active_record/relation/finder_methods.rb +58 -46
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +13 -42
- data/lib/active_record/relation/predicate_builder.rb +99 -105
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -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/range_handler.rb +17 -0
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +274 -238
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -6
- data/lib/active_record/relation/where_clause.rb +173 -0
- data/lib/active_record/relation/where_clause_factory.rb +37 -0
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +94 -65
- data/lib/active_record/schema.rb +23 -22
- data/lib/active_record/schema_dumper.rb +33 -22
- data/lib/active_record/schema_migration.rb +10 -4
- data/lib/active_record/scoping.rb +17 -6
- data/lib/active_record/scoping/default.rb +19 -6
- data/lib/active_record/scoping/named.rb +39 -28
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +15 -13
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +54 -0
- data/lib/active_record/table_metadata.rb +64 -0
- data/lib/active_record/tasks/database_tasks.rb +30 -40
- data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +16 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- 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 +9 -14
- data/lib/active_record/type/time.rb +3 -21
- 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 +24 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +36 -0
- data/lib/active_record/validations/presence.rb +12 -12
- data/lib/active_record/validations/uniqueness.rb +24 -21
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +50 -35
- 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
@@ -5,13 +5,13 @@ module ActiveRecord
|
|
5
5
|
class Uuid < Type::Value # :nodoc:
|
6
6
|
ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
|
7
7
|
|
8
|
-
alias_method :
|
8
|
+
alias_method :serialize, :deserialize
|
9
9
|
|
10
10
|
def type
|
11
11
|
:uuid
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def cast(value)
|
15
15
|
value.to_s[ACCEPTABLE_UUID, 0]
|
16
16
|
end
|
17
17
|
end
|
@@ -31,6 +31,11 @@ module ActiveRecord
|
|
31
31
|
Utils.extract_schema_qualified_name(name.to_s).quoted
|
32
32
|
end
|
33
33
|
|
34
|
+
# Quotes schema names for use in SQL queries.
|
35
|
+
def quote_schema_name(name)
|
36
|
+
PGconn.quote_ident(name)
|
37
|
+
end
|
38
|
+
|
34
39
|
def quote_table_name_for_assignment(table, attr)
|
35
40
|
quote_column_name(attr)
|
36
41
|
end
|
@@ -40,30 +45,32 @@ module ActiveRecord
|
|
40
45
|
PGconn.quote_ident(name.to_s)
|
41
46
|
end
|
42
47
|
|
43
|
-
# Quote date/time values for use in SQL input.
|
44
|
-
# if the value is a Time responding to usec.
|
48
|
+
# Quote date/time values for use in SQL input.
|
45
49
|
def quoted_date(value) #:nodoc:
|
46
|
-
result = super
|
47
|
-
if value.acts_like?(:time) && value.respond_to?(:usec)
|
48
|
-
result = "#{result}.#{sprintf("%06d", value.usec)}"
|
49
|
-
end
|
50
|
-
|
51
50
|
if value.year <= 0
|
52
51
|
bce_year = format("%04d", -value.year + 1)
|
53
|
-
|
52
|
+
super.sub(/^-?\d+/, bce_year) + " BC"
|
53
|
+
else
|
54
|
+
super
|
54
55
|
end
|
55
|
-
result
|
56
56
|
end
|
57
57
|
|
58
58
|
# Does not quote function default values for UUID columns
|
59
|
-
def
|
59
|
+
def quote_default_expression(value, column) #:nodoc:
|
60
60
|
if column.type == :uuid && value =~ /\(\)/
|
61
61
|
value
|
62
|
+
elsif column.respond_to?(:array?)
|
63
|
+
value = type_cast_from_column(column, value)
|
64
|
+
quote(value)
|
62
65
|
else
|
63
|
-
|
66
|
+
super
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
70
|
+
def lookup_cast_type_from_column(column) # :nodoc:
|
71
|
+
type_map.lookup(column.oid, column.fmod, column.sql_type)
|
72
|
+
end
|
73
|
+
|
67
74
|
private
|
68
75
|
|
69
76
|
def _quote(value)
|
@@ -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,54 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module PostgreSQL
|
4
|
+
module ColumnDumper
|
5
|
+
def column_spec_for_primary_key(column)
|
6
|
+
spec = {}
|
7
|
+
if column.serial?
|
8
|
+
return unless column.bigint?
|
9
|
+
spec[:id] = ':bigserial'
|
10
|
+
elsif column.type == :uuid
|
11
|
+
spec[:id] = ':uuid'
|
12
|
+
spec[:default] = column.default_function.inspect
|
13
|
+
else
|
14
|
+
spec[:id] = column.type.inspect
|
15
|
+
spec.merge!(prepare_column_options(column).delete_if { |key, _| [:name, :type, :null].include?(key) })
|
16
|
+
end
|
17
|
+
spec
|
18
|
+
end
|
19
|
+
|
20
|
+
# Adds +:array+ option to the default set
|
21
|
+
def prepare_column_options(column)
|
22
|
+
spec = super
|
23
|
+
spec[:array] = 'true' if column.array?
|
24
|
+
spec
|
25
|
+
end
|
26
|
+
|
27
|
+
# Adds +:array+ as a valid migration key
|
28
|
+
def migration_keys
|
29
|
+
super + [:array]
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def schema_type(column)
|
35
|
+
return super unless column.serial?
|
36
|
+
|
37
|
+
if column.bigint?
|
38
|
+
'bigserial'
|
39
|
+
else
|
40
|
+
'serial'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def schema_default(column)
|
45
|
+
if column.default_function
|
46
|
+
column.default_function.inspect unless column.serial?
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -5,33 +5,15 @@ module ActiveRecord
|
|
5
5
|
private
|
6
6
|
|
7
7
|
def visit_ColumnDefinition(o)
|
8
|
-
|
9
|
-
|
10
|
-
sql << " PRIMARY KEY "
|
11
|
-
add_column_options!(sql, column_options(o))
|
12
|
-
end
|
13
|
-
sql
|
8
|
+
o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
|
9
|
+
super
|
14
10
|
end
|
15
11
|
|
16
12
|
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
|
13
|
+
if options[:collation]
|
14
|
+
sql << " COLLATE \"#{options[:collation]}\""
|
34
15
|
end
|
16
|
+
super
|
35
17
|
end
|
36
18
|
end
|
37
19
|
|
@@ -88,11 +70,13 @@ module ActiveRecord
|
|
88
70
|
|
89
71
|
# Returns the list of all tables in the schema search path.
|
90
72
|
def tables(name = nil)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
73
|
+
if name
|
74
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
75
|
+
Passing arguments to #tables is deprecated without replacement.
|
76
|
+
MSG
|
77
|
+
end
|
78
|
+
|
79
|
+
select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
|
96
80
|
end
|
97
81
|
|
98
82
|
def data_sources # :nodoc
|
@@ -109,10 +93,20 @@ module ActiveRecord
|
|
109
93
|
# If the schema is not specified as part of +name+ then it will only find tables within
|
110
94
|
# the current schema search path (regardless of permissions to access tables in other schemas)
|
111
95
|
def table_exists?(name)
|
96
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
97
|
+
#table_exists? currently checks both tables and views.
|
98
|
+
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
99
|
+
Use #data_source_exists? instead.
|
100
|
+
MSG
|
101
|
+
|
102
|
+
data_source_exists?(name)
|
103
|
+
end
|
104
|
+
|
105
|
+
def data_source_exists?(name)
|
112
106
|
name = Utils.extract_schema_qualified_name(name.to_s)
|
113
107
|
return false unless name.identifier
|
114
108
|
|
115
|
-
|
109
|
+
select_value(<<-SQL, 'SCHEMA').to_i > 0
|
116
110
|
SELECT COUNT(*)
|
117
111
|
FROM pg_class c
|
118
112
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
@@ -121,52 +115,79 @@ module ActiveRecord
|
|
121
115
|
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
122
116
|
SQL
|
123
117
|
end
|
124
|
-
alias data_source_exists? table_exists?
|
125
118
|
|
126
|
-
def
|
127
|
-
|
119
|
+
def views # :nodoc:
|
120
|
+
select_values(<<-SQL, 'SCHEMA')
|
121
|
+
SELECT c.relname
|
122
|
+
FROM pg_class c
|
123
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
124
|
+
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
125
|
+
AND n.nspname = ANY (current_schemas(false))
|
126
|
+
SQL
|
127
|
+
end
|
128
|
+
|
129
|
+
def view_exists?(view_name) # :nodoc:
|
130
|
+
name = Utils.extract_schema_qualified_name(view_name.to_s)
|
131
|
+
return false unless name.identifier
|
132
|
+
|
133
|
+
select_values(<<-SQL, 'SCHEMA').any?
|
134
|
+
SELECT c.relname
|
135
|
+
FROM pg_class c
|
136
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
137
|
+
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
138
|
+
AND c.relname = '#{name.identifier}'
|
139
|
+
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
140
|
+
SQL
|
141
|
+
end
|
142
|
+
|
143
|
+
def drop_table(table_name, options = {}) # :nodoc:
|
144
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
128
145
|
end
|
129
146
|
|
130
147
|
# Returns true if schema exists.
|
131
148
|
def schema_exists?(name)
|
132
|
-
|
133
|
-
SELECT COUNT(*)
|
134
|
-
FROM pg_namespace
|
135
|
-
WHERE nspname = '#{name}'
|
136
|
-
SQL
|
149
|
+
select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = '#{name}'", 'SCHEMA').to_i > 0
|
137
150
|
end
|
138
151
|
|
152
|
+
# Verifies existence of an index with a given name.
|
139
153
|
def index_name_exists?(table_name, index_name, default)
|
140
|
-
|
154
|
+
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
155
|
+
index = Utils.extract_schema_qualified_name(index_name.to_s)
|
156
|
+
|
157
|
+
select_value(<<-SQL, 'SCHEMA').to_i > 0
|
141
158
|
SELECT COUNT(*)
|
142
159
|
FROM pg_class t
|
143
160
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
144
161
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
162
|
+
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
145
163
|
WHERE i.relkind = 'i'
|
146
|
-
AND i.relname = '#{
|
147
|
-
AND t.relname = '#{
|
148
|
-
AND
|
164
|
+
AND i.relname = '#{index.identifier}'
|
165
|
+
AND t.relname = '#{table.identifier}'
|
166
|
+
AND n.nspname = #{index.schema ? "'#{index.schema}'" : 'ANY (current_schemas(false))'}
|
149
167
|
SQL
|
150
168
|
end
|
151
169
|
|
152
170
|
# Returns an array of indexes for the given table.
|
153
171
|
def indexes(table_name, name = nil)
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
172
|
+
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
173
|
+
|
174
|
+
result = query(<<-SQL, 'SCHEMA')
|
175
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
176
|
+
FROM pg_class t
|
177
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
178
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
179
|
+
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
180
|
+
WHERE i.relkind = 'i'
|
181
|
+
AND d.indisprimary = 'f'
|
182
|
+
AND t.relname = '#{table.identifier}'
|
183
|
+
AND n.nspname = #{table.schema ? "'#{table.schema}'" : 'ANY (current_schemas(false))'}
|
163
184
|
ORDER BY i.relname
|
164
185
|
SQL
|
165
186
|
|
166
187
|
result.map do |row|
|
167
188
|
index_name = row[0]
|
168
|
-
unique = row[1]
|
169
|
-
indkey = row[2].split(" ")
|
189
|
+
unique = row[1]
|
190
|
+
indkey = row[2].split(" ").map(&:to_i)
|
170
191
|
inddef = row[3]
|
171
192
|
oid = row[4]
|
172
193
|
|
@@ -194,53 +215,48 @@ module ActiveRecord
|
|
194
215
|
# Returns the list of all column definitions for a table.
|
195
216
|
def columns(table_name)
|
196
217
|
# Limit, precision, and scale are all handled by the superclass.
|
197
|
-
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
|
198
|
-
oid =
|
199
|
-
|
218
|
+
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod, collation|
|
219
|
+
oid = oid.to_i
|
220
|
+
fmod = fmod.to_i
|
221
|
+
type_metadata = fetch_type_metadata(column_name, type, oid, fmod)
|
222
|
+
default_value = extract_value_from_default(default)
|
200
223
|
default_function = extract_default_function(default_value, default)
|
201
|
-
new_column(column_name, default_value,
|
224
|
+
new_column(column_name, default_value, type_metadata, !notnull, default_function, collation)
|
202
225
|
end
|
203
226
|
end
|
204
227
|
|
205
|
-
def new_column(name, default,
|
206
|
-
PostgreSQLColumn.new(name, default,
|
228
|
+
def new_column(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation = nil) # :nodoc:
|
229
|
+
PostgreSQLColumn.new(name, default, sql_type_metadata, null, default_function, collation)
|
207
230
|
end
|
208
231
|
|
209
232
|
# Returns the current database name.
|
210
233
|
def current_database
|
211
|
-
|
234
|
+
select_value('select current_database()', 'SCHEMA')
|
212
235
|
end
|
213
236
|
|
214
237
|
# Returns the current schema name.
|
215
238
|
def current_schema
|
216
|
-
|
239
|
+
select_value('SELECT current_schema', 'SCHEMA')
|
217
240
|
end
|
218
241
|
|
219
242
|
# Returns the current database encoding format.
|
220
243
|
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
|
244
|
+
select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
225
245
|
end
|
226
246
|
|
227
247
|
# Returns the current database collation.
|
228
248
|
def collation
|
229
|
-
|
230
|
-
SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
231
|
-
end_sql
|
249
|
+
select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
232
250
|
end
|
233
251
|
|
234
252
|
# Returns the current database ctype.
|
235
253
|
def ctype
|
236
|
-
|
237
|
-
SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
238
|
-
end_sql
|
254
|
+
select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
239
255
|
end
|
240
256
|
|
241
257
|
# Returns an array of schema names.
|
242
258
|
def schema_names
|
243
|
-
|
259
|
+
select_values(<<-SQL, 'SCHEMA')
|
244
260
|
SELECT nspname
|
245
261
|
FROM pg_namespace
|
246
262
|
WHERE nspname !~ '^pg_.*'
|
@@ -251,12 +267,12 @@ module ActiveRecord
|
|
251
267
|
|
252
268
|
# Creates a schema for the given schema name.
|
253
269
|
def create_schema schema_name
|
254
|
-
execute "CREATE SCHEMA #{schema_name}"
|
270
|
+
execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
|
255
271
|
end
|
256
272
|
|
257
273
|
# Drops the schema for the given schema name.
|
258
|
-
def drop_schema
|
259
|
-
execute "DROP SCHEMA #{schema_name} CASCADE"
|
274
|
+
def drop_schema(schema_name, options = {})
|
275
|
+
execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
|
260
276
|
end
|
261
277
|
|
262
278
|
# Sets the schema search path to a string of comma-separated schema names.
|
@@ -273,12 +289,12 @@ module ActiveRecord
|
|
273
289
|
|
274
290
|
# Returns the active schema search path.
|
275
291
|
def schema_search_path
|
276
|
-
@schema_search_path ||=
|
292
|
+
@schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
|
277
293
|
end
|
278
294
|
|
279
295
|
# Returns the current client message level.
|
280
296
|
def client_min_messages
|
281
|
-
|
297
|
+
select_value('SHOW client_min_messages', 'SCHEMA')
|
282
298
|
end
|
283
299
|
|
284
300
|
# Set the client message level.
|
@@ -296,10 +312,7 @@ module ActiveRecord
|
|
296
312
|
end
|
297
313
|
|
298
314
|
def serial_sequence(table, column)
|
299
|
-
|
300
|
-
SELECT pg_get_serial_sequence('#{table}', '#{column}')
|
301
|
-
eosql
|
302
|
-
result.rows.first.first
|
315
|
+
select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", 'SCHEMA')
|
303
316
|
end
|
304
317
|
|
305
318
|
# Sets the sequence of a table's primary key to the specified value.
|
@@ -310,9 +323,7 @@ module ActiveRecord
|
|
310
323
|
if sequence
|
311
324
|
quoted_sequence = quote_table_name(sequence)
|
312
325
|
|
313
|
-
select_value
|
314
|
-
SELECT setval('#{quoted_sequence}', #{value})
|
315
|
-
end_sql
|
326
|
+
select_value("SELECT setval('#{quoted_sequence}', #{value})", 'SCHEMA')
|
316
327
|
else
|
317
328
|
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
|
318
329
|
end
|
@@ -335,7 +346,7 @@ module ActiveRecord
|
|
335
346
|
if pk && sequence
|
336
347
|
quoted_sequence = quote_table_name(sequence)
|
337
348
|
|
338
|
-
select_value
|
349
|
+
select_value(<<-end_sql, 'SCHEMA')
|
339
350
|
SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
|
340
351
|
end_sql
|
341
352
|
end
|
@@ -395,17 +406,19 @@ module ActiveRecord
|
|
395
406
|
nil
|
396
407
|
end
|
397
408
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
+
def primary_keys(table_name) # :nodoc:
|
410
|
+
select_values(<<-SQL.strip_heredoc, 'SCHEMA')
|
411
|
+
WITH pk_constraint AS (
|
412
|
+
SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
|
413
|
+
WHERE contype = 'p'
|
414
|
+
AND conrelid = '#{quote_table_name(table_name)}'::regclass
|
415
|
+
), cons AS (
|
416
|
+
SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
|
417
|
+
)
|
418
|
+
SELECT attr.attname FROM pg_attribute attr
|
419
|
+
INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
|
420
|
+
ORDER BY cons.rownum
|
421
|
+
SQL
|
409
422
|
end
|
410
423
|
|
411
424
|
# Renames a table.
|
@@ -422,7 +435,7 @@ module ActiveRecord
|
|
422
435
|
new_seq = "#{new_name}_#{pk}_seq"
|
423
436
|
idx = "#{table_name}_pkey"
|
424
437
|
new_idx = "#{new_name}_pkey"
|
425
|
-
execute "ALTER TABLE #{
|
438
|
+
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
426
439
|
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
427
440
|
end
|
428
441
|
|
@@ -434,16 +447,20 @@ module ActiveRecord
|
|
434
447
|
super
|
435
448
|
end
|
436
449
|
|
437
|
-
|
438
|
-
def change_column(table_name, column_name, type, options = {})
|
450
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
439
451
|
clear_cache!
|
440
452
|
quoted_table_name = quote_table_name(table_name)
|
441
|
-
|
442
|
-
sql_type
|
443
|
-
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{
|
444
|
-
|
445
|
-
|
446
|
-
|
453
|
+
quoted_column_name = quote_column_name(column_name)
|
454
|
+
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale], options[:array])
|
455
|
+
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
|
456
|
+
if options[:collation]
|
457
|
+
sql << " COLLATE \"#{options[:collation]}\""
|
458
|
+
end
|
459
|
+
if options[:using]
|
460
|
+
sql << " USING #{options[:using]}"
|
461
|
+
elsif options[:cast_as]
|
462
|
+
cast_as_type = type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale], options[:array])
|
463
|
+
sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
447
464
|
end
|
448
465
|
execute sql
|
449
466
|
|
@@ -452,26 +469,27 @@ module ActiveRecord
|
|
452
469
|
end
|
453
470
|
|
454
471
|
# Changes the default value of a table column.
|
455
|
-
def change_column_default(table_name, column_name,
|
472
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
456
473
|
clear_cache!
|
457
474
|
column = column_for(table_name, column_name)
|
458
475
|
return unless column
|
459
476
|
|
477
|
+
default = extract_new_default_value(default_or_changes)
|
460
478
|
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
461
479
|
if default.nil?
|
462
480
|
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
463
481
|
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
464
482
|
execute alter_column_query % "DROP DEFAULT"
|
465
483
|
else
|
466
|
-
execute alter_column_query % "SET DEFAULT #{
|
484
|
+
execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
467
485
|
end
|
468
486
|
end
|
469
487
|
|
470
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
488
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
471
489
|
clear_cache!
|
472
490
|
unless null || default.nil?
|
473
491
|
column = column_for(table_name, column_name)
|
474
|
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{
|
492
|
+
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
493
|
end
|
476
494
|
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
477
495
|
end
|
@@ -488,10 +506,32 @@ module ActiveRecord
|
|
488
506
|
execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
|
489
507
|
end
|
490
508
|
|
491
|
-
def remove_index
|
492
|
-
|
509
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
510
|
+
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
511
|
+
|
512
|
+
if options.is_a?(Hash) && options.key?(:name)
|
513
|
+
provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
|
514
|
+
|
515
|
+
options[:name] = provided_index.identifier
|
516
|
+
table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
|
517
|
+
|
518
|
+
if provided_index.schema.present? && table.schema != provided_index.schema
|
519
|
+
raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, options))
|
524
|
+
algorithm =
|
525
|
+
if options.is_a?(Hash) && options.key?(:algorithm)
|
526
|
+
index_algorithms.fetch(options[:algorithm]) do
|
527
|
+
raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
|
528
|
+
end
|
529
|
+
end
|
530
|
+
execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
|
493
531
|
end
|
494
532
|
|
533
|
+
# Renames an index of a table. Raises error if length of new
|
534
|
+
# index name is greater than allowed limit.
|
495
535
|
def rename_index(table_name, old_name, new_name)
|
496
536
|
validate_index_length!(table_name, new_name)
|
497
537
|
|
@@ -540,41 +580,35 @@ module ActiveRecord
|
|
540
580
|
end
|
541
581
|
|
542
582
|
# 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
|
583
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil, array = nil)
|
584
|
+
sql = case type.to_s
|
545
585
|
when 'binary'
|
546
586
|
# PostgreSQL doesn't support limits on binary (bytea) columns.
|
547
|
-
# The hard limit is
|
587
|
+
# The hard limit is 1GB, because of a 32-bit size field, and TOAST.
|
548
588
|
case limit
|
549
589
|
when nil, 0..0x3fffffff; super(type)
|
550
590
|
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
|
551
591
|
end
|
552
592
|
when 'text'
|
553
593
|
# PostgreSQL doesn't support limits on text columns.
|
554
|
-
# The hard limit is
|
594
|
+
# The hard limit is 1GB, according to section 8.3 in the manual.
|
555
595
|
case limit
|
556
596
|
when nil, 0..0x3fffffff; super(type)
|
557
597
|
else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
|
558
598
|
end
|
559
599
|
when 'integer'
|
560
|
-
return 'integer' unless limit
|
561
|
-
|
562
600
|
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")
|
601
|
+
when 1, 2; 'smallint'
|
602
|
+
when nil, 3, 4; 'integer'
|
603
|
+
when 5..8; 'bigint'
|
604
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
574
605
|
end
|
575
606
|
else
|
576
|
-
super
|
607
|
+
super(type, limit, precision, scale)
|
577
608
|
end
|
609
|
+
|
610
|
+
sql << '[]' if array && type != :primary_key
|
611
|
+
sql
|
578
612
|
end
|
579
613
|
|
580
614
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
@@ -590,6 +624,18 @@ module ActiveRecord
|
|
590
624
|
|
591
625
|
[super, *order_columns].join(', ')
|
592
626
|
end
|
627
|
+
|
628
|
+
def fetch_type_metadata(column_name, sql_type, oid, fmod)
|
629
|
+
cast_type = get_oid_type(oid, fmod, column_name, sql_type)
|
630
|
+
simple_type = SqlTypeMetadata.new(
|
631
|
+
sql_type: sql_type,
|
632
|
+
type: cast_type.type,
|
633
|
+
limit: cast_type.limit,
|
634
|
+
precision: cast_type.precision,
|
635
|
+
scale: cast_type.scale,
|
636
|
+
)
|
637
|
+
PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
|
638
|
+
end
|
593
639
|
end
|
594
640
|
end
|
595
641
|
end
|