activerecord 5.0.6 → 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +638 -2023
- data/MIT-LICENSE +3 -1
- data/README.rdoc +8 -6
- data/examples/performance.rb +31 -29
- data/examples/simple.rb +5 -3
- data/lib/active_record/aggregations.rb +249 -246
- data/lib/active_record/association_relation.rb +24 -13
- data/lib/active_record/associations/alias_tracker.rb +24 -33
- data/lib/active_record/associations/association.rb +119 -56
- data/lib/active_record/associations/association_scope.rb +94 -94
- data/lib/active_record/associations/belongs_to_association.rb +58 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -12
- data/lib/active_record/associations/builder/association.rb +18 -25
- data/lib/active_record/associations/builder/belongs_to.rb +43 -54
- data/lib/active_record/associations/builder/collection_association.rb +7 -18
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -61
- data/lib/active_record/associations/builder/has_many.rb +4 -0
- data/lib/active_record/associations/builder/has_one.rb +37 -1
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +80 -252
- data/lib/active_record/associations/collection_proxy.rb +158 -121
- data/lib/active_record/associations/foreign_association.rb +9 -0
- data/lib/active_record/associations/has_many_association.rb +23 -29
- data/lib/active_record/associations/has_many_through_association.rb +58 -44
- data/lib/active_record/associations/has_one_association.rb +59 -54
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +38 -90
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +134 -176
- data/lib/active_record/associations/preloader/association.rb +84 -125
- data/lib/active_record/associations/preloader/through_association.rb +82 -75
- data/lib/active_record/associations/preloader.rb +90 -102
- data/lib/active_record/associations/singular_association.rb +12 -45
- data/lib/active_record/associations/through_association.rb +26 -14
- data/lib/active_record/associations.rb +1603 -1592
- data/lib/active_record/attribute_assignment.rb +54 -60
- data/lib/active_record/attribute_decorators.rb +38 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +12 -7
- data/lib/active_record/attribute_methods/dirty.rb +179 -109
- data/lib/active_record/attribute_methods/primary_key.rb +86 -91
- data/lib/active_record/attribute_methods/query.rb +4 -3
- data/lib/active_record/attribute_methods/read.rb +21 -49
- data/lib/active_record/attribute_methods/serialization.rb +30 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -64
- data/lib/active_record/attribute_methods/write.rb +35 -33
- data/lib/active_record/attribute_methods.rb +66 -106
- data/lib/active_record/attributes.rb +38 -24
- data/lib/active_record/autosave_association.rb +53 -32
- data/lib/active_record/base.rb +27 -24
- data/lib/active_record/callbacks.rb +63 -33
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +11 -11
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +553 -321
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +23 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +213 -94
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +59 -28
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -75
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +33 -27
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -126
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +68 -80
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +369 -199
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +363 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +405 -551
- data/lib/active_record/connection_adapters/column.rb +41 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +172 -138
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -4
- data/lib/active_record/connection_adapters/mysql/column.rb +8 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +143 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +24 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -20
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +50 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +58 -56
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +70 -36
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +264 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +12 -13
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +49 -30
- data/lib/active_record/connection_adapters/postgresql/column.rb +22 -7
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +60 -54
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +22 -10
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +6 -5
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +4 -2
- 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 +19 -17
- 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 -5
- data/lib/active_record/connection_adapters/postgresql/oid/{json.rb → oid.rb} +6 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +31 -9
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +34 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +9 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +24 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +95 -35
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +147 -105
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +35 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +380 -300
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +26 -25
- data/lib/active_record/connection_adapters/postgresql/utils.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +382 -275
- data/lib/active_record/connection_adapters/schema_cache.rb +46 -12
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +74 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +3 -8
- 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 +254 -262
- data/lib/active_record/connection_adapters/statement_pool.rb +9 -7
- data/lib/active_record/connection_handling.rb +159 -40
- data/lib/active_record/core.rb +202 -162
- data/lib/active_record/counter_cache.rb +57 -28
- 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/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -86
- data/lib/active_record/enum.rb +60 -23
- data/lib/active_record/errors.rb +114 -18
- data/lib/active_record/explain.rb +4 -3
- data/lib/active_record/explain_registry.rb +3 -1
- data/lib/active_record/explain_subscriber.rb +9 -4
- data/lib/active_record/fixture_set/file.rb +13 -8
- 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 +195 -502
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +151 -97
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +116 -25
- data/lib/active_record/internal_metadata.rb +15 -18
- data/lib/active_record/legacy_yaml_adapter.rb +4 -2
- data/lib/active_record/locking/optimistic.rb +78 -87
- data/lib/active_record/locking/pessimistic.rb +18 -6
- data/lib/active_record/log_subscriber.rb +48 -29
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +143 -97
- data/lib/active_record/migration/compatibility.rb +174 -56
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +367 -300
- data/lib/active_record/model_schema.rb +145 -139
- data/lib/active_record/nested_attributes.rb +214 -201
- data/lib/active_record/no_touching.rb +10 -1
- data/lib/active_record/null_relation.rb +13 -34
- data/lib/active_record/persistence.rb +442 -72
- data/lib/active_record/query_cache.rb +15 -14
- data/lib/active_record/querying.rb +36 -23
- data/lib/active_record/railtie.rb +128 -36
- 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 +309 -177
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +211 -249
- data/lib/active_record/relation/batches/batch_enumerator.rb +3 -1
- data/lib/active_record/relation/batches.rb +99 -52
- data/lib/active_record/relation/calculations.rb +211 -172
- data/lib/active_record/relation/delegation.rb +67 -65
- data/lib/active_record/relation/finder_methods.rb +208 -247
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +78 -61
- data/lib/active_record/relation/predicate_builder/array_handler.rb +20 -14
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +43 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +6 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +53 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +7 -18
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/predicate_builder.rb +86 -104
- data/lib/active_record/relation/query_attribute.rb +33 -2
- data/lib/active_record/relation/query_methods.rb +458 -329
- data/lib/active_record/relation/record_fetch_warning.rb +5 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +111 -95
- data/lib/active_record/relation/where_clause_factory.rb +6 -11
- data/lib/active_record/relation.rb +429 -318
- data/lib/active_record/result.rb +69 -39
- data/lib/active_record/runtime_registry.rb +5 -3
- data/lib/active_record/sanitization.rb +83 -99
- data/lib/active_record/schema.rb +7 -14
- data/lib/active_record/schema_dumper.rb +71 -69
- data/lib/active_record/schema_migration.rb +15 -5
- data/lib/active_record/scoping/default.rb +93 -95
- data/lib/active_record/scoping/named.rb +45 -25
- data/lib/active_record/scoping.rb +20 -19
- data/lib/active_record/secure_token.rb +4 -2
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +63 -28
- data/lib/active_record/store.rb +121 -41
- data/lib/active_record/suppressor.rb +4 -1
- data/lib/active_record/table_metadata.rb +26 -20
- data/lib/active_record/tasks/database_tasks.rb +276 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +54 -90
- data/lib/active_record/tasks/postgresql_database_tasks.rb +78 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +34 -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 +70 -35
- data/lib/active_record/touch_later.rb +7 -4
- data/lib/active_record/transactions.rb +133 -149
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +44 -45
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +16 -8
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +2 -1
- data/lib/active_record/type/type_map.rb +13 -15
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +23 -17
- data/lib/active_record/type_caster/connection.rb +17 -12
- data/lib/active_record/type_caster/map.rb +5 -4
- data/lib/active_record/type_caster.rb +4 -2
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +3 -1
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +4 -2
- data/lib/active_record/validations/uniqueness.rb +29 -42
- data/lib/active_record/validations.rb +7 -4
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -22
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -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/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/nodes.rb +68 -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/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/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +58 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +1 -1
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +4 -2
- data/lib/rails/generators/active_record/migration.rb +17 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -29
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +10 -1
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +133 -50
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -20
- data/lib/active_record/attribute/user_provided_default.rb +0 -28
- data/lib/active_record/attribute.rb +0 -213
- data/lib/active_record/attribute_mutation_tracker.rb +0 -70
- data/lib/active_record/attribute_set/builder.rb +0 -130
- data/lib/active_record/attribute_set.rb +0 -110
- data/lib/active_record/collection_cache_key.rb +0 -50
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +0 -50
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -57
- data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,24 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
|
-
class SchemaCreation < AbstractAdapter::SchemaCreation
|
7
|
-
private
|
8
|
-
|
9
|
-
def visit_ColumnDefinition(o)
|
10
|
-
o.sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale, o.array)
|
11
|
-
super
|
12
|
-
end
|
13
|
-
|
14
|
-
def add_column_options!(sql, options)
|
15
|
-
if options[:collation]
|
16
|
-
sql << " COLLATE \"#{options[:collation]}\""
|
17
|
-
end
|
18
|
-
super
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
6
|
module SchemaStatements
|
23
7
|
# Drops the database specified on the +name+ attribute
|
24
8
|
# and creates it again using the provided +options+.
|
@@ -36,26 +20,26 @@ module ActiveRecord
|
|
36
20
|
# create_database config[:database], config
|
37
21
|
# create_database 'foo_development', encoding: 'unicode'
|
38
22
|
def create_database(name, options = {})
|
39
|
-
options = { encoding:
|
40
|
-
|
41
|
-
option_string = options.
|
42
|
-
memo
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
23
|
+
options = { encoding: "utf8" }.merge!(options.symbolize_keys)
|
24
|
+
|
25
|
+
option_string = options.each_with_object(+"") do |(key, value), memo|
|
26
|
+
memo << case key
|
27
|
+
when :owner
|
28
|
+
" OWNER = \"#{value}\""
|
29
|
+
when :template
|
30
|
+
" TEMPLATE = \"#{value}\""
|
31
|
+
when :encoding
|
32
|
+
" ENCODING = '#{value}'"
|
33
|
+
when :collation
|
34
|
+
" LC_COLLATE = '#{value}'"
|
35
|
+
when :ctype
|
36
|
+
" LC_CTYPE = '#{value}'"
|
37
|
+
when :tablespace
|
38
|
+
" TABLESPACE = \"#{value}\""
|
39
|
+
when :connection_limit
|
40
|
+
" CONNECTION LIMIT = #{value}"
|
41
|
+
else
|
42
|
+
""
|
59
43
|
end
|
60
44
|
end
|
61
45
|
|
@@ -70,123 +54,48 @@ module ActiveRecord
|
|
70
54
|
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
|
71
55
|
end
|
72
56
|
|
73
|
-
# Returns the list of all tables in the schema search path.
|
74
|
-
def tables(name = nil)
|
75
|
-
if name
|
76
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
77
|
-
Passing arguments to #tables is deprecated without replacement.
|
78
|
-
MSG
|
79
|
-
end
|
80
|
-
|
81
|
-
select_values("SELECT tablename FROM pg_tables WHERE schemaname = ANY(current_schemas(false))", 'SCHEMA')
|
82
|
-
end
|
83
|
-
|
84
|
-
def data_sources # :nodoc
|
85
|
-
select_values(<<-SQL, 'SCHEMA')
|
86
|
-
SELECT c.relname
|
87
|
-
FROM pg_class c
|
88
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
89
|
-
WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
90
|
-
AND n.nspname = ANY (current_schemas(false))
|
91
|
-
SQL
|
92
|
-
end
|
93
|
-
|
94
|
-
# Returns true if table exists.
|
95
|
-
# If the schema is not specified as part of +name+ then it will only find tables within
|
96
|
-
# the current schema search path (regardless of permissions to access tables in other schemas)
|
97
|
-
def table_exists?(name)
|
98
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
99
|
-
#table_exists? currently checks both tables and views.
|
100
|
-
This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
|
101
|
-
Use #data_source_exists? instead.
|
102
|
-
MSG
|
103
|
-
|
104
|
-
data_source_exists?(name)
|
105
|
-
end
|
106
|
-
|
107
|
-
def data_source_exists?(name)
|
108
|
-
name = Utils.extract_schema_qualified_name(name.to_s)
|
109
|
-
return false unless name.identifier
|
110
|
-
|
111
|
-
select_value(<<-SQL, 'SCHEMA').to_i > 0
|
112
|
-
SELECT COUNT(*)
|
113
|
-
FROM pg_class c
|
114
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
115
|
-
WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
|
116
|
-
AND c.relname = '#{name.identifier}'
|
117
|
-
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
118
|
-
SQL
|
119
|
-
end
|
120
|
-
|
121
|
-
def views # :nodoc:
|
122
|
-
select_values(<<-SQL, 'SCHEMA')
|
123
|
-
SELECT c.relname
|
124
|
-
FROM pg_class c
|
125
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
126
|
-
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
127
|
-
AND n.nspname = ANY (current_schemas(false))
|
128
|
-
SQL
|
129
|
-
end
|
130
|
-
|
131
|
-
def view_exists?(view_name) # :nodoc:
|
132
|
-
name = Utils.extract_schema_qualified_name(view_name.to_s)
|
133
|
-
return false unless name.identifier
|
134
|
-
|
135
|
-
select_values(<<-SQL, 'SCHEMA').any?
|
136
|
-
SELECT c.relname
|
137
|
-
FROM pg_class c
|
138
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
139
|
-
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
140
|
-
AND c.relname = '#{name.identifier}'
|
141
|
-
AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
|
142
|
-
SQL
|
143
|
-
end
|
144
|
-
|
145
57
|
def drop_table(table_name, options = {}) # :nodoc:
|
146
58
|
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
|
147
59
|
end
|
148
60
|
|
149
61
|
# Returns true if schema exists.
|
150
62
|
def schema_exists?(name)
|
151
|
-
|
63
|
+
query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
|
152
64
|
end
|
153
65
|
|
154
66
|
# Verifies existence of an index with a given name.
|
155
|
-
def index_name_exists?(table_name, index_name
|
156
|
-
table =
|
157
|
-
index =
|
67
|
+
def index_name_exists?(table_name, index_name)
|
68
|
+
table = quoted_scope(table_name)
|
69
|
+
index = quoted_scope(index_name)
|
158
70
|
|
159
|
-
|
71
|
+
query_value(<<~SQL, "SCHEMA").to_i > 0
|
160
72
|
SELECT COUNT(*)
|
161
73
|
FROM pg_class t
|
162
74
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
163
75
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
164
76
|
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
165
77
|
WHERE i.relkind = 'i'
|
166
|
-
AND i.relname =
|
167
|
-
AND t.relname =
|
168
|
-
AND n.nspname = #{index
|
78
|
+
AND i.relname = #{index[:name]}
|
79
|
+
AND t.relname = #{table[:name]}
|
80
|
+
AND n.nspname = #{index[:schema]}
|
169
81
|
SQL
|
170
82
|
end
|
171
83
|
|
172
84
|
# Returns an array of indexes for the given table.
|
173
|
-
def indexes(table_name
|
174
|
-
|
85
|
+
def indexes(table_name) # :nodoc:
|
86
|
+
scope = quoted_scope(table_name)
|
175
87
|
|
176
|
-
result = query(
|
88
|
+
result = query(<<~SQL, "SCHEMA")
|
177
89
|
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid,
|
178
|
-
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
179
|
-
(SELECT COUNT(*) FROM pg_opclass o
|
180
|
-
JOIN (SELECT unnest(string_to_array(d.indclass::text, ' '))::int oid) c
|
181
|
-
ON o.oid = c.oid WHERE o.opcdefault = 'f')
|
90
|
+
pg_catalog.obj_description(i.oid, 'pg_class') AS comment
|
182
91
|
FROM pg_class t
|
183
92
|
INNER JOIN pg_index d ON t.oid = d.indrelid
|
184
93
|
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
185
94
|
LEFT JOIN pg_namespace n ON n.oid = i.relnamespace
|
186
95
|
WHERE i.relkind = 'i'
|
187
96
|
AND d.indisprimary = 'f'
|
188
|
-
AND t.relname =
|
189
|
-
AND n.nspname = #{
|
97
|
+
AND t.relname = #{scope[:name]}
|
98
|
+
AND n.nspname = #{scope[:schema]}
|
190
99
|
ORDER BY i.relname
|
191
100
|
SQL
|
192
101
|
|
@@ -197,47 +106,48 @@ module ActiveRecord
|
|
197
106
|
inddef = row[3]
|
198
107
|
oid = row[4]
|
199
108
|
comment = row[5]
|
200
|
-
opclass = row[6]
|
201
109
|
|
202
|
-
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/).flatten
|
110
|
+
using, expressions, where = inddef.scan(/ USING (\w+?) \((.+?)\)(?: WHERE (.+))?\z/m).flatten
|
111
|
+
|
112
|
+
orders = {}
|
113
|
+
opclasses = {}
|
203
114
|
|
204
|
-
if indkey.include?(0)
|
115
|
+
if indkey.include?(0)
|
205
116
|
columns = expressions
|
206
117
|
else
|
207
|
-
columns = Hash[query(
|
118
|
+
columns = Hash[query(<<~SQL, "SCHEMA")].values_at(*indkey).compact
|
208
119
|
SELECT a.attnum, a.attname
|
209
120
|
FROM pg_attribute a
|
210
121
|
WHERE a.attrelid = #{oid}
|
211
122
|
AND a.attnum IN (#{indkey.join(",")})
|
212
123
|
SQL
|
213
124
|
|
214
|
-
# add info on sort order
|
215
|
-
|
216
|
-
|
217
|
-
|
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
|
218
135
|
end
|
219
136
|
|
220
|
-
IndexDefinition.new(
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
default_value = extract_value_from_default(default)
|
232
|
-
default_function = extract_default_function(default_value, default)
|
233
|
-
new_column(column_name, default_value, type_metadata, !notnull, table_name, default_function, collation, comment: comment.presence)
|
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
|
+
)
|
234
148
|
end
|
235
149
|
end
|
236
150
|
|
237
|
-
def new_column(*args) # :nodoc:
|
238
|
-
PostgreSQLColumn.new(*args)
|
239
|
-
end
|
240
|
-
|
241
151
|
def table_options(table_name) # :nodoc:
|
242
152
|
if comment = table_comment(table_name)
|
243
153
|
{ comment: comment }
|
@@ -246,47 +156,47 @@ module ActiveRecord
|
|
246
156
|
|
247
157
|
# Returns a comment stored in database for given table
|
248
158
|
def table_comment(table_name) # :nodoc:
|
249
|
-
|
250
|
-
if name
|
251
|
-
|
159
|
+
scope = quoted_scope(table_name, type: "BASE TABLE")
|
160
|
+
if scope[:name]
|
161
|
+
query_value(<<~SQL, "SCHEMA")
|
252
162
|
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
|
253
163
|
FROM pg_catalog.pg_class c
|
254
164
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
255
|
-
WHERE c.relname = #{
|
256
|
-
AND c.relkind IN (
|
257
|
-
AND n.nspname = #{
|
165
|
+
WHERE c.relname = #{scope[:name]}
|
166
|
+
AND c.relkind IN (#{scope[:type]})
|
167
|
+
AND n.nspname = #{scope[:schema]}
|
258
168
|
SQL
|
259
169
|
end
|
260
170
|
end
|
261
171
|
|
262
172
|
# Returns the current database name.
|
263
173
|
def current_database
|
264
|
-
|
174
|
+
query_value("SELECT current_database()", "SCHEMA")
|
265
175
|
end
|
266
176
|
|
267
177
|
# Returns the current schema name.
|
268
178
|
def current_schema
|
269
|
-
|
179
|
+
query_value("SELECT current_schema", "SCHEMA")
|
270
180
|
end
|
271
181
|
|
272
182
|
# Returns the current database encoding format.
|
273
183
|
def encoding
|
274
|
-
|
184
|
+
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
275
185
|
end
|
276
186
|
|
277
187
|
# Returns the current database collation.
|
278
188
|
def collation
|
279
|
-
|
189
|
+
query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
280
190
|
end
|
281
191
|
|
282
192
|
# Returns the current database ctype.
|
283
193
|
def ctype
|
284
|
-
|
194
|
+
query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
|
285
195
|
end
|
286
196
|
|
287
197
|
# Returns an array of schema names.
|
288
198
|
def schema_names
|
289
|
-
|
199
|
+
query_values(<<~SQL, "SCHEMA")
|
290
200
|
SELECT nspname
|
291
201
|
FROM pg_namespace
|
292
202
|
WHERE nspname !~ '^pg_.*'
|
@@ -296,7 +206,7 @@ module ActiveRecord
|
|
296
206
|
end
|
297
207
|
|
298
208
|
# Creates a schema for the given schema name.
|
299
|
-
def create_schema
|
209
|
+
def create_schema(schema_name)
|
300
210
|
execute "CREATE SCHEMA #{quote_schema_name(schema_name)}"
|
301
211
|
end
|
302
212
|
|
@@ -307,42 +217,42 @@ module ActiveRecord
|
|
307
217
|
|
308
218
|
# Sets the schema search path to a string of comma-separated schema names.
|
309
219
|
# Names beginning with $ have to be quoted (e.g. $user => '$user').
|
310
|
-
# See:
|
220
|
+
# See: https://www.postgresql.org/docs/current/static/ddl-schemas.html
|
311
221
|
#
|
312
222
|
# This should be not be called manually but set in database.yml.
|
313
223
|
def schema_search_path=(schema_csv)
|
314
224
|
if schema_csv
|
315
|
-
execute("SET search_path TO #{schema_csv}",
|
225
|
+
execute("SET search_path TO #{schema_csv}", "SCHEMA")
|
316
226
|
@schema_search_path = schema_csv
|
317
227
|
end
|
318
228
|
end
|
319
229
|
|
320
230
|
# Returns the active schema search path.
|
321
231
|
def schema_search_path
|
322
|
-
@schema_search_path ||=
|
232
|
+
@schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
|
323
233
|
end
|
324
234
|
|
325
235
|
# Returns the current client message level.
|
326
236
|
def client_min_messages
|
327
|
-
|
237
|
+
query_value("SHOW client_min_messages", "SCHEMA")
|
328
238
|
end
|
329
239
|
|
330
240
|
# Set the client message level.
|
331
241
|
def client_min_messages=(level)
|
332
|
-
execute("SET client_min_messages TO '#{level}'",
|
242
|
+
execute("SET client_min_messages TO '#{level}'", "SCHEMA")
|
333
243
|
end
|
334
244
|
|
335
245
|
# Returns the sequence name for a table's primary key or some other specified key.
|
336
|
-
def default_sequence_name(table_name, pk =
|
337
|
-
result = serial_sequence(table_name, pk
|
246
|
+
def default_sequence_name(table_name, pk = "id") #:nodoc:
|
247
|
+
result = serial_sequence(table_name, pk)
|
338
248
|
return nil unless result
|
339
249
|
Utils.extract_schema_qualified_name(result).to_s
|
340
250
|
rescue ActiveRecord::StatementInvalid
|
341
|
-
PostgreSQL::Name.new(nil, "#{table_name}_#{pk
|
251
|
+
PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
|
342
252
|
end
|
343
253
|
|
344
254
|
def serial_sequence(table, column)
|
345
|
-
|
255
|
+
query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
|
346
256
|
end
|
347
257
|
|
348
258
|
# Sets the sequence of a table's primary key to the specified value.
|
@@ -353,7 +263,7 @@ module ActiveRecord
|
|
353
263
|
if sequence
|
354
264
|
quoted_sequence = quote_table_name(sequence)
|
355
265
|
|
356
|
-
|
266
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
|
357
267
|
else
|
358
268
|
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
|
359
269
|
end
|
@@ -362,7 +272,7 @@ module ActiveRecord
|
|
362
272
|
|
363
273
|
# Resets the sequence of a table's primary key to the maximum value.
|
364
274
|
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
|
365
|
-
unless pk
|
275
|
+
unless pk && sequence
|
366
276
|
default_pk, default_sequence = pk_and_sequence_for(table)
|
367
277
|
|
368
278
|
pk ||= default_pk
|
@@ -375,18 +285,16 @@ module ActiveRecord
|
|
375
285
|
|
376
286
|
if pk && sequence
|
377
287
|
quoted_sequence = quote_table_name(sequence)
|
378
|
-
max_pk =
|
288
|
+
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
|
379
289
|
if max_pk.nil?
|
380
|
-
if
|
381
|
-
minvalue =
|
290
|
+
if database_version >= 100000
|
291
|
+
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
|
382
292
|
else
|
383
|
-
minvalue =
|
293
|
+
minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
|
384
294
|
end
|
385
295
|
end
|
386
296
|
|
387
|
-
|
388
|
-
SELECT setval('#{quoted_sequence}', #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})
|
389
|
-
end_sql
|
297
|
+
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
|
390
298
|
end
|
391
299
|
end
|
392
300
|
|
@@ -394,7 +302,7 @@ module ActiveRecord
|
|
394
302
|
def pk_and_sequence_for(table) #:nodoc:
|
395
303
|
# First try looking for a sequence with a dependency on the
|
396
304
|
# given table's primary key.
|
397
|
-
result = query(
|
305
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
398
306
|
SELECT attr.attname, nsp.nspname, seq.relname
|
399
307
|
FROM pg_class seq,
|
400
308
|
pg_attribute attr,
|
@@ -410,11 +318,11 @@ module ActiveRecord
|
|
410
318
|
AND seq.relnamespace = nsp.oid
|
411
319
|
AND cons.contype = 'p'
|
412
320
|
AND dep.classid = 'pg_class'::regclass
|
413
|
-
AND dep.refobjid =
|
414
|
-
|
321
|
+
AND dep.refobjid = #{quote(quote_table_name(table))}::regclass
|
322
|
+
SQL
|
415
323
|
|
416
|
-
if result.nil?
|
417
|
-
result = query(
|
324
|
+
if result.nil? || result.empty?
|
325
|
+
result = query(<<~SQL, "SCHEMA")[0]
|
418
326
|
SELECT attr.attname, nsp.nspname,
|
419
327
|
CASE
|
420
328
|
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
|
@@ -428,10 +336,10 @@ module ActiveRecord
|
|
428
336
|
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
|
429
337
|
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
|
430
338
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
431
|
-
WHERE t.oid =
|
339
|
+
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
432
340
|
AND cons.contype = 'p'
|
433
341
|
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
434
|
-
|
342
|
+
SQL
|
435
343
|
end
|
436
344
|
|
437
345
|
pk = result.shift
|
@@ -445,17 +353,18 @@ module ActiveRecord
|
|
445
353
|
end
|
446
354
|
|
447
355
|
def primary_keys(table_name) # :nodoc:
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
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
|
459
368
|
SQL
|
460
369
|
end
|
461
370
|
|
@@ -469,14 +378,15 @@ module ActiveRecord
|
|
469
378
|
clear_cache!
|
470
379
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
471
380
|
pk, seq = pk_and_sequence_for(new_name)
|
472
|
-
if
|
473
|
-
new_seq = "#{new_name}_#{pk}_seq"
|
381
|
+
if pk
|
474
382
|
idx = "#{table_name}_pkey"
|
475
383
|
new_idx = "#{new_name}_pkey"
|
476
|
-
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
477
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
|
478
389
|
end
|
479
|
-
|
480
390
|
rename_table_indexes(table_name, new_name)
|
481
391
|
end
|
482
392
|
|
@@ -488,61 +398,36 @@ module ActiveRecord
|
|
488
398
|
|
489
399
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
490
400
|
clear_cache!
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}"
|
495
|
-
if options[:collation]
|
496
|
-
sql << " COLLATE \"#{options[:collation]}\""
|
497
|
-
end
|
498
|
-
if options[:using]
|
499
|
-
sql << " USING #{options[:using]}"
|
500
|
-
elsif options[:cast_as]
|
501
|
-
cast_as_type = type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale], options[:array])
|
502
|
-
sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
503
|
-
end
|
504
|
-
execute sql
|
505
|
-
|
506
|
-
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
|
507
|
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
508
|
-
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
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)
|
509
404
|
end
|
510
405
|
|
511
406
|
# Changes the default value of a table column.
|
512
407
|
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
|
513
|
-
|
514
|
-
column = column_for(table_name, column_name)
|
515
|
-
return unless column
|
516
|
-
|
517
|
-
default = extract_new_default_value(default_or_changes)
|
518
|
-
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
|
519
|
-
if default.nil?
|
520
|
-
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will
|
521
|
-
# cast the default to the columns type, which leaves us with a default like "default NULL::character varying".
|
522
|
-
execute alter_column_query % "DROP DEFAULT"
|
523
|
-
else
|
524
|
-
execute alter_column_query % "SET DEFAULT #{quote_default_expression(default, column)}"
|
525
|
-
end
|
408
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_default_for_alter(table_name, column_name, default_or_changes)}"
|
526
409
|
end
|
527
410
|
|
528
411
|
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
529
412
|
clear_cache!
|
530
413
|
unless null || default.nil?
|
531
414
|
column = column_for(table_name, column_name)
|
532
|
-
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
|
533
416
|
end
|
534
|
-
execute
|
417
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_null_for_alter(table_name, column_name, null, default)}"
|
535
418
|
end
|
536
419
|
|
537
420
|
# Adds comment for given table column or drops it if +comment+ is a +nil+
|
538
|
-
def change_column_comment(table_name, column_name,
|
421
|
+
def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc:
|
539
422
|
clear_cache!
|
423
|
+
comment = extract_new_comment_value(comment_or_changes)
|
540
424
|
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}"
|
541
425
|
end
|
542
426
|
|
543
427
|
# Adds comment for given table or drops it if +comment+ is a +nil+
|
544
|
-
def change_table_comment(table_name,
|
428
|
+
def change_table_comment(table_name, comment_or_changes) # :nodoc:
|
545
429
|
clear_cache!
|
430
|
+
comment = extract_new_comment_value(comment_or_changes)
|
546
431
|
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}"
|
547
432
|
end
|
548
433
|
|
@@ -554,8 +439,8 @@ module ActiveRecord
|
|
554
439
|
end
|
555
440
|
|
556
441
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
557
|
-
index_name, index_type,
|
558
|
-
execute("CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{
|
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
|
559
444
|
execute "COMMENT ON INDEX #{quote_column_name(index_name)} IS #{quote(comment)}" if comment
|
560
445
|
end
|
561
446
|
end
|
@@ -593,8 +478,9 @@ module ActiveRecord
|
|
593
478
|
end
|
594
479
|
|
595
480
|
def foreign_keys(table_name)
|
596
|
-
|
597
|
-
|
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
|
598
484
|
FROM pg_constraint c
|
599
485
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
600
486
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
@@ -602,94 +488,288 @@ module ActiveRecord
|
|
602
488
|
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
603
489
|
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
604
490
|
WHERE c.contype = 'f'
|
605
|
-
AND t1.relname = #{
|
606
|
-
AND t3.nspname =
|
491
|
+
AND t1.relname = #{scope[:name]}
|
492
|
+
AND t3.nspname = #{scope[:schema]}
|
607
493
|
ORDER BY c.conname
|
608
494
|
SQL
|
609
495
|
|
610
496
|
fk_info.map do |row|
|
611
497
|
options = {
|
612
|
-
column: row[
|
613
|
-
name: row[
|
614
|
-
primary_key: row[
|
498
|
+
column: row["column"],
|
499
|
+
name: row["name"],
|
500
|
+
primary_key: row["primary_key"]
|
615
501
|
}
|
616
502
|
|
617
|
-
options[:on_delete] = extract_foreign_key_action(row[
|
618
|
-
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"]
|
619
506
|
|
620
|
-
ForeignKeyDefinition.new(table_name, row[
|
507
|
+
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
621
508
|
end
|
622
509
|
end
|
623
510
|
|
624
|
-
def
|
625
|
-
|
626
|
-
when 'c'; :cascade
|
627
|
-
when 'n'; :nullify
|
628
|
-
when 'r'; :restrict
|
629
|
-
end
|
511
|
+
def foreign_tables
|
512
|
+
query_values(data_source_sql(type: "FOREIGN TABLE"), "SCHEMA")
|
630
513
|
end
|
631
514
|
|
632
|
-
def
|
633
|
-
|
515
|
+
def foreign_table_exists?(table_name)
|
516
|
+
query_values(data_source_sql(table_name, type: "FOREIGN TABLE"), "SCHEMA").any? if table_name.present?
|
634
517
|
end
|
635
518
|
|
636
519
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
637
|
-
def type_to_sql(type, limit
|
638
|
-
sql =
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
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
|
659
546
|
end
|
660
|
-
else
|
661
|
-
super(type, limit, precision, scale)
|
662
|
-
end
|
663
547
|
|
664
|
-
sql
|
548
|
+
sql = "#{sql}[]" if array && type != :primary_key
|
665
549
|
sql
|
666
550
|
end
|
667
551
|
|
668
552
|
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
|
669
553
|
# requires that the ORDER BY include the distinct column.
|
670
554
|
def columns_for_distinct(columns, orders) #:nodoc:
|
671
|
-
order_columns = orders.reject(&:blank?).map{ |s|
|
555
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
672
556
|
# Convert Arel node to string
|
673
557
|
s = s.to_sql unless s.is_a?(String)
|
674
558
|
# Remove any ASC/DESC modifiers
|
675
|
-
s.gsub(/\s+(?:ASC|DESC)\b/i,
|
676
|
-
.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, "")
|
677
561
|
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
|
678
562
|
|
679
|
-
|
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)
|
680
586
|
end
|
681
587
|
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
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
|
692
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
|
693
773
|
end
|
694
774
|
end
|
695
775
|
end
|