activerecord 5.0.7.2 → 6.1.1
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 +829 -2015
- data/MIT-LICENSE +3 -1
- data/README.rdoc +11 -9
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record.rb +37 -29
- data/lib/active_record/aggregations.rb +249 -247
- data/lib/active_record/association_relation.rb +30 -18
- data/lib/active_record/associations.rb +1714 -1596
- data/lib/active_record/associations/alias_tracker.rb +36 -42
- data/lib/active_record/associations/association.rb +143 -68
- data/lib/active_record/associations/association_scope.rb +98 -94
- data/lib/active_record/associations/belongs_to_association.rb +76 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +27 -28
- data/lib/active_record/associations/builder/belongs_to.rb +52 -60
- data/lib/active_record/associations/builder/collection_association.rb +12 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +40 -62
- data/lib/active_record/associations/builder/has_many.rb +10 -2
- data/lib/active_record/associations/builder/has_one.rb +35 -2
- data/lib/active_record/associations/builder/singular_association.rb +5 -1
- data/lib/active_record/associations/collection_association.rb +104 -259
- data/lib/active_record/associations/collection_proxy.rb +169 -125
- data/lib/active_record/associations/foreign_association.rb +22 -0
- data/lib/active_record/associations/has_many_association.rb +46 -31
- data/lib/active_record/associations/has_many_through_association.rb +66 -46
- data/lib/active_record/associations/has_one_association.rb +71 -52
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency.rb +169 -180
- data/lib/active_record/associations/join_dependency/join_association.rb +53 -79
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/preloader.rb +97 -104
- data/lib/active_record/associations/preloader/association.rb +109 -97
- data/lib/active_record/associations/preloader/through_association.rb +77 -76
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +27 -15
- data/lib/active_record/attribute_assignment.rb +55 -60
- data/lib/active_record/attribute_methods.rb +111 -141
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -9
- data/lib/active_record/attribute_methods/dirty.rb +172 -112
- data/lib/active_record/attribute_methods/primary_key.rb +88 -91
- data/lib/active_record/attribute_methods/query.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +18 -50
- data/lib/active_record/attribute_methods/serialization.rb +38 -10
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -66
- data/lib/active_record/attribute_methods/write.rb +25 -32
- data/lib/active_record/attributes.rb +69 -31
- data/lib/active_record/autosave_association.rb +102 -66
- data/lib/active_record/base.rb +16 -25
- data/lib/active_record/callbacks.rb +202 -43
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -12
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +661 -375
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +14 -38
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +269 -105
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +54 -35
- data/lib/active_record/connection_adapters/abstract/quoting.rb +137 -93
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +155 -113
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -162
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +591 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +229 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +392 -244
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +457 -582
- data/lib/active_record/connection_adapters/column.rb +55 -13
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +135 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +79 -49
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +66 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +20 -12
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +74 -37
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +39 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +70 -101
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +26 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +19 -18
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +30 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +98 -38
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +21 -27
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +34 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +426 -324
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +32 -23
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +418 -293
- data/lib/active_record/connection_adapters/schema_cache.rb +135 -18
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +22 -7
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +72 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +282 -290
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -8
- data/lib/active_record/connection_handling.rb +287 -45
- data/lib/active_record/core.rb +385 -181
- data/lib/active_record/counter_cache.rb +60 -28
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -87
- data/lib/active_record/enum.rb +122 -47
- data/lib/active_record/errors.rb +153 -22
- data/lib/active_record/explain.rb +13 -8
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +20 -22
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +246 -507
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +168 -95
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +114 -25
- data/lib/active_record/internal_metadata.rb +30 -24
- data/lib/active_record/legacy_yaml_adapter.rb +11 -5
- data/lib/active_record/locking/optimistic.rb +81 -85
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +68 -31
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/migration.rb +439 -342
- data/lib/active_record/migration/command_recorder.rb +152 -98
- data/lib/active_record/migration/compatibility.rb +229 -60
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/model_schema.rb +230 -122
- data/lib/active_record/nested_attributes.rb +213 -203
- data/lib/active_record/no_touching.rb +11 -2
- data/lib/active_record/null_relation.rb +12 -34
- data/lib/active_record/persistence.rb +471 -97
- data/lib/active_record/query_cache.rb +23 -12
- data/lib/active_record/querying.rb +43 -25
- data/lib/active_record/railtie.rb +155 -43
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +507 -195
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +245 -269
- data/lib/active_record/relation.rb +475 -324
- data/lib/active_record/relation/batches.rb +125 -72
- data/lib/active_record/relation/batches/batch_enumerator.rb +28 -10
- data/lib/active_record/relation/calculations.rb +267 -171
- data/lib/active_record/relation/delegation.rb +73 -69
- data/lib/active_record/relation/finder_methods.rb +238 -248
- data/lib/active_record/relation/from_clause.rb +7 -9
- data/lib/active_record/relation/merger.rb +95 -77
- data/lib/active_record/relation/predicate_builder.rb +109 -110
- data/lib/active_record/relation/predicate_builder/array_handler.rb +22 -17
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +55 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +654 -374
- data/lib/active_record/relation/record_fetch_warning.rb +8 -6
- data/lib/active_record/relation/spawn_methods.rb +15 -14
- data/lib/active_record/relation/where_clause.rb +171 -109
- data/lib/active_record/result.rb +88 -51
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +73 -100
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +101 -69
- data/lib/active_record/schema_migration.rb +16 -12
- data/lib/active_record/scoping.rb +20 -20
- data/lib/active_record/scoping/default.rb +92 -95
- data/lib/active_record/scoping/named.rb +39 -30
- data/lib/active_record/secure_token.rb +19 -9
- data/lib/active_record/serialization.rb +7 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +80 -29
- data/lib/active_record/store.rb +122 -42
- data/lib/active_record/suppressor.rb +6 -3
- data/lib/active_record/table_metadata.rb +51 -39
- data/lib/active_record/tasks/database_tasks.rb +332 -115
- data/lib/active_record/tasks/mysql_database_tasks.rb +66 -104
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -56
- data/lib/active_record/tasks/sqlite_database_tasks.rb +40 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +26 -24
- data/lib/active_record/transactions.rb +121 -184
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type.rb +29 -17
- data/lib/active_record/type/adapter_specific_registry.rb +44 -48
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +20 -9
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +12 -1
- data/lib/active_record/type/type_map.rb +14 -17
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/type_caster/connection.rb +17 -13
- data/lib/active_record/type_caster/map.rb +10 -6
- data/lib/active_record/validations.rb +8 -5
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +52 -45
- data/lib/active_record/version.rb +3 -1
- data/lib/arel.rb +54 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record.rb +7 -5
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +22 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +38 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +3 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +7 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +41 -25
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- metadata +141 -57
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute_decorators.rb +0 -67
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/attribute_set/builder.rb +0 -132
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -263
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -22
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -17
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/relation/where_clause_factory.rb +0 -38
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,46 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
|
7
|
+
private
|
8
|
+
def extensions(stream)
|
9
|
+
extensions = @connection.extensions
|
10
|
+
if extensions.any?
|
11
|
+
stream.puts " # These are extensions that must be enabled in order to support this database"
|
12
|
+
extensions.sort.each do |extension|
|
13
|
+
stream.puts " enable_extension #{extension.inspect}"
|
14
|
+
end
|
15
|
+
stream.puts
|
16
|
+
end
|
9
17
|
end
|
10
|
-
spec
|
11
|
-
end
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
# Adds +:array+ as a valid migration key
|
21
|
-
def migration_keys
|
22
|
-
super + [:array]
|
23
|
-
end
|
19
|
+
def prepare_column_options(column)
|
20
|
+
spec = super
|
21
|
+
spec[:array] = "true" if column.array?
|
22
|
+
spec
|
23
|
+
end
|
24
24
|
|
25
|
-
|
25
|
+
def default_primary_key?(column)
|
26
|
+
schema_type(column) == :bigserial
|
27
|
+
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
29
|
+
def explicit_primary_key_default?(column)
|
30
|
+
column.type == :uuid || (column.type == :integer && !column.serial?)
|
31
|
+
end
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
+
def schema_type(column)
|
34
|
+
return super unless column.serial?
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
if column.bigint?
|
37
|
+
:bigserial
|
38
|
+
else
|
39
|
+
:serial
|
40
|
+
end
|
38
41
|
end
|
39
|
-
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
def schema_expression(column)
|
44
|
+
super unless column.serial?
|
45
|
+
end
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
@@ -1,24 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
|
-
class SchemaCreation < AbstractAdapter::SchemaCreation
|
7
|
-
private
|
8
|
-
|
9
|
-
def visit_ColumnDefinition(o)
|
10
|
-
o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
def add_column_options!(sql, options)
|
15
|
-
if options[:collation]
|
16
|
-
sql << " COLLATE \"#{options[:collation]}\""
|
17
|
-
end
|
18
|
-
super
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
6
|
module SchemaStatements
|
23
7
|
# Drops the database specified on the +name+ attribute
|
24
8
|
# and creates it again using the provided +options+.
|
@@ -36,26 +20,26 @@ module ActiveRecord
|
|
36
20
|
# create_database config[:database], config
|
37
21
|
# create_database 'foo_development', encoding: 'unicode'
|
38
22
|
def create_database(name, options = {})
|
39
|
-
options = { encoding:
|
40
|
-
|
41
|
-
option_string = options.
|
42
|
-
memo
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
23
|
+
options = { encoding: "utf8" }.merge!(options.symbolize_keys)
|
24
|
+
|
25
|
+
option_string = options.each_with_object(+"") do |(key, value), memo|
|
26
|
+
memo << case key
|
27
|
+
when :owner
|
28
|
+
" OWNER = \"#{value}\""
|
29
|
+
when :template
|
30
|
+
" TEMPLATE = \"#{value}\""
|
31
|
+
when :encoding
|
32
|
+
" ENCODING = '#{value}'"
|
33
|
+
when :collation
|
34
|
+
" LC_COLLATE = '#{value}'"
|
35
|
+
when :ctype
|
36
|
+
" LC_CTYPE = '#{value}'"
|
37
|
+
when :tablespace
|
38
|
+
" TABLESPACE = \"#{value}\""
|
39
|
+
when :connection_limit
|
40
|
+
" CONNECTION LIMIT = #{value}"
|
41
|
+
else
|
42
|
+
""
|
59
43
|
end
|
60
44
|
end
|
61
45
|
|
@@ -70,123 +54,49 @@ module ActiveRecord
|
|
70
54
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
71
55
|
end
|
72
56
|
|
73
|
-
|
74
|
-
|
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')
|
82
|
-
end
|
83
|
-
|
84
|
-
def data_sources # :nodoc
|
85
|
-
select_values(<<-SQL, 'SCHEMA')
|
86
|
-
SELECT c.relname
|
87
|
-
FROM pg_class c
|
88
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
89
|
-
WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
90
|
-
AND n.nspname = ANY (current_schemas(false))
|
91
|
-
SQL
|
92
|
-
end
|
93
|
-
|
94
|
-
# Returns true if table exists.
|
95
|
-
# If the schema is not specified as part of +name+ then it will only find tables within
|
96
|
-
# the current schema search path (regardless of permissions to access tables in other schemas)
|
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)
|
108
|
-
name = Utils.extract_schema_qualified_name(name.to_s)
|
109
|
-
return false unless name.identifier
|
110
|
-
|
111
|
-
select_value(<<-SQL, 'SCHEMA').to_i > 0
|
112
|
-
SELECT COUNT(*)
|
113
|
-
FROM pg_class c
|
114
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
115
|
-
WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
116
|
-
AND c.relname = '#{name.identifier}'
|
117
|
-
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
118
|
-
SQL
|
119
|
-
end
|
120
|
-
|
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:
|
57
|
+
def drop_table(table_name, **options) # :nodoc:
|
58
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
146
59
|
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
147
60
|
end
|
148
61
|
|
149
62
|
# Returns true if schema exists.
|
150
63
|
def schema_exists?(name)
|
151
|
-
|
64
|
+
query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
|
152
65
|
end
|
153
66
|
|
154
67
|
# Verifies existence of an index with a given name.
|
155
|
-
def index_name_exists?(table_name, index_name
|
156
|
-
table =
|
157
|
-
index =
|
68
|
+
def index_name_exists?(table_name, index_name)
|
69
|
+
table = quoted_scope(table_name)
|
70
|
+
index = quoted_scope(index_name)
|
158
71
|
|
159
|
-
|
72
|
+
query_value(<<~SQL, "SCHEMA").to_i > 0
|
160
73
|
SELECT COUNT(*)
|
161
74
|
FROM pg_class t
|
162
75
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
163
76
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
164
77
|
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
165
|
-
WHERE i.relkind
|
166
|
-
AND i.relname =
|
167
|
-
AND t.relname =
|
168
|
-
AND n.nspname = #{index
|
78
|
+
WHERE i.relkind IN ('i', 'I')
|
79
|
+
AND i.relname = #{index[:name]}
|
80
|
+
AND t.relname = #{table[:name]}
|
81
|
+
AND n.nspname = #{index[:schema]}
|
169
82
|
SQL
|
170
83
|
end
|
171
84
|
|
172
85
|
# Returns an array of indexes for the given table.
|
173
|
-
def indexes(table_name
|
174
|
-
|
86
|
+
def indexes(table_name) # :nodoc:
|
87
|
+
scope = quoted_scope(table_name)
|
175
88
|
|
176
|
-
result = query(
|
89
|
+
result = query(<<~SQL, "SCHEMA")
|
177
90
|
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')
|
91
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
182
92
|
FROM pg_class t
|
183
93
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
184
94
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
185
95
|
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
186
|
-
WHERE i.relkind
|
96
|
+
WHERE i.relkind IN ('i', 'I')
|
187
97
|
AND d.indisprimary = 'f'
|
188
|
-
AND t.relname =
|
189
|
-
AND n.nspname = #{
|
98
|
+
AND t.relname = #{scope[:name]}
|
99
|
+
AND n.nspname = #{scope[:schema]}
|
190
100
|
ORDER BY i.relname
|
191
101
|
SQL
|
192
102
|
|
@@ -197,47 +107,48 @@ module ActiveRecord
|
|
197
107
|
inddef = row[3]
|
198
108
|
oid = row[4]
|
199
109
|
comment = row[5]
|
200
|
-
opclass = row[6]
|
201
110
|
|
202
|
-
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
|
111
|
+
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
|
112
|
+
|
113
|
+
orders = {}
|
114
|
+
opclasses = {}
|
203
115
|
|
204
|
-
if indkey.include?(0)
|
116
|
+
if indkey.include?(0)
|
205
117
|
columns = expressions
|
206
118
|
else
|
207
|
-
columns = Hash[query(
|
119
|
+
columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
|
208
120
|
SELECT a.attnum, a.attname
|
209
121
|
FROM pg_attribute a
|
210
122
|
WHERE a.attrelid = #{oid}
|
211
123
|
AND a.attnum IN (#{indkey.join(",")})
|
212
124
|
SQL
|
213
125
|
|
214
|
-
# add info on sort order
|
215
|
-
|
216
|
-
|
217
|
-
|
126
|
+
# add info on sort order (only desc order is explicitly specified, asc is the default)
|
127
|
+
# and non-default opclasses
|
128
|
+
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
|
129
|
+
opclasses[column] = opclass.to_sym if opclass
|
130
|
+
if nulls
|
131
|
+
orders[column] = [desc, nulls].compact.join(" ")
|
132
|
+
else
|
133
|
+
orders[column] = :desc if desc
|
134
|
+
end
|
135
|
+
end
|
218
136
|
end
|
219
137
|
|
220
|
-
IndexDefinition.new(
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
default_value = extract_value_from_default(default)
|
232
|
-
default_function = extract_default_function(default_value, default)
|
233
|
-
new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation, comment: comment.presence, max_identifier_length: max_identifier_length)
|
138
|
+
IndexDefinition.new(
|
139
|
+
table_name,
|
140
|
+
index_name,
|
141
|
+
unique,
|
142
|
+
columns,
|
143
|
+
orders: orders,
|
144
|
+
opclasses: opclasses,
|
145
|
+
where: where,
|
146
|
+
using: using.to_sym,
|
147
|
+
comment: comment.presence
|
148
|
+
)
|
234
149
|
end
|
235
150
|
end
|
236
151
|
|
237
|
-
def new_column(*args) # :nodoc:
|
238
|
-
PostgreSQLColumn.new(*args)
|
239
|
-
end
|
240
|
-
|
241
152
|
def table_options(table_name) # :nodoc:
|
242
153
|
if comment = table_comment(table_name)
|
243
154
|
{ comment: comment }
|
@@ -246,47 +157,47 @@ module ActiveRecord
|
|
246
157
|
|
247
158
|
# Returns a comment stored in database for given table
|
248
159
|
def table_comment(table_name) # :nodoc:
|
249
|
-
|
250
|
-
if name
|
251
|
-
|
160
|
+
scope = quoted_scope(table_name, type: "BASE TABLE")
|
161
|
+
if scope[:name]
|
162
|
+
query_value(<<~SQL, "SCHEMA")
|
252
163
|
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
|
253
164
|
FROM pg_catalog.pg_class c
|
254
165
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
255
|
-
WHERE c.relname = #{
|
256
|
-
AND c.relkind IN (
|
257
|
-
AND n.nspname = #{
|
166
|
+
WHERE c.relname = #{scope[:name]}
|
167
|
+
AND c.relkind IN (#{scope[:type]})
|
168
|
+
AND n.nspname = #{scope[:schema]}
|
258
169
|
SQL
|
259
170
|
end
|
260
171
|
end
|
261
172
|
|
262
173
|
# Returns the current database name.
|
263
174
|
def current_database
|
264
|
-
|
175
|
+
query_value("SELECT current_database()", "SCHEMA")
|
265
176
|
end
|
266
177
|
|
267
178
|
# Returns the current schema name.
|
268
179
|
def current_schema
|
269
|
-
|
180
|
+
query_value("SELECT current_schema", "SCHEMA")
|
270
181
|
end
|
271
182
|
|
272
183
|
# Returns the current database encoding format.
|
273
184
|
def encoding
|
274
|
-
|
185
|
+
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
275
186
|
end
|
276
187
|
|
277
188
|
# Returns the current database collation.
|
278
189
|
def collation
|
279
|
-
|
190
|
+
query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
280
191
|
end
|
281
192
|
|
282
193
|
# Returns the current database ctype.
|
283
194
|
def ctype
|
284
|
-
|
195
|
+
query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
285
196
|
end
|
286
197
|
|
287
198
|
# Returns an array of schema names.
|
288
199
|
def schema_names
|
289
|
-
|
200
|
+
query_values(<<~SQL, "SCHEMA")
|
290
201
|
SELECT nspname
|
291
202
|
FROM pg_namespace
|
292
203
|
WHERE nspname !~ '^pg_.*'
|
@@ -296,53 +207,53 @@ module ActiveRecord
|
|
296
207
|
end
|
297
208
|
|
298
209
|
# Creates a schema for the given schema name.
|
299
|
-
def create_schema
|
210
|
+
def create_schema(schema_name)
|
300
211
|
execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
|
301
212
|
end
|
302
213
|
|
303
214
|
# Drops the schema for the given schema name.
|
304
|
-
def drop_schema(schema_name, options
|
215
|
+
def drop_schema(schema_name, **options)
|
305
216
|
execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
|
306
217
|
end
|
307
218
|
|
308
219
|
# Sets the schema search path to a string of comma-separated schema names.
|
309
220
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
310
|
-
# See:
|
221
|
+
# See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
|
311
222
|
#
|
312
223
|
# This should be not be called manually but set in database.yml.
|
313
224
|
def schema_search_path=(schema_csv)
|
314
225
|
if schema_csv
|
315
|
-
execute("SET search_path TO #{schema_csv}",
|
226
|
+
execute("SET search_path TO #{schema_csv}", "SCHEMA")
|
316
227
|
@schema_search_path = schema_csv
|
317
228
|
end
|
318
229
|
end
|
319
230
|
|
320
231
|
# Returns the active schema search path.
|
321
232
|
def schema_search_path
|
322
|
-
@schema_search_path ||=
|
233
|
+
@schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
|
323
234
|
end
|
324
235
|
|
325
236
|
# Returns the current client message level.
|
326
237
|
def client_min_messages
|
327
|
-
|
238
|
+
query_value("SHOW client_min_messages", "SCHEMA")
|
328
239
|
end
|
329
240
|
|
330
241
|
# Set the client message level.
|
331
242
|
def client_min_messages=(level)
|
332
|
-
execute("SET client_min_messages TO '#{level}'",
|
243
|
+
execute("SET client_min_messages TO '#{level}'", "SCHEMA")
|
333
244
|
end
|
334
245
|
|
335
246
|
# Returns the sequence name for a table's primary key or some other specified key.
|
336
|
-
def default_sequence_name(table_name, pk =
|
337
|
-
result = serial_sequence(table_name, pk
|
247
|
+
def default_sequence_name(table_name, pk = "id") #:nodoc:
|
248
|
+
result = serial_sequence(table_name, pk)
|
338
249
|
return nil unless result
|
339
250
|
Utils.extract_schema_qualified_name(result).to_s
|
340
251
|
rescue ActiveRecord::StatementInvalid
|
341
|
-
PostgreSQL::Name.new(nil, "#{table_name}_#{pk
|
252
|
+
PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
|
342
253
|
end
|
343
254
|
|
344
255
|
def serial_sequence(table, column)
|
345
|
-
|
256
|
+
query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
|
346
257
|
end
|
347
258
|
|
348
259
|
# Sets the sequence of a table's primary key to the specified value.
|
@@ -353,7 +264,7 @@ module ActiveRecord
|
|
353
264
|
if sequence
|
354
265
|
quoted_sequence = quote_table_name(sequence)
|
355
266
|
|
356
|
-
|
267
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
|
357
268
|
else
|
358
269
|
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
|
359
270
|
end
|
@@ -362,7 +273,7 @@ module ActiveRecord
|
|
362
273
|
|
363
274
|
# Resets the sequence of a table's primary key to the maximum value.
|
364
275
|
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
|
365
|
-
unless pk
|
276
|
+
unless pk && sequence
|
366
277
|
default_pk, default_sequence = pk_and_sequence_for(table)
|
367
278
|
|
368
279
|
pk ||= default_pk
|
@@ -375,18 +286,16 @@ module ActiveRecord
|
|
375
286
|
|
376
287
|
if pk && sequence
|
377
288
|
quoted_sequence = quote_table_name(sequence)
|
378
|
-
max_pk =
|
289
|
+
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
|
379
290
|
if max_pk.nil?
|
380
|
-
if
|
381
|
-
minvalue =
|
291
|
+
if database_version >= 100000
|
292
|
+
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
|
382
293
|
else
|
383
|
-
minvalue =
|
294
|
+
minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
|
384
295
|
end
|
385
296
|
end
|
386
297
|
|
387
|
-
|
388
|
-
SELECT setval('#{quoted_sequence}', #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})
|
389
|
-
end_sql
|
298
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
390
299
|
end
|
391
300
|
end
|
392
301
|
|
@@ -394,7 +303,7 @@ module ActiveRecord
|
|
394
303
|
def pk_and_sequence_for(table) #:nodoc:
|
395
304
|
# First try looking for a sequence with a dependency on the
|
396
305
|
# given table's primary key.
|
397
|
-
result = query(
|
306
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
398
307
|
SELECT attr.attname, nsp.nspname, seq.relname
|
399
308
|
FROM pg_class seq,
|
400
309
|
pg_attribute attr,
|
@@ -410,11 +319,11 @@ module ActiveRecord
|
|
410
319
|
AND seq.relnamespace = nsp.oid
|
411
320
|
AND cons.contype = 'p'
|
412
321
|
AND dep.classid = 'pg_class'::regclass
|
413
|
-
AND dep.refobjid =
|
414
|
-
|
322
|
+
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
|
323
|
+
SQL
|
415
324
|
|
416
|
-
if result.nil?
|
417
|
-
result = query(
|
325
|
+
if result.nil? || result.empty?
|
326
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
418
327
|
SELECT attr.attname, nsp.nspname,
|
419
328
|
CASE
|
420
329
|
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
@@ -428,10 +337,10 @@ module ActiveRecord
|
|
428
337
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
429
338
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
430
339
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
431
|
-
WHERE t.oid =
|
340
|
+
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
432
341
|
AND cons.contype = 'p'
|
433
342
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
434
|
-
|
343
|
+
SQL
|
435
344
|
end
|
436
345
|
|
437
346
|
pk = result.shift
|
@@ -445,17 +354,18 @@ module ActiveRecord
|
|
445
354
|
end
|
446
355
|
|
447
356
|
def primary_keys(table_name) # :nodoc:
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
357
|
+
query_values(<<~SQL, "SCHEMA")
|
358
|
+
SELECT a.attname
|
359
|
+
FROM (
|
360
|
+
SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
|
361
|
+
FROM pg_index
|
362
|
+
WHERE indrelid = #{quote(quote_table_name(table_name))}::regclass
|
363
|
+
AND indisprimary
|
364
|
+
) i
|
365
|
+
JOIN pg_attribute a
|
366
|
+
ON a.attrelid = i.indrelid
|
367
|
+
AND a.attnum = i.indkey[i.idx]
|
368
|
+
ORDER BY i.idx
|
459
369
|
SQL
|
460
370
|
end
|
461
371
|
|
@@ -467,103 +377,84 @@ module ActiveRecord
|
|
467
377
|
# rename_table('octopuses', 'octopi')
|
468
378
|
def rename_table(table_name, new_name)
|
469
379
|
clear_cache!
|
380
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
381
|
+
schema_cache.clear_data_source_cache!(new_name.to_s)
|
470
382
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
471
383
|
pk, seq = pk_and_sequence_for(new_name)
|
472
|
-
if
|
473
|
-
new_seq = "#{new_name}_#{pk}_seq"
|
384
|
+
if pk
|
474
385
|
idx = "#{table_name}_pkey"
|
475
386
|
new_idx = "#{new_name}_pkey"
|
476
|
-
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
477
387
|
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
388
|
+
if seq && seq.identifier == "#{table_name}_#{pk}_seq"
|
389
|
+
new_seq = "#{new_name}_#{pk}_seq"
|
390
|
+
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
391
|
+
end
|
478
392
|
end
|
479
|
-
|
480
393
|
rename_table_indexes(table_name, new_name)
|
481
394
|
end
|
482
395
|
|
483
|
-
def add_column(table_name, column_name, type, options
|
396
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
484
397
|
clear_cache!
|
485
398
|
super
|
486
399
|
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
487
400
|
end
|
488
401
|
|
489
|
-
def change_column(table_name, column_name, type, options
|
402
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
490
403
|
clear_cache!
|
491
|
-
|
492
|
-
|
493
|
-
|
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})"
|
503
|
-
end
|
504
|
-
execute sql
|
505
|
-
|
506
|
-
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
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)
|
404
|
+
sqls, procs = Array(change_column_for_alter(table_name, column_name, type, **options)).partition { |v| v.is_a?(String) }
|
405
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{sqls.join(", ")}"
|
406
|
+
procs.each(&:call)
|
509
407
|
end
|
510
408
|
|
511
409
|
# Changes the default value of a table column.
|
512
410
|
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
513
|
-
|
514
|
-
column = column_for(table_name, column_name)
|
515
|
-
return unless column
|
516
|
-
|
517
|
-
default = extract_new_default_value(default_or_changes)
|
518
|
-
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
519
|
-
if default.nil?
|
520
|
-
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
521
|
-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
522
|
-
execute alter_column_query % "DROP DEFAULT"
|
523
|
-
else
|
524
|
-
execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
525
|
-
end
|
411
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
526
412
|
end
|
527
413
|
|
528
414
|
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
529
415
|
clear_cache!
|
530
416
|
unless null || default.nil?
|
531
417
|
column = column_for(table_name, column_name)
|
532
|
-
execute
|
418
|
+
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
|
533
419
|
end
|
534
|
-
execute
|
420
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
|
535
421
|
end
|
536
422
|
|
537
423
|
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
538
|
-
def change_column_comment(table_name, column_name,
|
424
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
539
425
|
clear_cache!
|
426
|
+
comment = extract_new_comment_value(comment_or_changes)
|
540
427
|
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
|
541
428
|
end
|
542
429
|
|
543
430
|
# Adds comment for given table or drops it if +comment+ is a +nil+
|
544
|
-
def change_table_comment(table_name,
|
431
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
545
432
|
clear_cache!
|
433
|
+
comment = extract_new_comment_value(comment_or_changes)
|
546
434
|
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
|
547
435
|
end
|
548
436
|
|
549
437
|
# Renames a column in a table.
|
550
438
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
551
439
|
clear_cache!
|
552
|
-
execute
|
440
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
553
441
|
rename_column_indexes(table_name, column_name, new_column_name)
|
554
442
|
end
|
555
443
|
|
556
|
-
def add_index(table_name, column_name, options
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
444
|
+
def add_index(table_name, column_name, **options) #:nodoc:
|
445
|
+
index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
|
446
|
+
|
447
|
+
create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
|
448
|
+
result = execute schema_creation.accept(create_index)
|
449
|
+
|
450
|
+
execute "COMMENT ON INDEX #{quote_column_name(index.name)} IS #{quote(index.comment)}" if index.comment
|
451
|
+
result
|
561
452
|
end
|
562
453
|
|
563
|
-
def remove_index(table_name,
|
454
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
564
455
|
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
565
456
|
|
566
|
-
if options.
|
457
|
+
if options.key?(:name)
|
567
458
|
provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
|
568
459
|
|
569
460
|
options[:name] = provided_index.identifier
|
@@ -574,14 +465,11 @@ module ActiveRecord
|
|
574
465
|
end
|
575
466
|
end
|
576
467
|
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
end
|
583
|
-
end
|
584
|
-
execute "DROP INDEX #{algorithm} #{quote_table_name(index_to_remove)}"
|
468
|
+
return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
|
469
|
+
|
470
|
+
index_to_remove = PostgreSQL::Name.new(table.schema, index_name_for_remove(table.to_s, column_name, options))
|
471
|
+
|
472
|
+
execute "DROP INDEX #{index_algorithm(options[:algorithm])} #{quote_table_name(index_to_remove)}"
|
585
473
|
end
|
586
474
|
|
587
475
|
# Renames an index of a table. Raises error if length of new
|
@@ -593,8 +481,9 @@ module ActiveRecord
|
|
593
481
|
end
|
594
482
|
|
595
483
|
def foreign_keys(table_name)
|
596
|
-
|
597
|
-
|
484
|
+
scope = quoted_scope(table_name)
|
485
|
+
fk_info = exec_query(<<~SQL, "SCHEMA")
|
486
|
+
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, c.convalidated AS valid
|
598
487
|
FROM pg_constraint c
|
599
488
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
600
489
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
@@ -602,90 +491,303 @@ module ActiveRecord
|
|
602
491
|
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
603
492
|
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
604
493
|
WHERE c.contype = 'f'
|
605
|
-
AND t1.relname = #{
|
606
|
-
AND t3.nspname =
|
494
|
+
AND t1.relname = #{scope[:name]}
|
495
|
+
AND t3.nspname = #{scope[:schema]}
|
607
496
|
ORDER BY c.conname
|
608
497
|
SQL
|
609
498
|
|
610
499
|
fk_info.map do |row|
|
611
500
|
options = {
|
612
|
-
column: row[
|
613
|
-
name: row[
|
614
|
-
primary_key: row[
|
501
|
+
column: row["column"],
|
502
|
+
name: row["name"],
|
503
|
+
primary_key: row["primary_key"]
|
615
504
|
}
|
616
505
|
|
617
|
-
options[:on_delete] = extract_foreign_key_action(row[
|
618
|
-
options[:on_update] = extract_foreign_key_action(row[
|
506
|
+
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
507
|
+
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
508
|
+
options[:validate] = row["valid"]
|
619
509
|
|
620
|
-
ForeignKeyDefinition.new(table_name, row[
|
510
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
621
511
|
end
|
622
512
|
end
|
623
513
|
|
624
|
-
def
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
514
|
+
def foreign_tables
|
515
|
+
query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
|
516
|
+
end
|
517
|
+
|
518
|
+
def foreign_table_exists?(table_name)
|
519
|
+
query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
|
520
|
+
end
|
521
|
+
|
522
|
+
def check_constraints(table_name) # :nodoc:
|
523
|
+
scope = quoted_scope(table_name)
|
524
|
+
|
525
|
+
check_info = exec_query(<<-SQL, "SCHEMA")
|
526
|
+
SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef, c.convalidated AS valid
|
527
|
+
FROM pg_constraint c
|
528
|
+
JOIN pg_class t ON c.conrelid = t.oid
|
529
|
+
WHERE c.contype = 'c'
|
530
|
+
AND t.relname = #{scope[:name]}
|
531
|
+
SQL
|
532
|
+
|
533
|
+
check_info.map do |row|
|
534
|
+
options = {
|
535
|
+
name: row["conname"],
|
536
|
+
validate: row["valid"]
|
537
|
+
}
|
538
|
+
expression = row["constraintdef"][/CHECK \({2}(.+)\){2}/, 1]
|
539
|
+
|
540
|
+
CheckConstraintDefinition.new(table_name, expression, options)
|
629
541
|
end
|
630
542
|
end
|
631
543
|
|
632
544
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
633
|
-
def type_to_sql(type, limit
|
634
|
-
sql =
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
545
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
|
546
|
+
sql = \
|
547
|
+
case type.to_s
|
548
|
+
when "binary"
|
549
|
+
# PostgreSQL doesn't support limits on binary (bytea) columns.
|
550
|
+
# The hard limit is 1GB, because of a 32-bit size field, and TOAST.
|
551
|
+
case limit
|
552
|
+
when nil, 0..0x3fffffff; super(type)
|
553
|
+
else raise ArgumentError, "No binary type has byte size #{limit}. The limit on binary can be at most 1GB - 1byte."
|
554
|
+
end
|
555
|
+
when "text"
|
556
|
+
# PostgreSQL doesn't support limits on text columns.
|
557
|
+
# The hard limit is 1GB, according to section 8.3 in the manual.
|
558
|
+
case limit
|
559
|
+
when nil, 0..0x3fffffff; super(type)
|
560
|
+
else raise ArgumentError, "No text type has byte size #{limit}. The limit on text can be at most 1GB - 1byte."
|
561
|
+
end
|
562
|
+
when "integer"
|
563
|
+
case limit
|
564
|
+
when 1, 2; "smallint"
|
565
|
+
when nil, 3, 4; "integer"
|
566
|
+
when 5..8; "bigint"
|
567
|
+
else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
|
568
|
+
end
|
569
|
+
else
|
570
|
+
super
|
655
571
|
end
|
656
|
-
else
|
657
|
-
super(type, limit, precision, scale)
|
658
|
-
end
|
659
572
|
|
660
|
-
sql
|
573
|
+
sql = "#{sql}[]" if array && type != :primary_key
|
661
574
|
sql
|
662
575
|
end
|
663
576
|
|
664
577
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
665
578
|
# requires that the ORDER BY include the distinct column.
|
666
579
|
def columns_for_distinct(columns, orders) #:nodoc:
|
667
|
-
order_columns = orders.
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
end
|
677
|
-
|
678
|
-
def
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
580
|
+
order_columns = orders.compact_blank.map { |s|
|
581
|
+
# Convert Arel node to string
|
582
|
+
s = visitor.compile(s) unless s.is_a?(String)
|
583
|
+
# Remove any ASC/DESC modifiers
|
584
|
+
s.gsub(/\s+(?:ASC|DESC)\b/i, "")
|
585
|
+
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
|
586
|
+
}.compact_blank.map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
587
|
+
|
588
|
+
(order_columns << super).join(", ")
|
589
|
+
end
|
590
|
+
|
591
|
+
def update_table_definition(table_name, base) # :nodoc:
|
592
|
+
PostgreSQL::Table.new(table_name, base)
|
593
|
+
end
|
594
|
+
|
595
|
+
def create_schema_dumper(options) # :nodoc:
|
596
|
+
PostgreSQL::SchemaDumper.create(self, options)
|
597
|
+
end
|
598
|
+
|
599
|
+
# Validates the given constraint.
|
600
|
+
#
|
601
|
+
# Validates the constraint named +constraint_name+ on +accounts+.
|
602
|
+
#
|
603
|
+
# validate_constraint :accounts, :constraint_name
|
604
|
+
def validate_constraint(table_name, constraint_name)
|
605
|
+
at = create_alter_table table_name
|
606
|
+
at.validate_constraint constraint_name
|
607
|
+
|
608
|
+
execute schema_creation.accept(at)
|
688
609
|
end
|
610
|
+
|
611
|
+
# Validates the given foreign key.
|
612
|
+
#
|
613
|
+
# Validates the foreign key on +accounts.branch_id+.
|
614
|
+
#
|
615
|
+
# validate_foreign_key :accounts, :branches
|
616
|
+
#
|
617
|
+
# Validates the foreign key on +accounts.owner_id+.
|
618
|
+
#
|
619
|
+
# validate_foreign_key :accounts, column: :owner_id
|
620
|
+
#
|
621
|
+
# Validates the foreign key named +special_fk_name+ on the +accounts+ table.
|
622
|
+
#
|
623
|
+
# validate_foreign_key :accounts, name: :special_fk_name
|
624
|
+
#
|
625
|
+
# The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
|
626
|
+
def validate_foreign_key(from_table, to_table = nil, **options)
|
627
|
+
fk_name_to_validate = foreign_key_for!(from_table, to_table: to_table, **options).name
|
628
|
+
|
629
|
+
validate_constraint from_table, fk_name_to_validate
|
630
|
+
end
|
631
|
+
|
632
|
+
# Validates the given check constraint.
|
633
|
+
#
|
634
|
+
# validate_check_constraint :products, name: "price_check"
|
635
|
+
#
|
636
|
+
# The +options+ hash accepts the same keys as add_check_constraint[rdoc-ref:ConnectionAdapters::SchemaStatements#add_check_constraint].
|
637
|
+
def validate_check_constraint(table_name, **options)
|
638
|
+
chk_name_to_validate = check_constraint_for!(table_name, **options).name
|
639
|
+
|
640
|
+
validate_constraint table_name, chk_name_to_validate
|
641
|
+
end
|
642
|
+
|
643
|
+
private
|
644
|
+
def schema_creation
|
645
|
+
PostgreSQL::SchemaCreation.new(self)
|
646
|
+
end
|
647
|
+
|
648
|
+
def create_table_definition(name, **options)
|
649
|
+
PostgreSQL::TableDefinition.new(self, name, **options)
|
650
|
+
end
|
651
|
+
|
652
|
+
def create_alter_table(name)
|
653
|
+
PostgreSQL::AlterTable.new create_table_definition(name)
|
654
|
+
end
|
655
|
+
|
656
|
+
def new_column_from_field(table_name, field)
|
657
|
+
column_name, type, default, notnull, oid, fmod, collation, comment = field
|
658
|
+
type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
|
659
|
+
default_value = extract_value_from_default(default)
|
660
|
+
default_function = extract_default_function(default_value, default)
|
661
|
+
|
662
|
+
if match = default_function&.match(/\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z/)
|
663
|
+
serial = sequence_name_from_parts(table_name, column_name, match[:suffix]) == match[:sequence_name]
|
664
|
+
end
|
665
|
+
|
666
|
+
PostgreSQL::Column.new(
|
667
|
+
column_name,
|
668
|
+
default_value,
|
669
|
+
type_metadata,
|
670
|
+
!notnull,
|
671
|
+
default_function,
|
672
|
+
collation: collation,
|
673
|
+
comment: comment.presence,
|
674
|
+
serial: serial
|
675
|
+
)
|
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
|
+
PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod)
|
688
|
+
end
|
689
|
+
|
690
|
+
def sequence_name_from_parts(table_name, column_name, suffix)
|
691
|
+
over_length = [table_name, column_name, suffix].sum(&:length) + 2 - max_identifier_length
|
692
|
+
|
693
|
+
if over_length > 0
|
694
|
+
column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
|
695
|
+
over_length -= column_name.length - column_name_length
|
696
|
+
column_name = column_name[0, column_name_length - [over_length, 0].min]
|
697
|
+
end
|
698
|
+
|
699
|
+
if over_length > 0
|
700
|
+
table_name = table_name[0, table_name.length - over_length]
|
701
|
+
end
|
702
|
+
|
703
|
+
"#{table_name}_#{column_name}_#{suffix}"
|
704
|
+
end
|
705
|
+
|
706
|
+
def extract_foreign_key_action(specifier)
|
707
|
+
case specifier
|
708
|
+
when "c"; :cascade
|
709
|
+
when "n"; :nullify
|
710
|
+
when "r"; :restrict
|
711
|
+
end
|
712
|
+
end
|
713
|
+
|
714
|
+
def add_column_for_alter(table_name, column_name, type, **options)
|
715
|
+
return super unless options.key?(:comment)
|
716
|
+
[super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
|
717
|
+
end
|
718
|
+
|
719
|
+
def change_column_for_alter(table_name, column_name, type, **options)
|
720
|
+
td = create_table_definition(table_name)
|
721
|
+
cd = td.new_column_definition(column_name, type, **options)
|
722
|
+
sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
|
723
|
+
sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
|
724
|
+
sqls
|
725
|
+
end
|
726
|
+
|
727
|
+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
728
|
+
column = column_for(table_name, column_name)
|
729
|
+
return unless column
|
730
|
+
|
731
|
+
default = extract_new_default_value(default_or_changes)
|
732
|
+
alter_column_query = "ALTER COLUMN #{quote_column_name(column_name)} %s"
|
733
|
+
if default.nil?
|
734
|
+
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
735
|
+
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
736
|
+
alter_column_query % "DROP DEFAULT"
|
737
|
+
else
|
738
|
+
alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
def change_column_null_for_alter(table_name, column_name, null, default = nil)
|
743
|
+
"ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
|
744
|
+
end
|
745
|
+
|
746
|
+
def add_index_opclass(quoted_columns, **options)
|
747
|
+
opclasses = options_for_index_columns(options[:opclass])
|
748
|
+
quoted_columns.each do |name, column|
|
749
|
+
column << " #{opclasses[name]}" if opclasses[name].present?
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
def add_options_for_index_columns(quoted_columns, **options)
|
754
|
+
quoted_columns = add_index_opclass(quoted_columns, **options)
|
755
|
+
super
|
756
|
+
end
|
757
|
+
|
758
|
+
def data_source_sql(name = nil, type: nil)
|
759
|
+
scope = quoted_scope(name, type: type)
|
760
|
+
scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
|
761
|
+
|
762
|
+
sql = +"SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace"
|
763
|
+
sql << " WHERE n.nspname = #{scope[:schema]}"
|
764
|
+
sql << " AND c.relname = #{scope[:name]}" if scope[:name]
|
765
|
+
sql << " AND c.relkind IN (#{scope[:type]})"
|
766
|
+
sql
|
767
|
+
end
|
768
|
+
|
769
|
+
def quoted_scope(name = nil, type: nil)
|
770
|
+
schema, name = extract_schema_qualified_name(name)
|
771
|
+
type = \
|
772
|
+
case type
|
773
|
+
when "BASE TABLE"
|
774
|
+
"'r','p'"
|
775
|
+
when "VIEW"
|
776
|
+
"'v','m'"
|
777
|
+
when "FOREIGN TABLE"
|
778
|
+
"'f'"
|
779
|
+
end
|
780
|
+
scope = {}
|
781
|
+
scope[:schema] = schema ? quote(schema) : "ANY (current_schemas(false))"
|
782
|
+
scope[:name] = quote(name) if name
|
783
|
+
scope[:type] = type if type
|
784
|
+
scope
|
785
|
+
end
|
786
|
+
|
787
|
+
def extract_schema_qualified_name(string)
|
788
|
+
name = Utils.extract_schema_qualified_name(string.to_s)
|
789
|
+
[name.schema, name.identifier]
|
790
|
+
end
|
689
791
|
end
|
690
792
|
end
|
691
793
|
end
|