activerecord 5.0.6 → 6.0.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.
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
         |