activerecord 5.2.8 → 7.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1393 -587
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +10 -9
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +122 -47
- data/lib/active_record/associations/association_scope.rb +24 -24
- data/lib/active_record/associations/belongs_to_association.rb +67 -49
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -7
- data/lib/active_record/associations/builder/association.rb +52 -23
- data/lib/active_record/associations/builder/belongs_to.rb +44 -61
- data/lib/active_record/associations/builder/collection_association.rb +17 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +10 -3
- data/lib/active_record/associations/builder/has_one.rb +35 -3
- data/lib/active_record/associations/builder/singular_association.rb +5 -3
- data/lib/active_record/associations/collection_association.rb +59 -50
- data/lib/active_record/associations/collection_proxy.rb +32 -23
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +27 -14
- data/lib/active_record/associations/has_many_through_association.rb +26 -19
- data/lib/active_record/associations/has_one_association.rb +52 -37
- data/lib/active_record/associations/has_one_through_association.rb +6 -6
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +97 -62
- data/lib/active_record/associations/preloader/association.rb +220 -60
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +85 -40
- data/lib/active_record/associations/preloader.rb +44 -105
- data/lib/active_record/associations/singular_association.rb +9 -17
- data/lib/active_record/associations/through_association.rb +4 -4
- data/lib/active_record/associations.rb +207 -66
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods/before_type_cast.rb +19 -8
- data/lib/active_record/attribute_methods/dirty.rb +141 -47
- data/lib/active_record/attribute_methods/primary_key.rb +22 -27
- data/lib/active_record/attribute_methods/query.rb +6 -10
- data/lib/active_record/attribute_methods/read.rb +15 -55
- data/lib/active_record/attribute_methods/serialization.rb +77 -18
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +16 -18
- data/lib/active_record/attribute_methods/write.rb +18 -37
- data/lib/active_record/attribute_methods.rb +90 -153
- data/lib/active_record/attributes.rb +38 -12
- data/lib/active_record/autosave_association.rb +50 -50
- data/lib/active_record/base.rb +23 -18
- data/lib/active_record/callbacks.rb +159 -44
- data/lib/active_record/coders/yaml_column.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +92 -464
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +209 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +38 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +103 -82
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +140 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -94
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +16 -5
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +456 -159
- data/lib/active_record/connection_adapters/abstract/transaction.rb +169 -78
- data/lib/active_record/connection_adapters/abstract_adapter.rb +367 -162
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +311 -327
- data/lib/active_record/connection_adapters/column.rb +33 -11
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +113 -45
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +71 -5
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +25 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +143 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +63 -22
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +53 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +56 -63
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +54 -16
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +26 -12
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +128 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +149 -113
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +386 -182
- data/lib/active_record/connection_adapters/schema_cache.rb +161 -22
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +65 -18
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +92 -26
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +251 -204
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +53 -0
- data/lib/active_record/connection_handling.rb +292 -38
- data/lib/active_record/core.rb +385 -158
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations/connection_url_resolver.rb +100 -0
- data/lib/active_record/database_configurations/database_config.rb +83 -0
- data/lib/active_record/database_configurations/hash_config.rb +154 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +256 -0
- data/lib/active_record/delegated_type.rb +250 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +4 -5
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +130 -51
- data/lib/active_record/errors.rb +129 -23
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +187 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +206 -490
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +104 -37
- data/lib/active_record/insert_all.rb +278 -0
- data/lib/active_record/integration.rb +69 -18
- data/lib/active_record/internal_metadata.rb +24 -9
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +41 -26
- data/lib/active_record/locking/pessimistic.rb +18 -8
- data/lib/active_record/log_subscriber.rb +46 -35
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector.rb +82 -0
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +246 -64
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration.rb +266 -187
- data/lib/active_record/model_schema.rb +165 -52
- data/lib/active_record/nested_attributes.rb +17 -19
- data/lib/active_record/no_touching.rb +11 -4
- data/lib/active_record/null_relation.rb +2 -7
- data/lib/active_record/persistence.rb +467 -92
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +51 -24
- data/lib/active_record/railtie.rb +224 -57
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +31 -36
- data/lib/active_record/railties/databases.rake +369 -101
- data/lib/active_record/readonly_attributes.rb +15 -0
- data/lib/active_record/reflection.rb +170 -137
- data/lib/active_record/relation/batches/batch_enumerator.rb +44 -14
- data/lib/active_record/relation/batches.rb +46 -37
- data/lib/active_record/relation/calculations.rb +168 -96
- data/lib/active_record/relation/delegation.rb +37 -52
- data/lib/active_record/relation/finder_methods.rb +79 -58
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +50 -51
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +58 -46
- data/lib/active_record/relation/query_attribute.rb +9 -10
- data/lib/active_record/relation/query_methods.rb +685 -208
- data/lib/active_record/relation/record_fetch_warning.rb +9 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -10
- data/lib/active_record/relation/where_clause.rb +108 -64
- data/lib/active_record/relation.rb +515 -151
- data/lib/active_record/result.rb +78 -42
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +29 -44
- data/lib/active_record/schema.rb +37 -31
- data/lib/active_record/schema_dumper.rb +74 -23
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping/default.rb +62 -17
- data/lib/active_record/scoping/named.rb +17 -32
- data/lib/active_record/scoping.rb +70 -41
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +6 -4
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +13 -17
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +352 -94
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +41 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +44 -34
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +67 -128
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +34 -19
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +7 -4
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +9 -5
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations/associated.rb +2 -3
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +39 -31
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record.rb +209 -32
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +48 -0
- data/lib/arel/delete_manager.rb +32 -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/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +44 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +46 -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 +71 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +258 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +117 -0
- data/lib/arel/tree_manager.rb +60 -0
- data/lib/arel/update_manager.rb +48 -0
- data/lib/arel/visitors/dot.rb +298 -0
- data/lib/arel/visitors/mysql.rb +99 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +955 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +55 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +162 -32
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -0,0 +1,955 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arel # :nodoc: all
|
4
|
+
module Visitors
|
5
|
+
class UnsupportedVisitError < StandardError
|
6
|
+
def initialize(object)
|
7
|
+
super "Unsupported argument type: #{object.class.name}. Construct an Arel node instead."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ToSql < Arel::Visitors::Visitor
|
12
|
+
def initialize(connection)
|
13
|
+
super()
|
14
|
+
@connection = connection
|
15
|
+
end
|
16
|
+
|
17
|
+
def compile(node, collector = Arel::Collectors::SQLString.new)
|
18
|
+
accept(node, collector).value
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def visit_Arel_Nodes_DeleteStatement(o, collector)
|
23
|
+
o = prepare_delete_statement(o)
|
24
|
+
|
25
|
+
if has_join_sources?(o)
|
26
|
+
collector << "DELETE "
|
27
|
+
visit o.relation.left, collector
|
28
|
+
collector << " FROM "
|
29
|
+
else
|
30
|
+
collector << "DELETE FROM "
|
31
|
+
end
|
32
|
+
collector = visit o.relation, collector
|
33
|
+
|
34
|
+
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
35
|
+
collect_nodes_for o.orders, collector, " ORDER BY "
|
36
|
+
maybe_visit o.limit, collector
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit_Arel_Nodes_UpdateStatement(o, collector)
|
40
|
+
o = prepare_update_statement(o)
|
41
|
+
|
42
|
+
collector << "UPDATE "
|
43
|
+
collector = visit o.relation, collector
|
44
|
+
collect_nodes_for o.values, collector, " SET "
|
45
|
+
|
46
|
+
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
47
|
+
collect_nodes_for o.orders, collector, " ORDER BY "
|
48
|
+
maybe_visit o.limit, collector
|
49
|
+
end
|
50
|
+
|
51
|
+
def visit_Arel_Nodes_InsertStatement(o, collector)
|
52
|
+
collector << "INSERT INTO "
|
53
|
+
collector = visit o.relation, collector
|
54
|
+
|
55
|
+
unless o.columns.empty?
|
56
|
+
collector << " ("
|
57
|
+
o.columns.each_with_index do |x, i|
|
58
|
+
collector << ", " unless i == 0
|
59
|
+
collector << quote_column_name(x.name)
|
60
|
+
end
|
61
|
+
collector << ")"
|
62
|
+
end
|
63
|
+
|
64
|
+
if o.values
|
65
|
+
maybe_visit o.values, collector
|
66
|
+
elsif o.select
|
67
|
+
maybe_visit o.select, collector
|
68
|
+
else
|
69
|
+
collector
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def visit_Arel_Nodes_Exists(o, collector)
|
74
|
+
collector << "EXISTS ("
|
75
|
+
collector = visit(o.expressions, collector) << ")"
|
76
|
+
if o.alias
|
77
|
+
collector << " AS "
|
78
|
+
visit o.alias, collector
|
79
|
+
else
|
80
|
+
collector
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def visit_Arel_Nodes_Casted(o, collector)
|
85
|
+
collector << quote(o.value_for_database).to_s
|
86
|
+
end
|
87
|
+
alias :visit_Arel_Nodes_Quoted :visit_Arel_Nodes_Casted
|
88
|
+
|
89
|
+
def visit_Arel_Nodes_True(o, collector)
|
90
|
+
collector << "TRUE"
|
91
|
+
end
|
92
|
+
|
93
|
+
def visit_Arel_Nodes_False(o, collector)
|
94
|
+
collector << "FALSE"
|
95
|
+
end
|
96
|
+
|
97
|
+
def visit_Arel_Nodes_ValuesList(o, collector)
|
98
|
+
collector << "VALUES "
|
99
|
+
|
100
|
+
o.rows.each_with_index do |row, i|
|
101
|
+
collector << ", " unless i == 0
|
102
|
+
collector << "("
|
103
|
+
row.each_with_index do |value, k|
|
104
|
+
collector << ", " unless k == 0
|
105
|
+
case value
|
106
|
+
when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
|
107
|
+
collector = visit(value, collector)
|
108
|
+
else
|
109
|
+
collector << quote(value).to_s
|
110
|
+
end
|
111
|
+
end
|
112
|
+
collector << ")"
|
113
|
+
end
|
114
|
+
collector
|
115
|
+
end
|
116
|
+
|
117
|
+
def visit_Arel_Nodes_SelectStatement(o, collector)
|
118
|
+
if o.with
|
119
|
+
collector = visit o.with, collector
|
120
|
+
collector << " "
|
121
|
+
end
|
122
|
+
|
123
|
+
collector = o.cores.inject(collector) { |c, x|
|
124
|
+
visit_Arel_Nodes_SelectCore(x, c)
|
125
|
+
}
|
126
|
+
|
127
|
+
unless o.orders.empty?
|
128
|
+
collector << " ORDER BY "
|
129
|
+
o.orders.each_with_index do |x, i|
|
130
|
+
collector << ", " unless i == 0
|
131
|
+
collector = visit(x, collector)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
visit_Arel_Nodes_SelectOptions(o, collector)
|
136
|
+
end
|
137
|
+
|
138
|
+
# The Oracle enhanced adapter uses this private method,
|
139
|
+
# see https://github.com/rsim/oracle-enhanced/issues/2186
|
140
|
+
def visit_Arel_Nodes_SelectOptions(o, collector)
|
141
|
+
collector = maybe_visit o.limit, collector
|
142
|
+
collector = maybe_visit o.offset, collector
|
143
|
+
maybe_visit o.lock, collector
|
144
|
+
end
|
145
|
+
|
146
|
+
def visit_Arel_Nodes_SelectCore(o, collector)
|
147
|
+
collector << "SELECT"
|
148
|
+
|
149
|
+
collector = collect_optimizer_hints(o, collector)
|
150
|
+
collector = maybe_visit o.set_quantifier, collector
|
151
|
+
|
152
|
+
collect_nodes_for o.projections, collector, " "
|
153
|
+
|
154
|
+
if o.source && !o.source.empty?
|
155
|
+
collector << " FROM "
|
156
|
+
collector = visit o.source, collector
|
157
|
+
end
|
158
|
+
|
159
|
+
collect_nodes_for o.wheres, collector, " WHERE ", " AND "
|
160
|
+
collect_nodes_for o.groups, collector, " GROUP BY "
|
161
|
+
collect_nodes_for o.havings, collector, " HAVING ", " AND "
|
162
|
+
collect_nodes_for o.windows, collector, " WINDOW "
|
163
|
+
|
164
|
+
maybe_visit o.comment, collector
|
165
|
+
end
|
166
|
+
|
167
|
+
def visit_Arel_Nodes_OptimizerHints(o, collector)
|
168
|
+
hints = o.expr.map { |v| sanitize_as_sql_comment(v) }.join(" ")
|
169
|
+
collector << "/*+ #{hints} */"
|
170
|
+
end
|
171
|
+
|
172
|
+
def visit_Arel_Nodes_Comment(o, collector)
|
173
|
+
collector << o.values.map { |v| "/* #{sanitize_as_sql_comment(v)} */" }.join(" ")
|
174
|
+
end
|
175
|
+
|
176
|
+
def collect_nodes_for(nodes, collector, spacer, connector = ", ")
|
177
|
+
unless nodes.empty?
|
178
|
+
collector << spacer
|
179
|
+
inject_join nodes, collector, connector
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def visit_Arel_Nodes_Bin(o, collector)
|
184
|
+
visit o.expr, collector
|
185
|
+
end
|
186
|
+
|
187
|
+
def visit_Arel_Nodes_Distinct(o, collector)
|
188
|
+
collector << "DISTINCT"
|
189
|
+
end
|
190
|
+
|
191
|
+
def visit_Arel_Nodes_DistinctOn(o, collector)
|
192
|
+
raise NotImplementedError, "DISTINCT ON not implemented for this db"
|
193
|
+
end
|
194
|
+
|
195
|
+
def visit_Arel_Nodes_With(o, collector)
|
196
|
+
collector << "WITH "
|
197
|
+
collect_ctes(o.children, collector)
|
198
|
+
end
|
199
|
+
|
200
|
+
def visit_Arel_Nodes_WithRecursive(o, collector)
|
201
|
+
collector << "WITH RECURSIVE "
|
202
|
+
collect_ctes(o.children, collector)
|
203
|
+
end
|
204
|
+
|
205
|
+
def visit_Arel_Nodes_Union(o, collector)
|
206
|
+
infix_value_with_paren(o, collector, " UNION ")
|
207
|
+
end
|
208
|
+
|
209
|
+
def visit_Arel_Nodes_UnionAll(o, collector)
|
210
|
+
infix_value_with_paren(o, collector, " UNION ALL ")
|
211
|
+
end
|
212
|
+
|
213
|
+
def visit_Arel_Nodes_Intersect(o, collector)
|
214
|
+
collector << "( "
|
215
|
+
infix_value(o, collector, " INTERSECT ") << " )"
|
216
|
+
end
|
217
|
+
|
218
|
+
def visit_Arel_Nodes_Except(o, collector)
|
219
|
+
collector << "( "
|
220
|
+
infix_value(o, collector, " EXCEPT ") << " )"
|
221
|
+
end
|
222
|
+
|
223
|
+
def visit_Arel_Nodes_NamedWindow(o, collector)
|
224
|
+
collector << quote_column_name(o.name)
|
225
|
+
collector << " AS "
|
226
|
+
visit_Arel_Nodes_Window o, collector
|
227
|
+
end
|
228
|
+
|
229
|
+
def visit_Arel_Nodes_Window(o, collector)
|
230
|
+
collector << "("
|
231
|
+
|
232
|
+
collect_nodes_for o.partitions, collector, "PARTITION BY "
|
233
|
+
|
234
|
+
if o.orders.any?
|
235
|
+
collector << " " if o.partitions.any?
|
236
|
+
collector << "ORDER BY "
|
237
|
+
collector = inject_join o.orders, collector, ", "
|
238
|
+
end
|
239
|
+
|
240
|
+
if o.framing
|
241
|
+
collector << " " if o.partitions.any? || o.orders.any?
|
242
|
+
collector = visit o.framing, collector
|
243
|
+
end
|
244
|
+
|
245
|
+
collector << ")"
|
246
|
+
end
|
247
|
+
|
248
|
+
def visit_Arel_Nodes_Filter(o, collector)
|
249
|
+
visit o.left, collector
|
250
|
+
collector << " FILTER (WHERE "
|
251
|
+
visit o.right, collector
|
252
|
+
collector << ")"
|
253
|
+
end
|
254
|
+
|
255
|
+
def visit_Arel_Nodes_Rows(o, collector)
|
256
|
+
if o.expr
|
257
|
+
collector << "ROWS "
|
258
|
+
visit o.expr, collector
|
259
|
+
else
|
260
|
+
collector << "ROWS"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def visit_Arel_Nodes_Range(o, collector)
|
265
|
+
if o.expr
|
266
|
+
collector << "RANGE "
|
267
|
+
visit o.expr, collector
|
268
|
+
else
|
269
|
+
collector << "RANGE"
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def visit_Arel_Nodes_Preceding(o, collector)
|
274
|
+
collector = if o.expr
|
275
|
+
visit o.expr, collector
|
276
|
+
else
|
277
|
+
collector << "UNBOUNDED"
|
278
|
+
end
|
279
|
+
|
280
|
+
collector << " PRECEDING"
|
281
|
+
end
|
282
|
+
|
283
|
+
def visit_Arel_Nodes_Following(o, collector)
|
284
|
+
collector = if o.expr
|
285
|
+
visit o.expr, collector
|
286
|
+
else
|
287
|
+
collector << "UNBOUNDED"
|
288
|
+
end
|
289
|
+
|
290
|
+
collector << " FOLLOWING"
|
291
|
+
end
|
292
|
+
|
293
|
+
def visit_Arel_Nodes_CurrentRow(o, collector)
|
294
|
+
collector << "CURRENT ROW"
|
295
|
+
end
|
296
|
+
|
297
|
+
def visit_Arel_Nodes_Over(o, collector)
|
298
|
+
case o.right
|
299
|
+
when nil
|
300
|
+
visit(o.left, collector) << " OVER ()"
|
301
|
+
when Arel::Nodes::SqlLiteral
|
302
|
+
infix_value o, collector, " OVER "
|
303
|
+
when String, Symbol
|
304
|
+
visit(o.left, collector) << " OVER #{quote_column_name o.right.to_s}"
|
305
|
+
else
|
306
|
+
infix_value o, collector, " OVER "
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def visit_Arel_Nodes_Offset(o, collector)
|
311
|
+
collector << "OFFSET "
|
312
|
+
visit o.expr, collector
|
313
|
+
end
|
314
|
+
|
315
|
+
def visit_Arel_Nodes_Limit(o, collector)
|
316
|
+
collector << "LIMIT "
|
317
|
+
visit o.expr, collector
|
318
|
+
end
|
319
|
+
|
320
|
+
def visit_Arel_Nodes_Lock(o, collector)
|
321
|
+
visit o.expr, collector
|
322
|
+
end
|
323
|
+
|
324
|
+
def visit_Arel_Nodes_Grouping(o, collector)
|
325
|
+
if o.expr.is_a? Nodes::Grouping
|
326
|
+
visit(o.expr, collector)
|
327
|
+
else
|
328
|
+
collector << "("
|
329
|
+
visit(o.expr, collector) << ")"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def visit_Arel_Nodes_HomogeneousIn(o, collector)
|
334
|
+
collector.preparable = false
|
335
|
+
|
336
|
+
collector << quote_table_name(o.table_name) << "." << quote_column_name(o.column_name)
|
337
|
+
|
338
|
+
if o.type == :in
|
339
|
+
collector << " IN ("
|
340
|
+
else
|
341
|
+
collector << " NOT IN ("
|
342
|
+
end
|
343
|
+
|
344
|
+
values = o.casted_values
|
345
|
+
|
346
|
+
if values.empty?
|
347
|
+
collector << @connection.quote(nil)
|
348
|
+
else
|
349
|
+
collector.add_binds(values, o.proc_for_binds, &bind_block)
|
350
|
+
end
|
351
|
+
|
352
|
+
collector << ")"
|
353
|
+
collector
|
354
|
+
end
|
355
|
+
|
356
|
+
def visit_Arel_SelectManager(o, collector)
|
357
|
+
collector << "("
|
358
|
+
visit(o.ast, collector) << ")"
|
359
|
+
end
|
360
|
+
|
361
|
+
def visit_Arel_Nodes_Ascending(o, collector)
|
362
|
+
visit(o.expr, collector) << " ASC"
|
363
|
+
end
|
364
|
+
|
365
|
+
def visit_Arel_Nodes_Descending(o, collector)
|
366
|
+
visit(o.expr, collector) << " DESC"
|
367
|
+
end
|
368
|
+
|
369
|
+
# NullsFirst is available on all but MySQL, where it is redefined.
|
370
|
+
def visit_Arel_Nodes_NullsFirst(o, collector)
|
371
|
+
visit o.expr, collector
|
372
|
+
collector << " NULLS FIRST"
|
373
|
+
end
|
374
|
+
|
375
|
+
def visit_Arel_Nodes_NullsLast(o, collector)
|
376
|
+
visit o.expr, collector
|
377
|
+
collector << " NULLS LAST"
|
378
|
+
end
|
379
|
+
|
380
|
+
def visit_Arel_Nodes_Group(o, collector)
|
381
|
+
visit o.expr, collector
|
382
|
+
end
|
383
|
+
|
384
|
+
def visit_Arel_Nodes_NamedFunction(o, collector)
|
385
|
+
collector << o.name
|
386
|
+
collector << "("
|
387
|
+
collector << "DISTINCT " if o.distinct
|
388
|
+
collector = inject_join(o.expressions, collector, ", ") << ")"
|
389
|
+
if o.alias
|
390
|
+
collector << " AS "
|
391
|
+
visit o.alias, collector
|
392
|
+
else
|
393
|
+
collector
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def visit_Arel_Nodes_Extract(o, collector)
|
398
|
+
collector << "EXTRACT(#{o.field.to_s.upcase} FROM "
|
399
|
+
visit(o.expr, collector) << ")"
|
400
|
+
end
|
401
|
+
|
402
|
+
def visit_Arel_Nodes_Count(o, collector)
|
403
|
+
aggregate "COUNT", o, collector
|
404
|
+
end
|
405
|
+
|
406
|
+
def visit_Arel_Nodes_Sum(o, collector)
|
407
|
+
aggregate "SUM", o, collector
|
408
|
+
end
|
409
|
+
|
410
|
+
def visit_Arel_Nodes_Max(o, collector)
|
411
|
+
aggregate "MAX", o, collector
|
412
|
+
end
|
413
|
+
|
414
|
+
def visit_Arel_Nodes_Min(o, collector)
|
415
|
+
aggregate "MIN", o, collector
|
416
|
+
end
|
417
|
+
|
418
|
+
def visit_Arel_Nodes_Avg(o, collector)
|
419
|
+
aggregate "AVG", o, collector
|
420
|
+
end
|
421
|
+
|
422
|
+
def visit_Arel_Nodes_TableAlias(o, collector)
|
423
|
+
collector = visit o.relation, collector
|
424
|
+
collector << " "
|
425
|
+
collector << quote_table_name(o.name)
|
426
|
+
end
|
427
|
+
|
428
|
+
def visit_Arel_Nodes_Between(o, collector)
|
429
|
+
collector = visit o.left, collector
|
430
|
+
collector << " BETWEEN "
|
431
|
+
visit o.right, collector
|
432
|
+
end
|
433
|
+
|
434
|
+
def visit_Arel_Nodes_GreaterThanOrEqual(o, collector)
|
435
|
+
case unboundable?(o.right)
|
436
|
+
when 1
|
437
|
+
return collector << "1=0"
|
438
|
+
when -1
|
439
|
+
return collector << "1=1"
|
440
|
+
end
|
441
|
+
collector = visit o.left, collector
|
442
|
+
collector << " >= "
|
443
|
+
visit o.right, collector
|
444
|
+
end
|
445
|
+
|
446
|
+
def visit_Arel_Nodes_GreaterThan(o, collector)
|
447
|
+
case unboundable?(o.right)
|
448
|
+
when 1
|
449
|
+
return collector << "1=0"
|
450
|
+
when -1
|
451
|
+
return collector << "1=1"
|
452
|
+
end
|
453
|
+
collector = visit o.left, collector
|
454
|
+
collector << " > "
|
455
|
+
visit o.right, collector
|
456
|
+
end
|
457
|
+
|
458
|
+
def visit_Arel_Nodes_LessThanOrEqual(o, collector)
|
459
|
+
case unboundable?(o.right)
|
460
|
+
when 1
|
461
|
+
return collector << "1=1"
|
462
|
+
when -1
|
463
|
+
return collector << "1=0"
|
464
|
+
end
|
465
|
+
collector = visit o.left, collector
|
466
|
+
collector << " <= "
|
467
|
+
visit o.right, collector
|
468
|
+
end
|
469
|
+
|
470
|
+
def visit_Arel_Nodes_LessThan(o, collector)
|
471
|
+
case unboundable?(o.right)
|
472
|
+
when 1
|
473
|
+
return collector << "1=1"
|
474
|
+
when -1
|
475
|
+
return collector << "1=0"
|
476
|
+
end
|
477
|
+
collector = visit o.left, collector
|
478
|
+
collector << " < "
|
479
|
+
visit o.right, collector
|
480
|
+
end
|
481
|
+
|
482
|
+
def visit_Arel_Nodes_Matches(o, collector)
|
483
|
+
collector = visit o.left, collector
|
484
|
+
collector << " LIKE "
|
485
|
+
collector = visit o.right, collector
|
486
|
+
if o.escape
|
487
|
+
collector << " ESCAPE "
|
488
|
+
visit o.escape, collector
|
489
|
+
else
|
490
|
+
collector
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
def visit_Arel_Nodes_DoesNotMatch(o, collector)
|
495
|
+
collector = visit o.left, collector
|
496
|
+
collector << " NOT LIKE "
|
497
|
+
collector = visit o.right, collector
|
498
|
+
if o.escape
|
499
|
+
collector << " ESCAPE "
|
500
|
+
visit o.escape, collector
|
501
|
+
else
|
502
|
+
collector
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def visit_Arel_Nodes_JoinSource(o, collector)
|
507
|
+
if o.left
|
508
|
+
collector = visit o.left, collector
|
509
|
+
end
|
510
|
+
if o.right.any?
|
511
|
+
collector << " " if o.left
|
512
|
+
collector = inject_join o.right, collector, " "
|
513
|
+
end
|
514
|
+
collector
|
515
|
+
end
|
516
|
+
|
517
|
+
def visit_Arel_Nodes_Regexp(o, collector)
|
518
|
+
raise NotImplementedError, "~ not implemented for this db"
|
519
|
+
end
|
520
|
+
|
521
|
+
def visit_Arel_Nodes_NotRegexp(o, collector)
|
522
|
+
raise NotImplementedError, "!~ not implemented for this db"
|
523
|
+
end
|
524
|
+
|
525
|
+
def visit_Arel_Nodes_StringJoin(o, collector)
|
526
|
+
visit o.left, collector
|
527
|
+
end
|
528
|
+
|
529
|
+
def visit_Arel_Nodes_FullOuterJoin(o, collector)
|
530
|
+
collector << "FULL OUTER JOIN "
|
531
|
+
collector = visit o.left, collector
|
532
|
+
collector << " "
|
533
|
+
visit o.right, collector
|
534
|
+
end
|
535
|
+
|
536
|
+
def visit_Arel_Nodes_OuterJoin(o, collector)
|
537
|
+
collector << "LEFT OUTER JOIN "
|
538
|
+
collector = visit o.left, collector
|
539
|
+
collector << " "
|
540
|
+
visit o.right, collector
|
541
|
+
end
|
542
|
+
|
543
|
+
def visit_Arel_Nodes_RightOuterJoin(o, collector)
|
544
|
+
collector << "RIGHT OUTER JOIN "
|
545
|
+
collector = visit o.left, collector
|
546
|
+
collector << " "
|
547
|
+
visit o.right, collector
|
548
|
+
end
|
549
|
+
|
550
|
+
def visit_Arel_Nodes_InnerJoin(o, collector)
|
551
|
+
collector << "INNER JOIN "
|
552
|
+
collector = visit o.left, collector
|
553
|
+
if o.right
|
554
|
+
collector << " "
|
555
|
+
visit(o.right, collector)
|
556
|
+
else
|
557
|
+
collector
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def visit_Arel_Nodes_On(o, collector)
|
562
|
+
collector << "ON "
|
563
|
+
visit o.expr, collector
|
564
|
+
end
|
565
|
+
|
566
|
+
def visit_Arel_Nodes_Not(o, collector)
|
567
|
+
collector << "NOT ("
|
568
|
+
visit(o.expr, collector) << ")"
|
569
|
+
end
|
570
|
+
|
571
|
+
def visit_Arel_Table(o, collector)
|
572
|
+
if o.table_alias
|
573
|
+
collector << quote_table_name(o.name) << " " << quote_table_name(o.table_alias)
|
574
|
+
else
|
575
|
+
collector << quote_table_name(o.name)
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
def visit_Arel_Nodes_In(o, collector)
|
580
|
+
collector.preparable = false
|
581
|
+
attr, values = o.left, o.right
|
582
|
+
|
583
|
+
if Array === values
|
584
|
+
unless values.empty?
|
585
|
+
values.delete_if { |value| unboundable?(value) }
|
586
|
+
end
|
587
|
+
|
588
|
+
return collector << "1=0" if values.empty?
|
589
|
+
end
|
590
|
+
|
591
|
+
visit(attr, collector) << " IN ("
|
592
|
+
visit(values, collector) << ")"
|
593
|
+
end
|
594
|
+
|
595
|
+
def visit_Arel_Nodes_NotIn(o, collector)
|
596
|
+
collector.preparable = false
|
597
|
+
attr, values = o.left, o.right
|
598
|
+
|
599
|
+
if Array === values
|
600
|
+
unless values.empty?
|
601
|
+
values.delete_if { |value| unboundable?(value) }
|
602
|
+
end
|
603
|
+
|
604
|
+
return collector << "1=1" if values.empty?
|
605
|
+
end
|
606
|
+
|
607
|
+
visit(attr, collector) << " NOT IN ("
|
608
|
+
visit(values, collector) << ")"
|
609
|
+
end
|
610
|
+
|
611
|
+
def visit_Arel_Nodes_And(o, collector)
|
612
|
+
inject_join o.children, collector, " AND "
|
613
|
+
end
|
614
|
+
|
615
|
+
def visit_Arel_Nodes_Or(o, collector)
|
616
|
+
stack = [o.right, o.left]
|
617
|
+
|
618
|
+
while o = stack.pop
|
619
|
+
if o.is_a?(Arel::Nodes::Or)
|
620
|
+
stack.push o.right, o.left
|
621
|
+
else
|
622
|
+
visit o, collector
|
623
|
+
collector << " OR " unless stack.empty?
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
collector
|
628
|
+
end
|
629
|
+
|
630
|
+
def visit_Arel_Nodes_Assignment(o, collector)
|
631
|
+
case o.right
|
632
|
+
when Arel::Nodes::Node, Arel::Attributes::Attribute, ActiveModel::Attribute
|
633
|
+
collector = visit o.left, collector
|
634
|
+
collector << " = "
|
635
|
+
visit o.right, collector
|
636
|
+
else
|
637
|
+
collector = visit o.left, collector
|
638
|
+
collector << " = "
|
639
|
+
collector << quote(o.right).to_s
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
def visit_Arel_Nodes_Equality(o, collector)
|
644
|
+
right = o.right
|
645
|
+
|
646
|
+
return collector << "1=0" if unboundable?(right)
|
647
|
+
|
648
|
+
collector = visit o.left, collector
|
649
|
+
|
650
|
+
if right.nil?
|
651
|
+
collector << " IS NULL"
|
652
|
+
else
|
653
|
+
collector << " = "
|
654
|
+
visit right, collector
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
|
659
|
+
if o.right.nil?
|
660
|
+
collector = visit o.left, collector
|
661
|
+
collector << " IS NULL"
|
662
|
+
else
|
663
|
+
collector = is_distinct_from(o, collector)
|
664
|
+
collector << " = 0"
|
665
|
+
end
|
666
|
+
end
|
667
|
+
|
668
|
+
def visit_Arel_Nodes_IsDistinctFrom(o, collector)
|
669
|
+
if o.right.nil?
|
670
|
+
collector = visit o.left, collector
|
671
|
+
collector << " IS NOT NULL"
|
672
|
+
else
|
673
|
+
collector = is_distinct_from(o, collector)
|
674
|
+
collector << " = 1"
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
def visit_Arel_Nodes_NotEqual(o, collector)
|
679
|
+
right = o.right
|
680
|
+
|
681
|
+
return collector << "1=1" if unboundable?(right)
|
682
|
+
|
683
|
+
collector = visit o.left, collector
|
684
|
+
|
685
|
+
if right.nil?
|
686
|
+
collector << " IS NOT NULL"
|
687
|
+
else
|
688
|
+
collector << " != "
|
689
|
+
visit right, collector
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
def visit_Arel_Nodes_As(o, collector)
|
694
|
+
collector = visit o.left, collector
|
695
|
+
collector << " AS "
|
696
|
+
visit o.right, collector
|
697
|
+
end
|
698
|
+
|
699
|
+
def visit_Arel_Nodes_Case(o, collector)
|
700
|
+
collector << "CASE "
|
701
|
+
if o.case
|
702
|
+
visit o.case, collector
|
703
|
+
collector << " "
|
704
|
+
end
|
705
|
+
o.conditions.each do |condition|
|
706
|
+
visit condition, collector
|
707
|
+
collector << " "
|
708
|
+
end
|
709
|
+
if o.default
|
710
|
+
visit o.default, collector
|
711
|
+
collector << " "
|
712
|
+
end
|
713
|
+
collector << "END"
|
714
|
+
end
|
715
|
+
|
716
|
+
def visit_Arel_Nodes_When(o, collector)
|
717
|
+
collector << "WHEN "
|
718
|
+
visit o.left, collector
|
719
|
+
collector << " THEN "
|
720
|
+
visit o.right, collector
|
721
|
+
end
|
722
|
+
|
723
|
+
def visit_Arel_Nodes_Else(o, collector)
|
724
|
+
collector << "ELSE "
|
725
|
+
visit o.expr, collector
|
726
|
+
end
|
727
|
+
|
728
|
+
def visit_Arel_Nodes_UnqualifiedColumn(o, collector)
|
729
|
+
collector << quote_column_name(o.name)
|
730
|
+
end
|
731
|
+
|
732
|
+
def visit_Arel_Attributes_Attribute(o, collector)
|
733
|
+
join_name = o.relation.table_alias || o.relation.name
|
734
|
+
collector << quote_table_name(join_name) << "." << quote_column_name(o.name)
|
735
|
+
end
|
736
|
+
|
737
|
+
BIND_BLOCK = proc { "?" }
|
738
|
+
private_constant :BIND_BLOCK
|
739
|
+
|
740
|
+
def bind_block; BIND_BLOCK; end
|
741
|
+
|
742
|
+
def visit_ActiveModel_Attribute(o, collector)
|
743
|
+
collector.add_bind(o, &bind_block)
|
744
|
+
end
|
745
|
+
|
746
|
+
def visit_Arel_Nodes_BindParam(o, collector)
|
747
|
+
collector.add_bind(o.value, &bind_block)
|
748
|
+
end
|
749
|
+
|
750
|
+
def visit_Arel_Nodes_SqlLiteral(o, collector)
|
751
|
+
collector.preparable = false
|
752
|
+
collector << o.to_s
|
753
|
+
end
|
754
|
+
|
755
|
+
def visit_Integer(o, collector)
|
756
|
+
collector << o.to_s
|
757
|
+
end
|
758
|
+
|
759
|
+
def unsupported(o, collector)
|
760
|
+
raise UnsupportedVisitError.new(o)
|
761
|
+
end
|
762
|
+
|
763
|
+
alias :visit_ActiveSupport_Multibyte_Chars :unsupported
|
764
|
+
alias :visit_ActiveSupport_StringInquirer :unsupported
|
765
|
+
alias :visit_BigDecimal :unsupported
|
766
|
+
alias :visit_Class :unsupported
|
767
|
+
alias :visit_Date :unsupported
|
768
|
+
alias :visit_DateTime :unsupported
|
769
|
+
alias :visit_FalseClass :unsupported
|
770
|
+
alias :visit_Float :unsupported
|
771
|
+
alias :visit_Hash :unsupported
|
772
|
+
alias :visit_NilClass :unsupported
|
773
|
+
alias :visit_String :unsupported
|
774
|
+
alias :visit_Symbol :unsupported
|
775
|
+
alias :visit_Time :unsupported
|
776
|
+
alias :visit_TrueClass :unsupported
|
777
|
+
|
778
|
+
def visit_Arel_Nodes_InfixOperation(o, collector)
|
779
|
+
collector = visit o.left, collector
|
780
|
+
collector << " #{o.operator} "
|
781
|
+
visit o.right, collector
|
782
|
+
end
|
783
|
+
|
784
|
+
def visit_Arel_Nodes_UnaryOperation(o, collector)
|
785
|
+
collector << " #{o.operator} "
|
786
|
+
visit o.expr, collector
|
787
|
+
end
|
788
|
+
|
789
|
+
def visit_Array(o, collector)
|
790
|
+
inject_join o, collector, ", "
|
791
|
+
end
|
792
|
+
alias :visit_Set :visit_Array
|
793
|
+
|
794
|
+
def quote(value)
|
795
|
+
return value if Arel::Nodes::SqlLiteral === value
|
796
|
+
@connection.quote value
|
797
|
+
end
|
798
|
+
|
799
|
+
def quote_table_name(name)
|
800
|
+
return name if Arel::Nodes::SqlLiteral === name
|
801
|
+
@connection.quote_table_name(name)
|
802
|
+
end
|
803
|
+
|
804
|
+
def quote_column_name(name)
|
805
|
+
return name if Arel::Nodes::SqlLiteral === name
|
806
|
+
@connection.quote_column_name(name)
|
807
|
+
end
|
808
|
+
|
809
|
+
def sanitize_as_sql_comment(value)
|
810
|
+
return value if Arel::Nodes::SqlLiteral === value
|
811
|
+
@connection.sanitize_as_sql_comment(value)
|
812
|
+
end
|
813
|
+
|
814
|
+
def collect_optimizer_hints(o, collector)
|
815
|
+
maybe_visit o.optimizer_hints, collector
|
816
|
+
end
|
817
|
+
|
818
|
+
def maybe_visit(thing, collector)
|
819
|
+
return collector unless thing
|
820
|
+
collector << " "
|
821
|
+
visit thing, collector
|
822
|
+
end
|
823
|
+
|
824
|
+
def inject_join(list, collector, join_str)
|
825
|
+
list.each_with_index do |x, i|
|
826
|
+
collector << join_str unless i == 0
|
827
|
+
collector = visit(x, collector)
|
828
|
+
end
|
829
|
+
collector
|
830
|
+
end
|
831
|
+
|
832
|
+
def unboundable?(value)
|
833
|
+
value.respond_to?(:unboundable?) && value.unboundable?
|
834
|
+
end
|
835
|
+
|
836
|
+
def has_join_sources?(o)
|
837
|
+
o.relation.is_a?(Nodes::JoinSource) && !o.relation.right.empty?
|
838
|
+
end
|
839
|
+
|
840
|
+
def has_limit_or_offset_or_orders?(o)
|
841
|
+
o.limit || o.offset || !o.orders.empty?
|
842
|
+
end
|
843
|
+
|
844
|
+
def has_group_by_and_having?(o)
|
845
|
+
!o.groups.empty? && !o.havings.empty?
|
846
|
+
end
|
847
|
+
|
848
|
+
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
|
849
|
+
# on MySQL (even when aliasing the tables), but MySQL allows using JOIN directly in
|
850
|
+
# an UPDATE statement, so in the MySQL visitor we redefine this to do that.
|
851
|
+
def prepare_update_statement(o)
|
852
|
+
if o.key && (has_limit_or_offset_or_orders?(o) || has_join_sources?(o))
|
853
|
+
stmt = o.clone
|
854
|
+
stmt.limit = nil
|
855
|
+
stmt.offset = nil
|
856
|
+
stmt.orders = []
|
857
|
+
stmt.wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
|
858
|
+
stmt.relation = o.relation.left if has_join_sources?(o)
|
859
|
+
stmt.groups = o.groups unless o.groups.empty?
|
860
|
+
stmt.havings = o.havings unless o.havings.empty?
|
861
|
+
stmt
|
862
|
+
else
|
863
|
+
o
|
864
|
+
end
|
865
|
+
end
|
866
|
+
alias :prepare_delete_statement :prepare_update_statement
|
867
|
+
|
868
|
+
# FIXME: we should probably have a 2-pass visitor for this
|
869
|
+
def build_subselect(key, o)
|
870
|
+
stmt = Nodes::SelectStatement.new
|
871
|
+
core = stmt.cores.first
|
872
|
+
core.froms = o.relation
|
873
|
+
core.wheres = o.wheres
|
874
|
+
core.projections = [key]
|
875
|
+
core.groups = o.groups unless o.groups.empty?
|
876
|
+
core.havings = o.havings unless o.havings.empty?
|
877
|
+
stmt.limit = o.limit
|
878
|
+
stmt.offset = o.offset
|
879
|
+
stmt.orders = o.orders
|
880
|
+
stmt
|
881
|
+
end
|
882
|
+
|
883
|
+
def infix_value(o, collector, value)
|
884
|
+
collector = visit o.left, collector
|
885
|
+
collector << value
|
886
|
+
visit o.right, collector
|
887
|
+
end
|
888
|
+
|
889
|
+
def infix_value_with_paren(o, collector, value, suppress_parens = false)
|
890
|
+
collector << "( " unless suppress_parens
|
891
|
+
collector = if o.left.class == o.class
|
892
|
+
infix_value_with_paren(o.left, collector, value, true)
|
893
|
+
else
|
894
|
+
visit o.left, collector
|
895
|
+
end
|
896
|
+
collector << value
|
897
|
+
collector = if o.right.class == o.class
|
898
|
+
infix_value_with_paren(o.right, collector, value, true)
|
899
|
+
else
|
900
|
+
visit o.right, collector
|
901
|
+
end
|
902
|
+
collector << " )" unless suppress_parens
|
903
|
+
collector
|
904
|
+
end
|
905
|
+
|
906
|
+
def aggregate(name, o, collector)
|
907
|
+
collector << "#{name}("
|
908
|
+
if o.distinct
|
909
|
+
collector << "DISTINCT "
|
910
|
+
end
|
911
|
+
collector = inject_join(o.expressions, collector, ", ") << ")"
|
912
|
+
if o.alias
|
913
|
+
collector << " AS "
|
914
|
+
visit o.alias, collector
|
915
|
+
else
|
916
|
+
collector
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
920
|
+
def is_distinct_from(o, collector)
|
921
|
+
collector << "CASE WHEN "
|
922
|
+
collector = visit o.left, collector
|
923
|
+
collector << " = "
|
924
|
+
collector = visit o.right, collector
|
925
|
+
collector << " OR ("
|
926
|
+
collector = visit o.left, collector
|
927
|
+
collector << " IS NULL AND "
|
928
|
+
collector = visit o.right, collector
|
929
|
+
collector << " IS NULL)"
|
930
|
+
collector << " THEN 0 ELSE 1 END"
|
931
|
+
end
|
932
|
+
|
933
|
+
def collect_ctes(children, collector)
|
934
|
+
children.each_with_index do |child, i|
|
935
|
+
collector << ", " unless i == 0
|
936
|
+
|
937
|
+
case child
|
938
|
+
when Arel::Nodes::As
|
939
|
+
name = child.left.name
|
940
|
+
relation = child.right
|
941
|
+
when Arel::Nodes::TableAlias
|
942
|
+
name = child.name
|
943
|
+
relation = child.relation
|
944
|
+
end
|
945
|
+
|
946
|
+
collector << quote_table_name(name)
|
947
|
+
collector << " AS "
|
948
|
+
visit relation, collector
|
949
|
+
end
|
950
|
+
|
951
|
+
collector
|
952
|
+
end
|
953
|
+
end
|
954
|
+
end
|
955
|
+
end
|