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