activerecord 4.2.9 → 6.1.4.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 +5 -5
- data/CHANGELOG.md +964 -1382
- data/MIT-LICENSE +4 -2
- data/README.rdoc +15 -14
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +266 -251
- data/lib/active_record/association_relation.rb +40 -15
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +162 -69
- data/lib/active_record/associations/association_scope.rb +105 -130
- data/lib/active_record/associations/belongs_to_association.rb +83 -65
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +57 -43
- data/lib/active_record/associations/builder/belongs_to.rb +74 -57
- data/lib/active_record/associations/builder/collection_association.rb +15 -37
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
- data/lib/active_record/associations/builder/has_many.rb +13 -5
- data/lib/active_record/associations/builder/has_one.rb +44 -6
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +148 -287
- data/lib/active_record/associations/collection_proxy.rb +252 -150
- data/lib/active_record/associations/foreign_association.rb +23 -1
- data/lib/active_record/associations/has_many_association.rb +56 -98
- data/lib/active_record/associations/has_many_through_association.rb +68 -89
- data/lib/active_record/associations/has_one_association.rb +73 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
- 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/join_dependency.rb +174 -169
- data/lib/active_record/associations/preloader/association.rb +108 -115
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +97 -94
- data/lib/active_record/associations/singular_association.rb +18 -39
- data/lib/active_record/associations/through_association.rb +39 -19
- data/lib/active_record/associations.rb +1845 -1598
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
- data/lib/active_record/attribute_methods/dirty.rb +168 -148
- data/lib/active_record/attribute_methods/primary_key.rb +93 -83
- data/lib/active_record/attribute_methods/query.rb +8 -10
- data/lib/active_record/attribute_methods/read.rb +19 -79
- data/lib/active_record/attribute_methods/serialization.rb +49 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
- data/lib/active_record/attribute_methods/write.rb +24 -55
- data/lib/active_record/attribute_methods.rb +149 -154
- data/lib/active_record/attributes.rb +234 -78
- data/lib/active_record/autosave_association.rb +133 -60
- data/lib/active_record/base.rb +46 -46
- data/lib/active_record/callbacks.rb +234 -79
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
- data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
- data/lib/active_record/connection_adapters/column.rb +67 -40
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
- 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 +31 -20
- 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 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- 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 +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
- data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
- 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 +322 -373
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +314 -41
- data/lib/active_record/core.rb +458 -241
- data/lib/active_record/counter_cache.rb +70 -49
- 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/database_configurations.rb +272 -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 -106
- data/lib/active_record/enum.rb +211 -92
- data/lib/active_record/errors.rb +224 -54
- data/lib/active_record/explain.rb +27 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +33 -14
- 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 +275 -500
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +175 -110
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +121 -29
- data/lib/active_record/internal_metadata.rb +62 -0
- data/lib/active_record/legacy_yaml_adapter.rb +27 -5
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +98 -92
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +93 -31
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +185 -90
- data/lib/active_record/migration/compatibility.rb +295 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +673 -325
- data/lib/active_record/model_schema.rb +418 -113
- data/lib/active_record/nested_attributes.rb +263 -224
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +572 -136
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +170 -51
- data/lib/active_record/railties/console_sandbox.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +523 -199
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +454 -291
- data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
- data/lib/active_record/relation/batches.rb +217 -59
- data/lib/active_record/relation/calculations.rb +324 -249
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +316 -242
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +95 -103
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +136 -122
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -413
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -20
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -343
- data/lib/active_record/result.rb +91 -47
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +134 -122
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +141 -92
- data/lib/active_record/schema_migration.rb +24 -23
- data/lib/active_record/scoping/default.rb +96 -83
- data/lib/active_record/scoping/named.rb +78 -36
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +48 -0
- data/lib/active_record/serialization.rb +8 -6
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +89 -36
- data/lib/active_record/store.rb +128 -43
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +81 -0
- data/lib/active_record/tasks/database_tasks.rb +364 -130
- data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
- data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +182 -163
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +27 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +84 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +63 -56
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +42 -29
- 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/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 +76 -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/nodes.rb +70 -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/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/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
- data/lib/rails/generators/active_record/migration.rb +35 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
- 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.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +172 -65
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- 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 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
- 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/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/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/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,40 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module PostgreSQL
|
4
|
-
class SchemaCreation < AbstractAdapter::SchemaCreation
|
5
|
-
private
|
6
|
-
|
7
|
-
def visit_ColumnDefinition(o)
|
8
|
-
sql = super
|
9
|
-
if o.primary_key? && o.type != :primary_key
|
10
|
-
sql << " PRIMARY KEY "
|
11
|
-
add_column_options!(sql, column_options(o))
|
12
|
-
end
|
13
|
-
sql
|
14
|
-
end
|
15
|
-
|
16
|
-
def add_column_options!(sql, options)
|
17
|
-
if options[:array] || options[:column].try(:array)
|
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
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
6
|
module SchemaStatements
|
39
7
|
# Drops the database specified on the +name+ attribute
|
40
8
|
# and creates it again using the provided +options+.
|
@@ -52,26 +20,26 @@ module ActiveRecord
|
|
52
20
|
# create_database config[:database], config
|
53
21
|
# create_database 'foo_development', encoding: 'unicode'
|
54
22
|
def create_database(name, options = {})
|
55
|
-
options = { encoding:
|
56
|
-
|
57
|
-
option_string = options.
|
58
|
-
memo
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
+
""
|
75
43
|
end
|
76
44
|
end
|
77
45
|
|
@@ -86,161 +54,150 @@ module ActiveRecord
|
|
86
54
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
87
55
|
end
|
88
56
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
SELECT tablename
|
93
|
-
FROM pg_tables
|
94
|
-
WHERE schemaname = ANY (current_schemas(false))
|
95
|
-
SQL
|
96
|
-
end
|
97
|
-
|
98
|
-
def data_sources # :nodoc
|
99
|
-
select_values(<<-SQL, 'SCHEMA')
|
100
|
-
SELECT c.relname
|
101
|
-
FROM pg_class c
|
102
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
103
|
-
WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
104
|
-
AND n.nspname = ANY (current_schemas(false))
|
105
|
-
SQL
|
106
|
-
end
|
107
|
-
|
108
|
-
# Returns true if table exists.
|
109
|
-
# If the schema is not specified as part of +name+ then it will only find tables within
|
110
|
-
# the current schema search path (regardless of permissions to access tables in other schemas)
|
111
|
-
def table_exists?(name)
|
112
|
-
name = Utils.extract_schema_qualified_name(name.to_s)
|
113
|
-
return false unless name.identifier
|
114
|
-
|
115
|
-
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
116
|
-
SELECT COUNT(*)
|
117
|
-
FROM pg_class c
|
118
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
119
|
-
WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
120
|
-
AND c.relname = '#{name.identifier}'
|
121
|
-
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
122
|
-
SQL
|
123
|
-
end
|
124
|
-
alias data_source_exists? table_exists?
|
125
|
-
|
126
|
-
def drop_table(table_name, options = {})
|
127
|
-
execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
57
|
+
def drop_table(table_name, **options) # :nodoc:
|
58
|
+
schema_cache.clear_data_source_cache!(table_name.to_s)
|
59
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
128
60
|
end
|
129
61
|
|
130
62
|
# Returns true if schema exists.
|
131
63
|
def schema_exists?(name)
|
132
|
-
|
133
|
-
SELECT COUNT(*)
|
134
|
-
FROM pg_namespace
|
135
|
-
WHERE nspname = '#{name}'
|
136
|
-
SQL
|
64
|
+
query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
|
137
65
|
end
|
138
66
|
|
139
|
-
|
140
|
-
|
67
|
+
# Verifies existence of an index with a given name.
|
68
|
+
def index_name_exists?(table_name, index_name)
|
69
|
+
table = quoted_scope(table_name)
|
70
|
+
index = quoted_scope(index_name)
|
71
|
+
|
72
|
+
query_value(<<~SQL, "SCHEMA").to_i > 0
|
141
73
|
SELECT COUNT(*)
|
142
74
|
FROM pg_class t
|
143
75
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
144
76
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
145
|
-
|
146
|
-
|
147
|
-
AND
|
148
|
-
AND
|
77
|
+
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
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]}
|
149
82
|
SQL
|
150
83
|
end
|
151
84
|
|
152
85
|
# Returns an array of indexes for the given table.
|
153
|
-
def indexes(table_name
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
86
|
+
def indexes(table_name) # :nodoc:
|
87
|
+
scope = quoted_scope(table_name)
|
88
|
+
|
89
|
+
result = query(<<~SQL, "SCHEMA")
|
90
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
|
91
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
92
|
+
FROM pg_class t
|
93
|
+
INNER JOIN pg_index d ON t.oid = d.indrelid
|
94
|
+
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
95
|
+
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
96
|
+
WHERE i.relkind IN ('i', 'I')
|
97
|
+
AND d.indisprimary = 'f'
|
98
|
+
AND t.relname = #{scope[:name]}
|
99
|
+
AND n.nspname = #{scope[:schema]}
|
163
100
|
ORDER BY i.relname
|
164
101
|
SQL
|
165
102
|
|
166
103
|
result.map do |row|
|
167
104
|
index_name = row[0]
|
168
|
-
unique = row[1]
|
169
|
-
indkey = row[2].split(" ")
|
105
|
+
unique = row[1]
|
106
|
+
indkey = row[2].split(" ").map(&:to_i)
|
170
107
|
inddef = row[3]
|
171
108
|
oid = row[4]
|
109
|
+
comment = row[5]
|
172
110
|
|
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
|
179
|
-
|
180
|
-
column_names = columns.values_at(*indkey).compact
|
111
|
+
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
|
181
112
|
|
182
|
-
|
183
|
-
|
184
|
-
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
|
185
|
-
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
|
186
|
-
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
187
|
-
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
113
|
+
orders = {}
|
114
|
+
opclasses = {}
|
188
115
|
|
189
|
-
|
116
|
+
if indkey.include?(0)
|
117
|
+
columns = expressions
|
118
|
+
else
|
119
|
+
columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
|
120
|
+
SELECT a.attnum, a.attname
|
121
|
+
FROM pg_attribute a
|
122
|
+
WHERE a.attrelid = #{oid}
|
123
|
+
AND a.attnum IN (#{indkey.join(",")})
|
124
|
+
SQL
|
125
|
+
|
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
|
190
136
|
end
|
191
|
-
|
137
|
+
|
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
|
+
)
|
149
|
+
end
|
192
150
|
end
|
193
151
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
|
198
|
-
oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type)
|
199
|
-
default_value = extract_value_from_default(oid, default)
|
200
|
-
default_function = extract_default_function(default_value, default)
|
201
|
-
new_column(column_name, default_value, oid, type, notnull == 'f', default_function)
|
152
|
+
def table_options(table_name) # :nodoc:
|
153
|
+
if comment = table_comment(table_name)
|
154
|
+
{ comment: comment }
|
202
155
|
end
|
203
156
|
end
|
204
157
|
|
205
|
-
|
206
|
-
|
158
|
+
# Returns a comment stored in database for given table
|
159
|
+
def table_comment(table_name) # :nodoc:
|
160
|
+
scope = quoted_scope(table_name, type: "BASE TABLE")
|
161
|
+
if scope[:name]
|
162
|
+
query_value(<<~SQL, "SCHEMA")
|
163
|
+
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
|
164
|
+
FROM pg_catalog.pg_class c
|
165
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
166
|
+
WHERE c.relname = #{scope[:name]}
|
167
|
+
AND c.relkind IN (#{scope[:type]})
|
168
|
+
AND n.nspname = #{scope[:schema]}
|
169
|
+
SQL
|
170
|
+
end
|
207
171
|
end
|
208
172
|
|
209
173
|
# Returns the current database name.
|
210
174
|
def current_database
|
211
|
-
|
175
|
+
query_value("SELECT current_database()", "SCHEMA")
|
212
176
|
end
|
213
177
|
|
214
178
|
# Returns the current schema name.
|
215
179
|
def current_schema
|
216
|
-
|
180
|
+
query_value("SELECT current_schema", "SCHEMA")
|
217
181
|
end
|
218
182
|
|
219
183
|
# Returns the current database encoding format.
|
220
184
|
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
|
185
|
+
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
225
186
|
end
|
226
187
|
|
227
188
|
# Returns the current database collation.
|
228
189
|
def collation
|
229
|
-
|
230
|
-
SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
231
|
-
end_sql
|
190
|
+
query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
232
191
|
end
|
233
192
|
|
234
193
|
# Returns the current database ctype.
|
235
194
|
def ctype
|
236
|
-
|
237
|
-
SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
238
|
-
end_sql
|
195
|
+
query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
239
196
|
end
|
240
197
|
|
241
198
|
# Returns an array of schema names.
|
242
199
|
def schema_names
|
243
|
-
|
200
|
+
query_values(<<~SQL, "SCHEMA")
|
244
201
|
SELECT nspname
|
245
202
|
FROM pg_namespace
|
246
203
|
WHERE nspname !~ '^pg_.*'
|
@@ -250,56 +207,53 @@ module ActiveRecord
|
|
250
207
|
end
|
251
208
|
|
252
209
|
# Creates a schema for the given schema name.
|
253
|
-
def create_schema
|
254
|
-
execute "CREATE SCHEMA #{schema_name}"
|
210
|
+
def create_schema(schema_name)
|
211
|
+
execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
|
255
212
|
end
|
256
213
|
|
257
214
|
# Drops the schema for the given schema name.
|
258
|
-
def drop_schema
|
259
|
-
execute "DROP SCHEMA #{schema_name} CASCADE"
|
215
|
+
def drop_schema(schema_name, **options)
|
216
|
+
execute "DROP SCHEMA#{' IF EXISTS' if options[:if_exists]} #{quote_schema_name(schema_name)} CASCADE"
|
260
217
|
end
|
261
218
|
|
262
219
|
# Sets the schema search path to a string of comma-separated schema names.
|
263
220
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
264
|
-
# See:
|
221
|
+
# See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
|
265
222
|
#
|
266
223
|
# This should be not be called manually but set in database.yml.
|
267
224
|
def schema_search_path=(schema_csv)
|
268
225
|
if schema_csv
|
269
|
-
execute("SET search_path TO #{schema_csv}",
|
226
|
+
execute("SET search_path TO #{schema_csv}", "SCHEMA")
|
270
227
|
@schema_search_path = schema_csv
|
271
228
|
end
|
272
229
|
end
|
273
230
|
|
274
231
|
# Returns the active schema search path.
|
275
232
|
def schema_search_path
|
276
|
-
@schema_search_path ||=
|
233
|
+
@schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
|
277
234
|
end
|
278
235
|
|
279
236
|
# Returns the current client message level.
|
280
237
|
def client_min_messages
|
281
|
-
|
238
|
+
query_value("SHOW client_min_messages", "SCHEMA")
|
282
239
|
end
|
283
240
|
|
284
241
|
# Set the client message level.
|
285
242
|
def client_min_messages=(level)
|
286
|
-
execute("SET client_min_messages TO '#{level}'",
|
243
|
+
execute("SET client_min_messages TO '#{level}'", "SCHEMA")
|
287
244
|
end
|
288
245
|
|
289
246
|
# Returns the sequence name for a table's primary key or some other specified key.
|
290
|
-
def default_sequence_name(table_name, pk =
|
291
|
-
result = serial_sequence(table_name, pk
|
247
|
+
def default_sequence_name(table_name, pk = "id") #:nodoc:
|
248
|
+
result = serial_sequence(table_name, pk)
|
292
249
|
return nil unless result
|
293
250
|
Utils.extract_schema_qualified_name(result).to_s
|
294
251
|
rescue ActiveRecord::StatementInvalid
|
295
|
-
PostgreSQL::Name.new(nil, "#{table_name}_#{pk
|
252
|
+
PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
|
296
253
|
end
|
297
254
|
|
298
255
|
def serial_sequence(table, column)
|
299
|
-
|
300
|
-
SELECT pg_get_serial_sequence('#{table}', '#{column}')
|
301
|
-
eosql
|
302
|
-
result.rows.first.first
|
256
|
+
query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
|
303
257
|
end
|
304
258
|
|
305
259
|
# Sets the sequence of a table's primary key to the specified value.
|
@@ -310,18 +264,16 @@ module ActiveRecord
|
|
310
264
|
if sequence
|
311
265
|
quoted_sequence = quote_table_name(sequence)
|
312
266
|
|
313
|
-
|
314
|
-
SELECT setval('#{quoted_sequence}', #{value})
|
315
|
-
end_sql
|
267
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
|
316
268
|
else
|
317
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
|
269
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
|
318
270
|
end
|
319
271
|
end
|
320
272
|
end
|
321
273
|
|
322
274
|
# Resets the sequence of a table's primary key to the maximum value.
|
323
275
|
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
|
324
|
-
unless pk
|
276
|
+
unless pk && sequence
|
325
277
|
default_pk, default_sequence = pk_and_sequence_for(table)
|
326
278
|
|
327
279
|
pk ||= default_pk
|
@@ -329,15 +281,21 @@ module ActiveRecord
|
|
329
281
|
end
|
330
282
|
|
331
283
|
if @logger && pk && !sequence
|
332
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence"
|
284
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence."
|
333
285
|
end
|
334
286
|
|
335
287
|
if pk && sequence
|
336
288
|
quoted_sequence = quote_table_name(sequence)
|
289
|
+
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
|
290
|
+
if max_pk.nil?
|
291
|
+
if database_version >= 100000
|
292
|
+
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
|
293
|
+
else
|
294
|
+
minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
|
295
|
+
end
|
296
|
+
end
|
337
297
|
|
338
|
-
|
339
|
-
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
|
-
end_sql
|
298
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
341
299
|
end
|
342
300
|
end
|
343
301
|
|
@@ -345,7 +303,7 @@ module ActiveRecord
|
|
345
303
|
def pk_and_sequence_for(table) #:nodoc:
|
346
304
|
# First try looking for a sequence with a dependency on the
|
347
305
|
# given table's primary key.
|
348
|
-
result = query(
|
306
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
349
307
|
SELECT attr.attname, nsp.nspname, seq.relname
|
350
308
|
FROM pg_class seq,
|
351
309
|
pg_attribute attr,
|
@@ -361,11 +319,11 @@ module ActiveRecord
|
|
361
319
|
AND seq.relnamespace = nsp.oid
|
362
320
|
AND cons.contype = 'p'
|
363
321
|
AND dep.classid = 'pg_class'::regclass
|
364
|
-
AND dep.refobjid =
|
365
|
-
|
322
|
+
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
|
323
|
+
SQL
|
366
324
|
|
367
|
-
if result.nil?
|
368
|
-
result = query(
|
325
|
+
if result.nil? || result.empty?
|
326
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
369
327
|
SELECT attr.attname, nsp.nspname,
|
370
328
|
CASE
|
371
329
|
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
@@ -379,10 +337,10 @@ module ActiveRecord
|
|
379
337
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
380
338
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
381
339
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
382
|
-
WHERE t.oid =
|
340
|
+
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
383
341
|
AND cons.contype = 'p'
|
384
342
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
385
|
-
|
343
|
+
SQL
|
386
344
|
end
|
387
345
|
|
388
346
|
pk = result.shift
|
@@ -395,17 +353,20 @@ module ActiveRecord
|
|
395
353
|
nil
|
396
354
|
end
|
397
355
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
356
|
+
def primary_keys(table_name) # :nodoc:
|
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
|
369
|
+
SQL
|
409
370
|
end
|
410
371
|
|
411
372
|
# Renames a table.
|
@@ -416,82 +377,103 @@ module ActiveRecord
|
|
416
377
|
# rename_table('octopuses', 'octopi')
|
417
378
|
def rename_table(table_name, new_name)
|
418
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)
|
419
382
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
420
383
|
pk, seq = pk_and_sequence_for(new_name)
|
421
|
-
if
|
422
|
-
new_seq = "#{new_name}_#{pk}_seq"
|
384
|
+
if pk
|
423
385
|
idx = "#{table_name}_pkey"
|
424
386
|
new_idx = "#{new_name}_pkey"
|
425
|
-
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
|
426
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
|
427
392
|
end
|
428
|
-
|
429
393
|
rename_table_indexes(table_name, new_name)
|
430
394
|
end
|
431
395
|
|
432
|
-
def add_column(table_name, column_name, type, options
|
396
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
433
397
|
clear_cache!
|
434
398
|
super
|
399
|
+
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
435
400
|
end
|
436
401
|
|
437
|
-
|
438
|
-
def change_column(table_name, column_name, type, options = {})
|
402
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
439
403
|
clear_cache!
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
|
444
|
-
sql << " USING #{options[:using]}" if options[:using]
|
445
|
-
if options[:cast_as]
|
446
|
-
sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
|
447
|
-
end
|
448
|
-
execute sql
|
449
|
-
|
450
|
-
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
451
|
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
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)
|
452
407
|
end
|
453
408
|
|
454
409
|
# Changes the default value of a table column.
|
455
|
-
def change_column_default(table_name, column_name,
|
456
|
-
|
457
|
-
column = column_for(table_name, column_name)
|
458
|
-
return unless column
|
459
|
-
|
460
|
-
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
461
|
-
if default.nil?
|
462
|
-
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
463
|
-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
464
|
-
execute alter_column_query % "DROP DEFAULT"
|
465
|
-
else
|
466
|
-
execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
|
467
|
-
end
|
410
|
+
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
411
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
468
412
|
end
|
469
413
|
|
470
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
414
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
471
415
|
clear_cache!
|
472
416
|
unless null || default.nil?
|
473
417
|
column = column_for(table_name, column_name)
|
474
|
-
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
|
475
419
|
end
|
476
|
-
execute
|
420
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
|
421
|
+
end
|
422
|
+
|
423
|
+
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
424
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
425
|
+
clear_cache!
|
426
|
+
comment = extract_new_comment_value(comment_or_changes)
|
427
|
+
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
|
428
|
+
end
|
429
|
+
|
430
|
+
# Adds comment for given table or drops it if +comment+ is a +nil+
|
431
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
432
|
+
clear_cache!
|
433
|
+
comment = extract_new_comment_value(comment_or_changes)
|
434
|
+
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
|
477
435
|
end
|
478
436
|
|
479
437
|
# Renames a column in a table.
|
480
438
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
481
439
|
clear_cache!
|
482
|
-
execute
|
440
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
483
441
|
rename_column_indexes(table_name, column_name, new_column_name)
|
484
442
|
end
|
485
443
|
|
486
|
-
def add_index(table_name, column_name, options
|
487
|
-
|
488
|
-
|
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
|
489
452
|
end
|
490
453
|
|
491
|
-
def remove_index
|
492
|
-
|
454
|
+
def remove_index(table_name, column_name = nil, **options) # :nodoc:
|
455
|
+
table = Utils.extract_schema_qualified_name(table_name.to_s)
|
456
|
+
|
457
|
+
if options.key?(:name)
|
458
|
+
provided_index = Utils.extract_schema_qualified_name(options[:name].to_s)
|
459
|
+
|
460
|
+
options[:name] = provided_index.identifier
|
461
|
+
table = PostgreSQL::Name.new(provided_index.schema, table.identifier) unless table.schema.present?
|
462
|
+
|
463
|
+
if provided_index.schema.present? && table.schema != provided_index.schema
|
464
|
+
raise ArgumentError.new("Index schema '#{provided_index.schema}' does not match table schema '#{table.schema}'")
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
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)}"
|
493
473
|
end
|
494
474
|
|
475
|
+
# Renames an index of a table. Raises error if length of new
|
476
|
+
# index name is greater than allowed limit.
|
495
477
|
def rename_index(table_name, old_name, new_name)
|
496
478
|
validate_index_length!(table_name, new_name)
|
497
479
|
|
@@ -499,8 +481,9 @@ module ActiveRecord
|
|
499
481
|
end
|
500
482
|
|
501
483
|
def foreign_keys(table_name)
|
502
|
-
|
503
|
-
|
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
|
504
487
|
FROM pg_constraint c
|
505
488
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
506
489
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
@@ -508,88 +491,303 @@ module ActiveRecord
|
|
508
491
|
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
509
492
|
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
510
493
|
WHERE c.contype = 'f'
|
511
|
-
AND t1.relname = #{
|
512
|
-
AND t3.nspname =
|
494
|
+
AND t1.relname = #{scope[:name]}
|
495
|
+
AND t3.nspname = #{scope[:schema]}
|
513
496
|
ORDER BY c.conname
|
514
497
|
SQL
|
515
498
|
|
516
499
|
fk_info.map do |row|
|
517
500
|
options = {
|
518
|
-
column: row[
|
519
|
-
name: row[
|
520
|
-
primary_key: row[
|
501
|
+
column: row["column"],
|
502
|
+
name: row["name"],
|
503
|
+
primary_key: row["primary_key"]
|
521
504
|
}
|
522
505
|
|
523
|
-
options[:on_delete] = extract_foreign_key_action(row[
|
524
|
-
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"]
|
525
509
|
|
526
|
-
ForeignKeyDefinition.new(table_name, row[
|
510
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
527
511
|
end
|
528
512
|
end
|
529
513
|
|
530
|
-
def
|
531
|
-
|
532
|
-
when 'c'; :cascade
|
533
|
-
when 'n'; :nullify
|
534
|
-
when 'r'; :restrict
|
535
|
-
end
|
514
|
+
def foreign_tables
|
515
|
+
query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
|
536
516
|
end
|
537
517
|
|
538
|
-
def
|
539
|
-
|
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)
|
541
|
+
end
|
540
542
|
end
|
541
543
|
|
542
544
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
543
|
-
def type_to_sql(type, limit
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
when
|
564
|
-
when
|
565
|
-
|
566
|
-
|
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
|
567
571
|
end
|
568
|
-
when 'datetime'
|
569
|
-
return super unless precision
|
570
572
|
|
571
|
-
|
572
|
-
|
573
|
-
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
574
|
-
end
|
575
|
-
else
|
576
|
-
super
|
577
|
-
end
|
573
|
+
sql = "#{sql}[]" if array && type != :primary_key
|
574
|
+
sql
|
578
575
|
end
|
579
576
|
|
580
577
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
581
578
|
# requires that the ORDER BY include the distinct column.
|
582
579
|
def columns_for_distinct(columns, orders) #:nodoc:
|
583
|
-
order_columns = orders.
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
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
590
|
|
591
|
-
|
591
|
+
def update_table_definition(table_name, base) # :nodoc:
|
592
|
+
PostgreSQL::Table.new(table_name, base)
|
592
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)
|
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
|
593
791
|
end
|
594
792
|
end
|
595
793
|
end
|