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