activerecord 4.2.0 → 6.1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1221 -796
- 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 +267 -249
- data/lib/active_record/association_relation.rb +45 -7
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +172 -67
- data/lib/active_record/associations/association_scope.rb +105 -129
- data/lib/active_record/associations/belongs_to_association.rb +85 -59
- 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 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
- 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 +168 -279
- data/lib/active_record/associations/collection_proxy.rb +263 -155
- data/lib/active_record/associations/foreign_association.rb +33 -0
- data/lib/active_record/associations/has_many_association.rb +57 -84
- data/lib/active_record/associations/has_many_through_association.rb +70 -82
- data/lib/active_record/associations/has_one_association.rb +74 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -73
- 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 +175 -164
- data/lib/active_record/associations/preloader/association.rb +107 -112
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +99 -96
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +49 -24
- data/lib/active_record/associations.rb +1845 -1597
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
- data/lib/active_record/attribute_methods/dirty.rb +168 -138
- 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 +59 -36
- data/lib/active_record/attribute_methods/write.rb +25 -56
- data/lib/active_record/attribute_methods.rb +153 -162
- data/lib/active_record/attributes.rb +234 -70
- data/lib/active_record/autosave_association.rb +157 -69
- data/lib/active_record/base.rb +49 -50
- 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 +46 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
- data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
- data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
- 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 +196 -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 +271 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
- 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 +78 -161
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
- 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 +5 -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 +17 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
- 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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
- 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 +171 -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 +499 -293
- 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 +595 -382
- data/lib/active_record/connection_adapters/schema_cache.rb +191 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -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 -389
- 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 +488 -243
- data/lib/active_record/counter_cache.rb +71 -50
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -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 +273 -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 +212 -94
- data/lib/active_record/errors.rb +225 -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 +11 -6
- 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 +273 -496
- 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 +64 -0
- data/lib/active_record/legacy_yaml_adapter.rb +52 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +103 -95
- 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 +298 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +685 -309
- data/lib/active_record/model_schema.rb +420 -113
- data/lib/active_record/nested_attributes.rb +265 -216
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +574 -135
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +175 -54
- 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 +533 -216
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +485 -310
- 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 +326 -244
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +318 -256
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +99 -84
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
- 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 +139 -96
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -409
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +23 -21
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -342
- 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 -26
- data/lib/active_record/scoping/default.rb +96 -82
- 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 +133 -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 +366 -129
- data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
- 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 +291 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +181 -152
- 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 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
- 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 +33 -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 +65 -48
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +44 -28
- 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 -10
- 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.rb +7 -5
- metadata +175 -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 -149
- data/lib/active_record/attribute_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,150 +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
|
-
# Returns true if table exists.
|
99
|
-
# If the schema is not specified as part of +name+ then it will only find tables within
|
100
|
-
# the current schema search path (regardless of permissions to access tables in other schemas)
|
101
|
-
def table_exists?(name)
|
102
|
-
name = Utils.extract_schema_qualified_name(name.to_s)
|
103
|
-
return false unless name.identifier
|
104
|
-
|
105
|
-
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
106
|
-
SELECT COUNT(*)
|
107
|
-
FROM pg_class c
|
108
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
109
|
-
WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
110
|
-
AND c.relname = '#{name.identifier}'
|
111
|
-
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
112
|
-
SQL
|
113
|
-
end
|
114
|
-
|
115
|
-
def drop_table(table_name, options = {})
|
116
|
-
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}"
|
117
60
|
end
|
118
61
|
|
119
62
|
# Returns true if schema exists.
|
120
63
|
def schema_exists?(name)
|
121
|
-
|
122
|
-
SELECT COUNT(*)
|
123
|
-
FROM pg_namespace
|
124
|
-
WHERE nspname = '#{name}'
|
125
|
-
SQL
|
64
|
+
query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
|
126
65
|
end
|
127
66
|
|
128
|
-
|
129
|
-
|
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
|
130
73
|
SELECT COUNT(*)
|
131
74
|
FROM pg_class t
|
132
75
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
133
76
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
134
|
-
|
135
|
-
|
136
|
-
AND
|
137
|
-
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]}
|
138
82
|
SQL
|
139
83
|
end
|
140
84
|
|
141
85
|
# Returns an array of indexes for the given table.
|
142
|
-
def indexes(table_name
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
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]}
|
152
100
|
ORDER BY i.relname
|
153
101
|
SQL
|
154
102
|
|
155
103
|
result.map do |row|
|
156
104
|
index_name = row[0]
|
157
|
-
unique = row[1]
|
158
|
-
indkey = row[2].split(" ")
|
105
|
+
unique = row[1]
|
106
|
+
indkey = row[2].split(" ").map(&:to_i)
|
159
107
|
inddef = row[3]
|
160
108
|
oid = row[4]
|
109
|
+
comment = row[5]
|
161
110
|
|
162
|
-
|
163
|
-
SELECT a.attnum, a.attname
|
164
|
-
FROM pg_attribute a
|
165
|
-
WHERE a.attrelid = #{oid}
|
166
|
-
AND a.attnum IN (#{indkey.join(",")})
|
167
|
-
SQL
|
111
|
+
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
|
168
112
|
|
169
|
-
|
113
|
+
orders = {}
|
114
|
+
opclasses = {}
|
170
115
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
179
136
|
end
|
180
|
-
|
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
|
181
150
|
end
|
182
151
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
|
187
|
-
oid = get_oid_type(oid.to_i, fmod.to_i, column_name, type)
|
188
|
-
default_value = extract_value_from_default(oid, default)
|
189
|
-
default_function = extract_default_function(default_value, default)
|
190
|
-
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 }
|
191
155
|
end
|
192
156
|
end
|
193
157
|
|
194
|
-
|
195
|
-
|
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
|
196
171
|
end
|
197
172
|
|
198
173
|
# Returns the current database name.
|
199
174
|
def current_database
|
200
|
-
|
175
|
+
query_value("SELECT current_database()", "SCHEMA")
|
201
176
|
end
|
202
177
|
|
203
178
|
# Returns the current schema name.
|
204
179
|
def current_schema
|
205
|
-
|
180
|
+
query_value("SELECT current_schema", "SCHEMA")
|
206
181
|
end
|
207
182
|
|
208
183
|
# Returns the current database encoding format.
|
209
184
|
def encoding
|
210
|
-
|
211
|
-
SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
|
212
|
-
WHERE pg_database.datname LIKE '#{current_database}'
|
213
|
-
end_sql
|
185
|
+
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
214
186
|
end
|
215
187
|
|
216
188
|
# Returns the current database collation.
|
217
189
|
def collation
|
218
|
-
|
219
|
-
SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
220
|
-
end_sql
|
190
|
+
query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
221
191
|
end
|
222
192
|
|
223
193
|
# Returns the current database ctype.
|
224
194
|
def ctype
|
225
|
-
|
226
|
-
SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
|
227
|
-
end_sql
|
195
|
+
query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
228
196
|
end
|
229
197
|
|
230
198
|
# Returns an array of schema names.
|
231
199
|
def schema_names
|
232
|
-
|
200
|
+
query_values(<<~SQL, "SCHEMA")
|
233
201
|
SELECT nspname
|
234
202
|
FROM pg_namespace
|
235
203
|
WHERE nspname !~ '^pg_.*'
|
@@ -239,56 +207,53 @@ module ActiveRecord
|
|
239
207
|
end
|
240
208
|
|
241
209
|
# Creates a schema for the given schema name.
|
242
|
-
def create_schema
|
243
|
-
execute "CREATE SCHEMA #{schema_name}"
|
210
|
+
def create_schema(schema_name)
|
211
|
+
execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
|
244
212
|
end
|
245
213
|
|
246
214
|
# Drops the schema for the given schema name.
|
247
|
-
def drop_schema
|
248
|
-
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"
|
249
217
|
end
|
250
218
|
|
251
219
|
# Sets the schema search path to a string of comma-separated schema names.
|
252
220
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
253
|
-
# See:
|
221
|
+
# See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
|
254
222
|
#
|
255
223
|
# This should be not be called manually but set in database.yml.
|
256
224
|
def schema_search_path=(schema_csv)
|
257
225
|
if schema_csv
|
258
|
-
execute("SET search_path TO #{schema_csv}",
|
226
|
+
execute("SET search_path TO #{schema_csv}", "SCHEMA")
|
259
227
|
@schema_search_path = schema_csv
|
260
228
|
end
|
261
229
|
end
|
262
230
|
|
263
231
|
# Returns the active schema search path.
|
264
232
|
def schema_search_path
|
265
|
-
@schema_search_path ||=
|
233
|
+
@schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
|
266
234
|
end
|
267
235
|
|
268
236
|
# Returns the current client message level.
|
269
237
|
def client_min_messages
|
270
|
-
|
238
|
+
query_value("SHOW client_min_messages", "SCHEMA")
|
271
239
|
end
|
272
240
|
|
273
241
|
# Set the client message level.
|
274
242
|
def client_min_messages=(level)
|
275
|
-
execute("SET client_min_messages TO '#{level}'",
|
243
|
+
execute("SET client_min_messages TO '#{level}'", "SCHEMA")
|
276
244
|
end
|
277
245
|
|
278
246
|
# Returns the sequence name for a table's primary key or some other specified key.
|
279
|
-
def default_sequence_name(table_name, pk =
|
280
|
-
result = serial_sequence(table_name, pk
|
247
|
+
def default_sequence_name(table_name, pk = "id") #:nodoc:
|
248
|
+
result = serial_sequence(table_name, pk)
|
281
249
|
return nil unless result
|
282
250
|
Utils.extract_schema_qualified_name(result).to_s
|
283
251
|
rescue ActiveRecord::StatementInvalid
|
284
|
-
PostgreSQL::Name.new(nil, "#{table_name}_#{pk
|
252
|
+
PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
|
285
253
|
end
|
286
254
|
|
287
255
|
def serial_sequence(table, column)
|
288
|
-
|
289
|
-
SELECT pg_get_serial_sequence('#{table}', '#{column}')
|
290
|
-
eosql
|
291
|
-
result.rows.first.first
|
256
|
+
query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
|
292
257
|
end
|
293
258
|
|
294
259
|
# Sets the sequence of a table's primary key to the specified value.
|
@@ -299,18 +264,16 @@ module ActiveRecord
|
|
299
264
|
if sequence
|
300
265
|
quoted_sequence = quote_table_name(sequence)
|
301
266
|
|
302
|
-
|
303
|
-
SELECT setval('#{quoted_sequence}', #{value})
|
304
|
-
end_sql
|
267
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
|
305
268
|
else
|
306
|
-
@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
|
307
270
|
end
|
308
271
|
end
|
309
272
|
end
|
310
273
|
|
311
274
|
# Resets the sequence of a table's primary key to the maximum value.
|
312
275
|
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
|
313
|
-
unless pk
|
276
|
+
unless pk && sequence
|
314
277
|
default_pk, default_sequence = pk_and_sequence_for(table)
|
315
278
|
|
316
279
|
pk ||= default_pk
|
@@ -318,15 +281,21 @@ module ActiveRecord
|
|
318
281
|
end
|
319
282
|
|
320
283
|
if @logger && pk && !sequence
|
321
|
-
@logger.warn "#{table} has primary key #{pk} with no default sequence"
|
284
|
+
@logger.warn "#{table} has primary key #{pk} with no default sequence."
|
322
285
|
end
|
323
286
|
|
324
287
|
if pk && sequence
|
325
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
|
326
297
|
|
327
|
-
|
328
|
-
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)
|
329
|
-
end_sql
|
298
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
330
299
|
end
|
331
300
|
end
|
332
301
|
|
@@ -334,7 +303,7 @@ module ActiveRecord
|
|
334
303
|
def pk_and_sequence_for(table) #:nodoc:
|
335
304
|
# First try looking for a sequence with a dependency on the
|
336
305
|
# given table's primary key.
|
337
|
-
result = query(
|
306
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
338
307
|
SELECT attr.attname, nsp.nspname, seq.relname
|
339
308
|
FROM pg_class seq,
|
340
309
|
pg_attribute attr,
|
@@ -350,11 +319,11 @@ module ActiveRecord
|
|
350
319
|
AND seq.relnamespace = nsp.oid
|
351
320
|
AND cons.contype = 'p'
|
352
321
|
AND dep.classid = 'pg_class'::regclass
|
353
|
-
AND dep.refobjid =
|
354
|
-
|
322
|
+
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
|
323
|
+
SQL
|
355
324
|
|
356
|
-
if result.nil?
|
357
|
-
result = query(
|
325
|
+
if result.nil? || result.empty?
|
326
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
358
327
|
SELECT attr.attname, nsp.nspname,
|
359
328
|
CASE
|
360
329
|
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
@@ -368,10 +337,10 @@ module ActiveRecord
|
|
368
337
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
369
338
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
370
339
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
371
|
-
WHERE t.oid =
|
340
|
+
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
372
341
|
AND cons.contype = 'p'
|
373
342
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
374
|
-
|
343
|
+
SQL
|
375
344
|
end
|
376
345
|
|
377
346
|
pk = result.shift
|
@@ -384,17 +353,20 @@ module ActiveRecord
|
|
384
353
|
nil
|
385
354
|
end
|
386
355
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
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
|
398
370
|
end
|
399
371
|
|
400
372
|
# Renames a table.
|
@@ -405,94 +377,113 @@ module ActiveRecord
|
|
405
377
|
# rename_table('octopuses', 'octopi')
|
406
378
|
def rename_table(table_name, new_name)
|
407
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)
|
408
382
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
409
383
|
pk, seq = pk_and_sequence_for(new_name)
|
410
|
-
if
|
411
|
-
new_seq = "#{new_name}_#{pk}_seq"
|
384
|
+
if pk
|
412
385
|
idx = "#{table_name}_pkey"
|
413
386
|
new_idx = "#{new_name}_pkey"
|
414
|
-
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
|
415
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
|
416
392
|
end
|
417
|
-
|
418
393
|
rename_table_indexes(table_name, new_name)
|
419
394
|
end
|
420
395
|
|
421
|
-
|
422
|
-
# See TableDefinition#column for details of the options you can use.
|
423
|
-
def add_column(table_name, column_name, type, options = {})
|
396
|
+
def add_column(table_name, column_name, type, **options) #:nodoc:
|
424
397
|
clear_cache!
|
425
398
|
super
|
399
|
+
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
426
400
|
end
|
427
401
|
|
428
|
-
|
429
|
-
def change_column(table_name, column_name, type, options = {})
|
402
|
+
def change_column(table_name, column_name, type, **options) #:nodoc:
|
430
403
|
clear_cache!
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
|
435
|
-
sql << " USING #{options[:using]}" if options[:using]
|
436
|
-
if options[:cast_as]
|
437
|
-
sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})"
|
438
|
-
end
|
439
|
-
execute sql
|
440
|
-
|
441
|
-
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
442
|
-
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)
|
443
407
|
end
|
444
408
|
|
445
409
|
# Changes the default value of a table column.
|
446
|
-
def change_column_default(table_name, column_name,
|
447
|
-
|
448
|
-
column = column_for(table_name, column_name)
|
449
|
-
return unless column
|
450
|
-
|
451
|
-
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
452
|
-
if default.nil?
|
453
|
-
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
454
|
-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
455
|
-
execute alter_column_query % "DROP DEFAULT"
|
456
|
-
else
|
457
|
-
execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}"
|
458
|
-
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)}"
|
459
412
|
end
|
460
413
|
|
461
|
-
def change_column_null(table_name, column_name, null, default = nil)
|
414
|
+
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
462
415
|
clear_cache!
|
463
416
|
unless null || default.nil?
|
464
417
|
column = column_for(table_name, column_name)
|
465
|
-
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
|
466
419
|
end
|
467
|
-
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)}"
|
468
435
|
end
|
469
436
|
|
470
437
|
# Renames a column in a table.
|
471
|
-
def rename_column(table_name, column_name, new_column_name)
|
438
|
+
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
472
439
|
clear_cache!
|
473
|
-
execute
|
440
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
474
441
|
rename_column_indexes(table_name, column_name, new_column_name)
|
475
442
|
end
|
476
443
|
|
477
|
-
def add_index(table_name, column_name, options
|
478
|
-
|
479
|
-
|
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
|
480
452
|
end
|
481
453
|
|
482
|
-
def remove_index
|
483
|
-
|
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)}"
|
484
473
|
end
|
485
474
|
|
475
|
+
# Renames an index of a table. Raises error if length of new
|
476
|
+
# index name is greater than allowed limit.
|
486
477
|
def rename_index(table_name, old_name, new_name)
|
487
|
-
|
488
|
-
|
489
|
-
end
|
478
|
+
validate_index_length!(table_name, new_name)
|
479
|
+
|
490
480
|
execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
|
491
481
|
end
|
492
482
|
|
493
483
|
def foreign_keys(table_name)
|
494
|
-
|
495
|
-
|
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
|
496
487
|
FROM pg_constraint c
|
497
488
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
498
489
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
@@ -500,88 +491,303 @@ module ActiveRecord
|
|
500
491
|
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
501
492
|
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
502
493
|
WHERE c.contype = 'f'
|
503
|
-
AND t1.relname = #{
|
504
|
-
AND t3.nspname =
|
494
|
+
AND t1.relname = #{scope[:name]}
|
495
|
+
AND t3.nspname = #{scope[:schema]}
|
505
496
|
ORDER BY c.conname
|
506
497
|
SQL
|
507
498
|
|
508
499
|
fk_info.map do |row|
|
509
500
|
options = {
|
510
|
-
column: row[
|
511
|
-
name: row[
|
512
|
-
primary_key: row[
|
501
|
+
column: row["column"],
|
502
|
+
name: row["name"],
|
503
|
+
primary_key: row["primary_key"]
|
513
504
|
}
|
514
505
|
|
515
|
-
options[:on_delete] = extract_foreign_key_action(row[
|
516
|
-
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"]
|
517
509
|
|
518
|
-
ForeignKeyDefinition.new(table_name, row[
|
510
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
519
511
|
end
|
520
512
|
end
|
521
513
|
|
522
|
-
def
|
523
|
-
|
524
|
-
when 'c'; :cascade
|
525
|
-
when 'n'; :nullify
|
526
|
-
when 'r'; :restrict
|
527
|
-
end
|
514
|
+
def foreign_tables
|
515
|
+
query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
|
528
516
|
end
|
529
517
|
|
530
|
-
def
|
531
|
-
|
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
|
532
542
|
end
|
533
543
|
|
534
544
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
535
|
-
def type_to_sql(type, limit
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
when
|
556
|
-
when
|
557
|
-
|
558
|
-
|
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
|
559
571
|
end
|
560
|
-
when 'datetime'
|
561
|
-
return super unless precision
|
562
572
|
|
563
|
-
|
564
|
-
|
565
|
-
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
566
|
-
end
|
567
|
-
else
|
568
|
-
super
|
569
|
-
end
|
573
|
+
sql = "#{sql}[]" if array && type != :primary_key
|
574
|
+
sql
|
570
575
|
end
|
571
576
|
|
572
577
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
573
578
|
# requires that the ORDER BY include the distinct column.
|
574
579
|
def columns_for_distinct(columns, orders) #:nodoc:
|
575
|
-
order_columns = orders.
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
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
|
582
590
|
|
583
|
-
|
591
|
+
def update_table_definition(table_name, base) # :nodoc:
|
592
|
+
PostgreSQL::Table.new(table_name, base)
|
584
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
|
585
791
|
end
|
586
792
|
end
|
587
793
|
end
|