activerecord 4.2.6 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1307 -1105
- 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/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +11 -9
- data/lib/active_record/associations/association_scope.rb +73 -102
- 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 +14 -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 +3 -10
- data/lib/active_record/associations/collection_association.rb +50 -31
- data/lib/active_record/associations/collection_proxy.rb +69 -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 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +29 -19
- data/lib/active_record/associations/preloader/association.rb +46 -52
- 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 +27 -14
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/associations.rb +317 -209
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +68 -18
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- 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 +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 +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attributes.rb +199 -80
- data/lib/active_record/autosave_association.rb +49 -16
- 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 +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- 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 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -140
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -362
- 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 +125 -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 -176
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- 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 +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 +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 -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/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- 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 +234 -148
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
- 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 +148 -203
- 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 +89 -107
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +113 -76
- data/lib/active_record/errors.rb +87 -48
- 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 +76 -40
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- 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 +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +364 -109
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +121 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +27 -18
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +58 -45
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +282 -115
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +163 -81
- 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/array_handler.rb +11 -15
- 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/predicate_builder.rb +120 -107
- 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/relation.rb +176 -116
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -17
- data/lib/active_record/scoping/default.rb +23 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +58 -41
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -20
- 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 +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +29 -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.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- 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 +30 -29
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +7 -2
- 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 +8 -1
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- 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 -491
- 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 -50
- 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 -105
@@ -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,170 @@ 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)
|
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
|
+
# Returns a comment stored in database for given table
|
242
|
+
def table_comment(table_name) # :nodoc:
|
243
|
+
name = Utils.extract_schema_qualified_name(table_name.to_s)
|
244
|
+
if name.identifier
|
245
|
+
select_value(<<-SQL.strip_heredoc, 'SCHEMA')
|
246
|
+
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
|
247
|
+
FROM pg_catalog.pg_class c
|
248
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
249
|
+
WHERE c.relname = #{quote(name.identifier)}
|
250
|
+
AND c.relkind IN ('r') -- (r)elation/table
|
251
|
+
AND n.nspname = #{name.schema ? quote(name.schema) : 'ANY (current_schemas(false))'}
|
252
|
+
SQL
|
253
|
+
end
|
207
254
|
end
|
208
255
|
|
209
256
|
# Returns the current database name.
|
210
257
|
def current_database
|
211
|
-
|
258
|
+
select_value('select current_database()', 'SCHEMA')
|
212
259
|
end
|
213
260
|
|
214
261
|
# Returns the current schema name.
|
215
262
|
def current_schema
|
216
|
-
|
263
|
+
select_value('SELECT current_schema', 'SCHEMA')
|
217
264
|
end
|
218
265
|
|
219
266
|
# Returns the current database encoding format.
|
220
267
|
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
|
268
|
+
select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
225
269
|
end
|
226
270
|
|
227
271
|
# Returns the current database collation.
|
228
272
|
def collation
|
229
|
-
|
230
|
-
SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
231
|
-
end_sql
|
273
|
+
select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
232
274
|
end
|
233
275
|
|
234
276
|
# Returns the current database ctype.
|
235
277
|
def ctype
|
236
|
-
|
237
|
-
SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
238
|
-
end_sql
|
278
|
+
select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", 'SCHEMA')
|
239
279
|
end
|
240
280
|
|
241
281
|
# Returns an array of schema names.
|
242
282
|
def schema_names
|
243
|
-
|
283
|
+
select_values(<<-SQL, 'SCHEMA')
|
244
284
|
SELECT nspname
|
245
285
|
FROM pg_namespace
|
246
286
|
WHERE nspname !~ '^pg_.*'
|
@@ -251,12 +291,12 @@ module ActiveRecord
|
|
251
291
|
|
252
292
|
# Creates a schema for the given schema name.
|
253
293
|
def create_schema schema_name
|
254
|
-
execute "CREATE SCHEMA #{schema_name}"
|
294
|
+
execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
|
255
295
|
end
|
256
296
|
|
257
297
|
# Drops the schema for the given schema name.
|
258
|
-
def drop_schema
|
259
|
-
execute "DROP SCHEMA #{schema_name} CASCADE"
|
298
|
+
def drop_schema(schema_name, options = {})
|
299
|
+
execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
|
260
300
|
end
|
261
301
|
|
262
302
|
# Sets the schema search path to a string of comma-separated schema names.
|
@@ -273,12 +313,12 @@ module ActiveRecord
|
|
273
313
|
|
274
314
|
# Returns the active schema search path.
|
275
315
|
def schema_search_path
|
276
|
-
@schema_search_path ||=
|
316
|
+
@schema_search_path ||= select_value('SHOW search_path', 'SCHEMA')
|
277
317
|
end
|
278
318
|
|
279
319
|
# Returns the current client message level.
|
280
320
|
def client_min_messages
|
281
|
-
|
321
|
+
select_value('SHOW client_min_messages', 'SCHEMA')
|
282
322
|
end
|
283
323
|
|
284
324
|
# Set the client message level.
|
@@ -296,10 +336,7 @@ module ActiveRecord
|
|
296
336
|
end
|
297
337
|
|
298
338
|
def serial_sequence(table, column)
|
299
|
-
|
300
|
-
SELECT pg_get_serial_sequence('#{table}', '#{column}')
|
301
|
-
eosql
|
302
|
-
result.rows.first.first
|
339
|
+
select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", 'SCHEMA')
|
303
340
|
end
|
304
341
|
|
305
342
|
# Sets the sequence of a table's primary key to the specified value.
|
@@ -310,11 +347,9 @@ module ActiveRecord
|
|
310
347
|
if sequence
|
311
348
|
quoted_sequence = quote_table_name(sequence)
|
312
349
|
|
313
|
-
select_value
|
314
|
-
SELECT setval('#{quoted_sequence}', #{value})
|
315
|
-
end_sql
|
350
|
+
select_value("SELECT setval('#{quoted_sequence}', #{value})", 'SCHEMA')
|
316
351
|
else
|
317
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
|
352
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
|
318
353
|
end
|
319
354
|
end
|
320
355
|
end
|
@@ -329,13 +364,13 @@ module ActiveRecord
|
|
329
364
|
end
|
330
365
|
|
331
366
|
if @logger && pk && !sequence
|
332
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence"
|
367
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence."
|
333
368
|
end
|
334
369
|
|
335
370
|
if pk && sequence
|
336
371
|
quoted_sequence = quote_table_name(sequence)
|
337
372
|
|
338
|
-
select_value
|
373
|
+
select_value(<<-end_sql, 'SCHEMA')
|
339
374
|
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
375
|
end_sql
|
341
376
|
end
|
@@ -395,17 +430,19 @@ module ActiveRecord
|
|
395
430
|
nil
|
396
431
|
end
|
397
432
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
433
|
+
def primary_keys(table_name) # :nodoc:
|
434
|
+
select_values(<<-SQL.strip_heredoc, 'SCHEMA')
|
435
|
+
WITH pk_constraint AS (
|
436
|
+
SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
|
437
|
+
WHERE contype = 'p'
|
438
|
+
AND conrelid = '#{quote_table_name(table_name)}'::regclass
|
439
|
+
), cons AS (
|
440
|
+
SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
|
441
|
+
)
|
442
|
+
SELECT attr.attname FROM pg_attribute attr
|
443
|
+
INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
|
444
|
+
ORDER BY cons.rownum
|
445
|
+
SQL
|
409
446
|
end
|
410
447
|
|
411
448
|
# Renames a table.
|
@@ -422,7 +459,7 @@ module ActiveRecord
|
|
422
459
|
new_seq = "#{new_name}_#{pk}_seq"
|
423
460
|
idx = "#{table_name}_pkey"
|
424
461
|
new_idx = "#{new_name}_pkey"
|
425
|
-
execute "ALTER TABLE #{
|
462
|
+
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
426
463
|
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
427
464
|
end
|
428
465
|
|
@@ -432,50 +469,69 @@ module ActiveRecord
|
|
432
469
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
433
470
|
clear_cache!
|
434
471
|
super
|
472
|
+
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
435
473
|
end
|
436
474
|
|
437
|
-
|
438
|
-
def change_column(table_name, column_name, type, options = {})
|
475
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
439
476
|
clear_cache!
|
440
477
|
quoted_table_name = quote_table_name(table_name)
|
441
|
-
|
442
|
-
sql_type
|
443
|
-
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{
|
444
|
-
|
445
|
-
|
446
|
-
|
478
|
+
quoted_column_name = quote_column_name(column_name)
|
479
|
+
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale], options[:array])
|
480
|
+
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
|
481
|
+
if options[:collation]
|
482
|
+
sql << " COLLATE \"#{options[:collation]}\""
|
483
|
+
end
|
484
|
+
if options[:using]
|
485
|
+
sql << " USING #{options[:using]}"
|
486
|
+
elsif options[:cast_as]
|
487
|
+
cast_as_type = type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale], options[:array])
|
488
|
+
sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
447
489
|
end
|
448
490
|
execute sql
|
449
491
|
|
450
492
|
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
451
493
|
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
494
|
+
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
452
495
|
end
|
453
496
|
|
454
497
|
# Changes the default value of a table column.
|
455
|
-
def change_column_default(table_name, column_name,
|
498
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
456
499
|
clear_cache!
|
457
500
|
column = column_for(table_name, column_name)
|
458
501
|
return unless column
|
459
502
|
|
503
|
+
default = extract_new_default_value(default_or_changes)
|
460
504
|
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
461
505
|
if default.nil?
|
462
506
|
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
463
507
|
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
464
508
|
execute alter_column_query % "DROP DEFAULT"
|
465
509
|
else
|
466
|
-
execute alter_column_query % "SET DEFAULT #{
|
510
|
+
execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
467
511
|
end
|
468
512
|
end
|
469
513
|
|
470
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
514
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
471
515
|
clear_cache!
|
472
516
|
unless null || default.nil?
|
473
517
|
column = column_for(table_name, column_name)
|
474
|
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{
|
518
|
+
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
519
|
end
|
476
520
|
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
477
521
|
end
|
478
522
|
|
523
|
+
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
524
|
+
def change_column_comment(table_name, column_name, comment) # :nodoc:
|
525
|
+
clear_cache!
|
526
|
+
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
|
527
|
+
end
|
528
|
+
|
529
|
+
# Adds comment for given table or drops it if +comment+ is a +nil+
|
530
|
+
def change_table_comment(table_name, comment) # :nodoc:
|
531
|
+
clear_cache!
|
532
|
+
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
|
533
|
+
end
|
534
|
+
|
479
535
|
# Renames a column in a table.
|
480
536
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
481
537
|
clear_cache!
|
@@ -484,14 +540,38 @@ module ActiveRecord
|
|
484
540
|
end
|
485
541
|
|
486
542
|
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
|
543
|
+
index_name, index_type, index_columns, index_options, index_algorithm, index_using, comment = add_index_options(table_name, column_name, options)
|
544
|
+
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
|
545
|
+
execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
|
546
|
+
end
|
489
547
|
end
|
490
548
|
|
491
|
-
def remove_index
|
492
|
-
|
549
|
+
def remove_index(table_name, options = {}) #:nodoc:
|
550
|
+
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
551
|
+
|
552
|
+
if options.is_a?(Hash) && options.key?(:name)
|
553
|
+
provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
|
554
|
+
|
555
|
+
options[:name] = provided_index.identifier
|
556
|
+
table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
|
557
|
+
|
558
|
+
if provided_index.schema.present? && table.schema != provided_index.schema
|
559
|
+
raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, options))
|
564
|
+
algorithm =
|
565
|
+
if options.is_a?(Hash) && options.key?(:algorithm)
|
566
|
+
index_algorithms.fetch(options[:algorithm]) do
|
567
|
+
raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
|
568
|
+
end
|
569
|
+
end
|
570
|
+
execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
|
493
571
|
end
|
494
572
|
|
573
|
+
# Renames an index of a table. Raises error if length of new
|
574
|
+
# index name is greater than allowed limit.
|
495
575
|
def rename_index(table_name, old_name, new_name)
|
496
576
|
validate_index_length!(table_name, new_name)
|
497
577
|
|
@@ -540,41 +620,35 @@ module ActiveRecord
|
|
540
620
|
end
|
541
621
|
|
542
622
|
# 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
|
623
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil, array = nil)
|
624
|
+
sql = case type.to_s
|
545
625
|
when 'binary'
|
546
626
|
# PostgreSQL doesn't support limits on binary (bytea) columns.
|
547
|
-
# The hard limit is
|
627
|
+
# The hard limit is 1GB, because of a 32-bit size field, and TOAST.
|
548
628
|
case limit
|
549
629
|
when nil, 0..0x3fffffff; super(type)
|
550
630
|
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
|
551
631
|
end
|
552
632
|
when 'text'
|
553
633
|
# PostgreSQL doesn't support limits on text columns.
|
554
|
-
# The hard limit is
|
634
|
+
# The hard limit is 1GB, according to section 8.3 in the manual.
|
555
635
|
case limit
|
556
636
|
when nil, 0..0x3fffffff; super(type)
|
557
637
|
else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
|
558
638
|
end
|
559
639
|
when 'integer'
|
560
|
-
return 'integer' unless limit
|
561
|
-
|
562
640
|
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")
|
641
|
+
when 1, 2; 'smallint'
|
642
|
+
when nil, 3, 4; 'integer'
|
643
|
+
when 5..8; 'bigint'
|
644
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
|
574
645
|
end
|
575
646
|
else
|
576
|
-
super
|
647
|
+
super(type, limit, precision, scale)
|
577
648
|
end
|
649
|
+
|
650
|
+
sql << '[]' if array && type != :primary_key
|
651
|
+
sql
|
578
652
|
end
|
579
653
|
|
580
654
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
@@ -590,6 +664,18 @@ module ActiveRecord
|
|
590
664
|
|
591
665
|
[super, *order_columns].join(', ')
|
592
666
|
end
|
667
|
+
|
668
|
+
def fetch_type_metadata(column_name, sql_type, oid, fmod)
|
669
|
+
cast_type = get_oid_type(oid, fmod, column_name, sql_type)
|
670
|
+
simple_type = SqlTypeMetadata.new(
|
671
|
+
sql_type: sql_type,
|
672
|
+
type: cast_type.type,
|
673
|
+
limit: cast_type.limit,
|
674
|
+
precision: cast_type.precision,
|
675
|
+
scale: cast_type.scale,
|
676
|
+
)
|
677
|
+
PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod)
|
678
|
+
end
|
593
679
|
end
|
594
680
|
end
|
595
681
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata)
|
4
|
+
attr_reader :oid, :fmod, :array
|
5
|
+
|
6
|
+
def initialize(type_metadata, oid: nil, fmod: nil)
|
7
|
+
super(type_metadata)
|
8
|
+
@type_metadata = type_metadata
|
9
|
+
@oid = oid
|
10
|
+
@fmod = fmod
|
11
|
+
@array = /\[\]$/ === type_metadata.sql_type
|
12
|
+
end
|
13
|
+
|
14
|
+
def sql_type
|
15
|
+
super.gsub(/\[\]$/, "".freeze)
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
other.is_a?(PostgreSQLTypeMetadata) &&
|
20
|
+
attributes_for_hash == other.attributes_for_hash
|
21
|
+
end
|
22
|
+
alias eql? ==
|
23
|
+
|
24
|
+
def hash
|
25
|
+
attributes_for_hash.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def attributes_for_hash
|
31
|
+
[self.class, @type_metadata, oid, fmod]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|