omg-activerecord 8.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +355 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +219 -0
- data/examples/performance.rb +185 -0
- data/examples/simple.rb +15 -0
- data/lib/active_record/aggregations.rb +287 -0
- data/lib/active_record/association_relation.rb +50 -0
- data/lib/active_record/associations/alias_tracker.rb +90 -0
- data/lib/active_record/associations/association.rb +417 -0
- data/lib/active_record/associations/association_scope.rb +175 -0
- data/lib/active_record/associations/belongs_to_association.rb +163 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/builder/association.rb +170 -0
- data/lib/active_record/associations/builder/belongs_to.rb +160 -0
- data/lib/active_record/associations/builder/collection_association.rb +80 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
- data/lib/active_record/associations/builder/has_many.rb +23 -0
- data/lib/active_record/associations/builder/has_one.rb +61 -0
- data/lib/active_record/associations/builder/singular_association.rb +48 -0
- data/lib/active_record/associations/collection_association.rb +535 -0
- data/lib/active_record/associations/collection_proxy.rb +1163 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +40 -0
- data/lib/active_record/associations/has_many_association.rb +167 -0
- data/lib/active_record/associations/has_many_through_association.rb +232 -0
- data/lib/active_record/associations/has_one_association.rb +142 -0
- data/lib/active_record/associations/has_one_through_association.rb +45 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/join_dependency.rb +301 -0
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +316 -0
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +150 -0
- data/lib/active_record/associations/preloader.rb +135 -0
- data/lib/active_record/associations/singular_association.rb +76 -0
- data/lib/active_record/associations/through_association.rb +132 -0
- data/lib/active_record/associations.rb +1897 -0
- data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
- data/lib/active_record/attribute_assignment.rb +82 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +262 -0
- data/lib/active_record/attribute_methods/primary_key.rb +158 -0
- data/lib/active_record/attribute_methods/query.rb +50 -0
- data/lib/active_record/attribute_methods/read.rb +46 -0
- data/lib/active_record/attribute_methods/serialization.rb +232 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
- data/lib/active_record/attribute_methods/write.rb +49 -0
- data/lib/active_record/attribute_methods.rb +542 -0
- data/lib/active_record/attributes.rb +307 -0
- data/lib/active_record/autosave_association.rb +586 -0
- data/lib/active_record/base.rb +338 -0
- data/lib/active_record/callbacks.rb +452 -0
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
- data/lib/active_record/connection_adapters/column.rb +122 -0
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- 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 +125 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +413 -0
- data/lib/active_record/core.rb +836 -0
- data/lib/active_record/counter_cache.rb +230 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
- data/lib/active_record/database_configurations/database_config.rb +104 -0
- data/lib/active_record/database_configurations/hash_config.rb +172 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/database_configurations.rb +309 -0
- data/lib/active_record/delegated_type.rb +289 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +121 -0
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +70 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -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 +177 -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 +159 -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 +53 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +107 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +424 -0
- data/lib/active_record/errors.rb +614 -0
- data/lib/active_record/explain.rb +63 -0
- data/lib/active_record/explain_registry.rb +37 -0
- data/lib/active_record/explain_subscriber.rb +34 -0
- data/lib/active_record/fixture_set/file.rb +89 -0
- data/lib/active_record/fixture_set/model_metadata.rb +42 -0
- data/lib/active_record/fixture_set/render_context.rb +19 -0
- data/lib/active_record/fixture_set/table_row.rb +208 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +850 -0
- data/lib/active_record/future_result.rb +182 -0
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +366 -0
- data/lib/active_record/insert_all.rb +328 -0
- data/lib/active_record/integration.rb +209 -0
- data/lib/active_record/internal_metadata.rb +164 -0
- data/lib/active_record/legacy_yaml_adapter.rb +15 -0
- data/lib/active_record/locale/en.yml +48 -0
- data/lib/active_record/locking/optimistic.rb +228 -0
- data/lib/active_record/locking/pessimistic.rb +102 -0
- data/lib/active_record/log_subscriber.rb +149 -0
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +87 -0
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +406 -0
- data/lib/active_record/migration/compatibility.rb +490 -0
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +16 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +1626 -0
- data/lib/active_record/model_schema.rb +635 -0
- data/lib/active_record/nested_attributes.rb +633 -0
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +968 -0
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/query_logs.rb +247 -0
- data/lib/active_record/query_logs_formatter.rb +30 -0
- data/lib/active_record/querying.rb +122 -0
- data/lib/active_record/railtie.rb +440 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +65 -0
- data/lib/active_record/railties/databases.rake +641 -0
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +66 -0
- data/lib/active_record/reflection.rb +1287 -0
- data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
- data/lib/active_record/relation/batches.rb +491 -0
- data/lib/active_record/relation/calculations.rb +679 -0
- data/lib/active_record/relation/delegation.rb +154 -0
- data/lib/active_record/relation/finder_methods.rb +661 -0
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +192 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -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 +60 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
- data/lib/active_record/relation/predicate_builder.rb +181 -0
- data/lib/active_record/relation/query_attribute.rb +68 -0
- data/lib/active_record/relation/query_methods.rb +2235 -0
- data/lib/active_record/relation/record_fetch_warning.rb +52 -0
- data/lib/active_record/relation/spawn_methods.rb +78 -0
- data/lib/active_record/relation/where_clause.rb +218 -0
- data/lib/active_record/relation.rb +1495 -0
- data/lib/active_record/result.rb +249 -0
- data/lib/active_record/runtime_registry.rb +82 -0
- data/lib/active_record/sanitization.rb +254 -0
- data/lib/active_record/schema.rb +77 -0
- data/lib/active_record/schema_dumper.rb +364 -0
- data/lib/active_record/schema_migration.rb +106 -0
- data/lib/active_record/scoping/default.rb +205 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +136 -0
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +66 -0
- data/lib/active_record/serialization.rb +29 -0
- data/lib/active_record/signed_id.rb +137 -0
- data/lib/active_record/statement_cache.rb +164 -0
- data/lib/active_record/store.rb +299 -0
- data/lib/active_record/suppressor.rb +59 -0
- data/lib/active_record/table_metadata.rb +85 -0
- data/lib/active_record/tasks/database_tasks.rb +681 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +321 -0
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +177 -0
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +70 -0
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +523 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type/adapter_specific_registry.rb +144 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
- data/lib/active_record/type/internal/timezone.rb +22 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +76 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +35 -0
- data/lib/active_record/type/type_map.rb +58 -0
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type.rb +83 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +65 -0
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +45 -0
- data/lib/active_record/validations/uniqueness.rb +295 -0
- data/lib/active_record/validations.rb +101 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/active_record.rb +616 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +31 -0
- data/lib/arel/collectors/composite.rb +46 -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 +19 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +53 -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/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +125 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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/cte.rb +36 -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/fragments.rb +35 -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 +68 -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/leading_join.rb +8 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/nary.rb +39 -0
- data/lib/arel/nodes/node.rb +161 -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 +32 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +35 -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 +75 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +260 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +121 -0
- data/lib/arel/tree_manager.rb +65 -0
- data/lib/arel/update_manager.rb +49 -0
- data/lib/arel/visitors/dot.rb +299 -0
- data/lib/arel/visitors/mysql.rb +111 -0
- data/lib/arel/visitors/postgresql.rb +99 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +1033 -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 +73 -0
- data/lib/rails/generators/active_record/application_record/USAGE +8 -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 +76 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +54 -0
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
- 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
- data/lib/rails/generators/active_record.rb +19 -0
- metadata +505 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class Promise < BasicObject
|
|
5
|
+
undef_method :==, :!, :!=
|
|
6
|
+
|
|
7
|
+
def initialize(future_result, block) # :nodoc:
|
|
8
|
+
@future_result = future_result
|
|
9
|
+
@block = block
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Returns whether the associated query is still being executed or not.
|
|
13
|
+
def pending?
|
|
14
|
+
@future_result.pending?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns the query result.
|
|
18
|
+
# If the query wasn't completed yet, accessing +#value+ will block until the query completes.
|
|
19
|
+
# If the query failed, +#value+ will raise the corresponding error.
|
|
20
|
+
def value
|
|
21
|
+
return @value if defined? @value
|
|
22
|
+
|
|
23
|
+
result = @future_result.result
|
|
24
|
+
@value = if @block
|
|
25
|
+
@block.call(result)
|
|
26
|
+
else
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns a new +ActiveRecord::Promise+ that will apply the passed block
|
|
32
|
+
# when the value is accessed:
|
|
33
|
+
#
|
|
34
|
+
# Post.async_pick(:title).then { |title| title.upcase }.value
|
|
35
|
+
# # => "POST TITLE"
|
|
36
|
+
def then(&block)
|
|
37
|
+
Promise.new(@future_result, @block ? @block >> block : block)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
[:class, :respond_to?, :is_a?].each do |method|
|
|
41
|
+
define_method(method, ::Object.instance_method(method))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def inspect # :nodoc:
|
|
45
|
+
"#<ActiveRecord::Promise status=#{status}>"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def pretty_print(q) # :nodoc:
|
|
49
|
+
q.text(inspect)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
def status
|
|
54
|
+
if @future_result.pending?
|
|
55
|
+
:pending
|
|
56
|
+
elsif @future_result.canceled?
|
|
57
|
+
:canceled
|
|
58
|
+
else
|
|
59
|
+
:complete
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class Complete < self # :nodoc:
|
|
64
|
+
attr_reader :value
|
|
65
|
+
|
|
66
|
+
def initialize(value)
|
|
67
|
+
@value = value
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def then
|
|
71
|
+
Complete.new(yield @value)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def pending?
|
|
75
|
+
false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
def status
|
|
80
|
+
:complete
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
# = Active Record Query Cache
|
|
5
|
+
class QueryCache
|
|
6
|
+
module ClassMethods
|
|
7
|
+
# Enable the query cache within the block if Active Record is configured.
|
|
8
|
+
# If it's not, it will execute the given block.
|
|
9
|
+
def cache(&block)
|
|
10
|
+
if connected? || !configurations.empty?
|
|
11
|
+
pool = connection_pool
|
|
12
|
+
was_enabled = pool.query_cache_enabled
|
|
13
|
+
begin
|
|
14
|
+
pool.enable_query_cache(&block)
|
|
15
|
+
ensure
|
|
16
|
+
pool.clear_query_cache unless was_enabled
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
yield
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Disable the query cache within the block if Active Record is configured.
|
|
24
|
+
# If it's not, it will execute the given block.
|
|
25
|
+
#
|
|
26
|
+
# Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
|
|
27
|
+
# (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
|
|
28
|
+
def uncached(dirties: true, &block)
|
|
29
|
+
if connected? || !configurations.empty?
|
|
30
|
+
connection_pool.disable_query_cache(dirties: dirties, &block)
|
|
31
|
+
else
|
|
32
|
+
yield
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.run
|
|
38
|
+
ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each(&:enable_query_cache!)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.complete(pools)
|
|
42
|
+
pools.each do |pool|
|
|
43
|
+
pool.disable_query_cache!
|
|
44
|
+
pool.clear_query_cache
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
|
|
48
|
+
pool.release_connection if pool.active_connection? && !pool.lease_connection.transaction_open?
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.install_executor_hooks(executor = ActiveSupport::Executor)
|
|
53
|
+
executor.register_hook(self)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/module/attribute_accessors_per_thread"
|
|
4
|
+
require "active_record/query_logs_formatter"
|
|
5
|
+
|
|
6
|
+
module ActiveRecord
|
|
7
|
+
# = Active Record Query Logs
|
|
8
|
+
#
|
|
9
|
+
# Automatically append comments to SQL queries with runtime information tags. This can be used to trace troublesome
|
|
10
|
+
# SQL statements back to the application code that generated these statements.
|
|
11
|
+
#
|
|
12
|
+
# Query logs can be enabled via \Rails configuration in <tt>config/application.rb</tt> or an initializer:
|
|
13
|
+
#
|
|
14
|
+
# config.active_record.query_log_tags_enabled = true
|
|
15
|
+
#
|
|
16
|
+
# By default the name of the application, the name and action of the controller, or the name of the job are logged.
|
|
17
|
+
# The default format is {SQLCommenter}[https://open-telemetry.github.io/opentelemetry-sqlcommenter/].
|
|
18
|
+
# The tags shown in a query comment can be configured via \Rails configuration:
|
|
19
|
+
#
|
|
20
|
+
# config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
|
|
21
|
+
#
|
|
22
|
+
# Active Record defines default tags available for use:
|
|
23
|
+
#
|
|
24
|
+
# * +application+
|
|
25
|
+
# * +pid+
|
|
26
|
+
# * +socket+
|
|
27
|
+
# * +db_host+
|
|
28
|
+
# * +database+
|
|
29
|
+
# * +source_location+
|
|
30
|
+
#
|
|
31
|
+
# Action Controller adds default tags when loaded:
|
|
32
|
+
#
|
|
33
|
+
# * +controller+
|
|
34
|
+
# * +action+
|
|
35
|
+
# * +namespaced_controller+
|
|
36
|
+
#
|
|
37
|
+
# Active Job adds default tags when loaded:
|
|
38
|
+
#
|
|
39
|
+
# * +job+
|
|
40
|
+
#
|
|
41
|
+
# New comment tags can be defined by adding them in a +Hash+ to the tags +Array+. Tags can have dynamic content by
|
|
42
|
+
# setting a +Proc+ or lambda value in the +Hash+, and can reference any value stored by \Rails in the +context+ object.
|
|
43
|
+
# ActiveSupport::CurrentAttributes can be used to store application values. Tags with +nil+ values are
|
|
44
|
+
# omitted from the query comment.
|
|
45
|
+
#
|
|
46
|
+
# Escaping is performed on the string returned, however untrusted user input should not be used.
|
|
47
|
+
#
|
|
48
|
+
# Example:
|
|
49
|
+
#
|
|
50
|
+
# config.active_record.query_log_tags = [
|
|
51
|
+
# :namespaced_controller,
|
|
52
|
+
# :action,
|
|
53
|
+
# :job,
|
|
54
|
+
# {
|
|
55
|
+
# request_id: ->(context) { context[:controller]&.request&.request_id },
|
|
56
|
+
# job_id: ->(context) { context[:job]&.job_id },
|
|
57
|
+
# tenant_id: -> { Current.tenant&.id },
|
|
58
|
+
# static: "value",
|
|
59
|
+
# },
|
|
60
|
+
# ]
|
|
61
|
+
#
|
|
62
|
+
# By default the name of the application, the name and action of the controller, or the name of the job are logged
|
|
63
|
+
# using the {SQLCommenter}[https://open-telemetry.github.io/opentelemetry-sqlcommenter/] format. This can be changed
|
|
64
|
+
# via {config.active_record.query_log_tags_format}[https://guides.rubyonrails.org/configuring.html#config-active-record-query-log-tags-format]
|
|
65
|
+
#
|
|
66
|
+
# Tag comments can be prepended to the query:
|
|
67
|
+
#
|
|
68
|
+
# ActiveRecord::QueryLogs.prepend_comment = true
|
|
69
|
+
#
|
|
70
|
+
# For applications where the content will not change during the lifetime of
|
|
71
|
+
# the request or job execution, the tags can be cached for reuse in every query:
|
|
72
|
+
#
|
|
73
|
+
# config.active_record.cache_query_log_tags = true
|
|
74
|
+
module QueryLogs
|
|
75
|
+
class GetKeyHandler # :nodoc:
|
|
76
|
+
def initialize(name)
|
|
77
|
+
@name = name
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def call(context)
|
|
81
|
+
context[@name]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class IdentityHandler # :nodoc:
|
|
86
|
+
def initialize(value)
|
|
87
|
+
@value = value
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def call(_context)
|
|
91
|
+
@value
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class ZeroArityHandler # :nodoc:
|
|
96
|
+
def initialize(proc)
|
|
97
|
+
@proc = proc
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def call(_context)
|
|
101
|
+
@proc.call
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
@taggings = {}.freeze
|
|
106
|
+
@tags = [ :application ].freeze
|
|
107
|
+
@prepend_comment = false
|
|
108
|
+
@cache_query_log_tags = false
|
|
109
|
+
@tags_formatter = false
|
|
110
|
+
|
|
111
|
+
thread_mattr_accessor :cached_comment, instance_accessor: false
|
|
112
|
+
|
|
113
|
+
class << self
|
|
114
|
+
attr_reader :tags, :taggings, :tags_formatter # :nodoc:
|
|
115
|
+
attr_accessor :prepend_comment, :cache_query_log_tags # :nodoc:
|
|
116
|
+
|
|
117
|
+
def taggings=(taggings) # :nodoc:
|
|
118
|
+
@taggings = taggings.freeze
|
|
119
|
+
@handlers = rebuild_handlers
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def tags=(tags) # :nodoc:
|
|
123
|
+
@tags = tags.freeze
|
|
124
|
+
@handlers = rebuild_handlers
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def tags_formatter=(format) # :nodoc:
|
|
128
|
+
@formatter = case format
|
|
129
|
+
when :legacy
|
|
130
|
+
LegacyFormatter
|
|
131
|
+
when :sqlcommenter
|
|
132
|
+
SQLCommenter
|
|
133
|
+
else
|
|
134
|
+
raise ArgumentError, "Formatter is unsupported: #{format}"
|
|
135
|
+
end
|
|
136
|
+
@tags_formatter = format
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def call(sql, connection) # :nodoc:
|
|
140
|
+
comment = self.comment(connection)
|
|
141
|
+
|
|
142
|
+
if comment.blank?
|
|
143
|
+
sql
|
|
144
|
+
elsif prepend_comment
|
|
145
|
+
"#{comment} #{sql}"
|
|
146
|
+
else
|
|
147
|
+
"#{sql} #{comment}"
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def clear_cache # :nodoc:
|
|
152
|
+
self.cached_comment = nil
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if Thread.respond_to?(:each_caller_location)
|
|
156
|
+
def query_source_location # :nodoc:
|
|
157
|
+
Thread.each_caller_location do |location|
|
|
158
|
+
frame = LogSubscriber.backtrace_cleaner.clean_frame(location.path)
|
|
159
|
+
return frame if frame
|
|
160
|
+
end
|
|
161
|
+
nil
|
|
162
|
+
end
|
|
163
|
+
else
|
|
164
|
+
def query_source_location # :nodoc:
|
|
165
|
+
LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
|
|
170
|
+
|
|
171
|
+
private
|
|
172
|
+
def rebuild_handlers
|
|
173
|
+
handlers = []
|
|
174
|
+
@tags.each do |i|
|
|
175
|
+
if i.is_a?(Hash)
|
|
176
|
+
i.each do |k, v|
|
|
177
|
+
handlers << [k, build_handler(k, v)]
|
|
178
|
+
end
|
|
179
|
+
else
|
|
180
|
+
handlers << [i, build_handler(i)]
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
handlers.sort_by! { |(key, _)| key.to_s }
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def build_handler(name, handler = nil)
|
|
187
|
+
handler ||= @taggings[name]
|
|
188
|
+
if handler.nil?
|
|
189
|
+
GetKeyHandler.new(name)
|
|
190
|
+
elsif handler.respond_to?(:call)
|
|
191
|
+
if handler.arity == 0
|
|
192
|
+
ZeroArityHandler.new(handler)
|
|
193
|
+
else
|
|
194
|
+
handler
|
|
195
|
+
end
|
|
196
|
+
else
|
|
197
|
+
IdentityHandler.new(handler)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Returns an SQL comment +String+ containing the query log tags.
|
|
202
|
+
# Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
|
|
203
|
+
def comment(connection)
|
|
204
|
+
if cache_query_log_tags
|
|
205
|
+
self.cached_comment ||= uncached_comment(connection)
|
|
206
|
+
else
|
|
207
|
+
uncached_comment(connection)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def uncached_comment(connection)
|
|
212
|
+
content = tag_content(connection)
|
|
213
|
+
|
|
214
|
+
if content.present?
|
|
215
|
+
"/*#{escape_sql_comment(content)}*/"
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def escape_sql_comment(content)
|
|
220
|
+
# Sanitize a string to appear within a SQL comment
|
|
221
|
+
# For compatibility, this also surrounding "/*+", "/*", and "*/"
|
|
222
|
+
# characters, possibly with single surrounding space.
|
|
223
|
+
# Then follows that by replacing any internal "*/" or "/ *" with
|
|
224
|
+
# "* /" or "/ *"
|
|
225
|
+
comment = content.to_s.dup
|
|
226
|
+
comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
|
|
227
|
+
comment.gsub!("*/", "* /")
|
|
228
|
+
comment.gsub!("/*", "/ *")
|
|
229
|
+
comment
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def tag_content(connection)
|
|
233
|
+
context = ActiveSupport::ExecutionContext.to_h
|
|
234
|
+
context[:connection] ||= connection
|
|
235
|
+
|
|
236
|
+
pairs = @handlers.filter_map do |(key, handler)|
|
|
237
|
+
val = handler.call(context)
|
|
238
|
+
@formatter.format(key, val) unless val.nil?
|
|
239
|
+
end
|
|
240
|
+
@formatter.join(pairs)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
@handlers = rebuild_handlers
|
|
245
|
+
self.tags_formatter = :legacy
|
|
246
|
+
end
|
|
247
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module QueryLogs
|
|
5
|
+
module LegacyFormatter # :nodoc:
|
|
6
|
+
class << self
|
|
7
|
+
# Formats the key value pairs into a string.
|
|
8
|
+
def format(key, value)
|
|
9
|
+
"#{key}:#{value}"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def join(pairs)
|
|
13
|
+
pairs.join(",")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class SQLCommenter # :nodoc:
|
|
19
|
+
class << self
|
|
20
|
+
def format(key, value)
|
|
21
|
+
"#{key}='#{ERB::Util.url_encode(value)}'"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def join(pairs)
|
|
25
|
+
pairs.join(",")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Querying
|
|
5
|
+
QUERYING_METHODS = [
|
|
6
|
+
:find, :find_by, :find_by!, :take, :take!, :sole, :find_sole_by, :first, :first!, :last, :last!,
|
|
7
|
+
:second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!,
|
|
8
|
+
:forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!,
|
|
9
|
+
:exists?, :any?, :many?, :none?, :one?,
|
|
10
|
+
:first_or_create, :first_or_create!, :first_or_initialize,
|
|
11
|
+
:find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
|
|
12
|
+
:create_or_find_by, :create_or_find_by!,
|
|
13
|
+
:destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
|
|
14
|
+
:find_each, :find_in_batches, :in_batches,
|
|
15
|
+
:select, :reselect, :order, :regroup, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
|
|
16
|
+
:where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
|
|
17
|
+
:and, :or, :annotate, :optimizer_hints, :extending,
|
|
18
|
+
:having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
|
|
19
|
+
:count, :average, :minimum, :maximum, :sum, :calculate,
|
|
20
|
+
:pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
|
|
21
|
+
:async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
|
|
22
|
+
:insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
|
|
23
|
+
].freeze # :nodoc:
|
|
24
|
+
delegate(*QUERYING_METHODS, to: :all)
|
|
25
|
+
|
|
26
|
+
# Executes a custom SQL query against your database and returns all the results. The results will
|
|
27
|
+
# be returned as an array, with the requested columns encapsulated as attributes of the model you call
|
|
28
|
+
# this method from. For example, if you call <tt>Product.find_by_sql</tt>, then the results will be returned in
|
|
29
|
+
# a +Product+ object with the attributes you specified in the SQL query.
|
|
30
|
+
#
|
|
31
|
+
# If you call a complicated SQL query which spans multiple tables, the columns specified by the
|
|
32
|
+
# SELECT will be attributes of the model, whether or not they are columns of the corresponding
|
|
33
|
+
# table.
|
|
34
|
+
#
|
|
35
|
+
# The +sql+ parameter is a full SQL query as a string. It will be called as is; there will be
|
|
36
|
+
# no database agnostic conversions performed. This should be a last resort because using
|
|
37
|
+
# database-specific terms will lock you into using that particular database engine, or require you to
|
|
38
|
+
# change your call if you switch engines.
|
|
39
|
+
#
|
|
40
|
+
# # A simple SQL query spanning multiple tables
|
|
41
|
+
# Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
|
|
42
|
+
# # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "author"=>"Quentin"}>, ...]
|
|
43
|
+
#
|
|
44
|
+
# You can use the same string replacement techniques as you can with ActiveRecord::QueryMethods#where :
|
|
45
|
+
#
|
|
46
|
+
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
|
|
47
|
+
# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
|
|
48
|
+
#
|
|
49
|
+
# Note that building your own SQL query string from user input may expose your application to
|
|
50
|
+
# injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
|
|
51
|
+
def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
|
|
52
|
+
result = with_connection do |c|
|
|
53
|
+
_query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
|
|
54
|
+
end
|
|
55
|
+
_load_from_sql(result, &block)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
|
|
59
|
+
def async_find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
|
|
60
|
+
with_connection do |c|
|
|
61
|
+
_query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry, async: true)
|
|
62
|
+
end.then do |result|
|
|
63
|
+
_load_from_sql(result, &block)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def _query_by_sql(connection, sql, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
|
|
68
|
+
connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async, allow_retry: allow_retry)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def _load_from_sql(result_set, &block) # :nodoc:
|
|
72
|
+
return [] if result_set.empty?
|
|
73
|
+
|
|
74
|
+
column_types = result_set.column_types
|
|
75
|
+
|
|
76
|
+
unless column_types.empty?
|
|
77
|
+
column_types = column_types.reject { |k, _| attribute_types.key?(k) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
message_bus = ActiveSupport::Notifications.instrumenter
|
|
81
|
+
|
|
82
|
+
payload = {
|
|
83
|
+
record_count: result_set.length,
|
|
84
|
+
class_name: name
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
message_bus.instrument("instantiation.active_record", payload) do
|
|
88
|
+
if result_set.includes_column?(inheritance_column)
|
|
89
|
+
result_set.map { |record| instantiate(record, column_types, &block) }
|
|
90
|
+
else
|
|
91
|
+
# Instantiate a homogeneous set
|
|
92
|
+
result_set.map { |record| instantiate_instance_of(self, record, column_types, &block) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
|
|
98
|
+
# The use of this method should be restricted to complicated SQL queries that can't be executed
|
|
99
|
+
# using the ActiveRecord::Calculations class methods. Look into those before using this method,
|
|
100
|
+
# as it could lock you into a specific database engine or require a code change to switch
|
|
101
|
+
# database engines.
|
|
102
|
+
#
|
|
103
|
+
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
|
|
104
|
+
# # => 12
|
|
105
|
+
#
|
|
106
|
+
# ==== Parameters
|
|
107
|
+
#
|
|
108
|
+
# * +sql+ - An SQL statement which should return a count query from the database, see the example above.
|
|
109
|
+
def count_by_sql(sql)
|
|
110
|
+
with_connection do |c|
|
|
111
|
+
c.select_value(sanitize_sql(sql), "#{name} Count").to_i
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
|
|
116
|
+
def async_count_by_sql(sql)
|
|
117
|
+
with_connection do |c|
|
|
118
|
+
c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|