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,747 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters # :nodoc:
|
|
5
|
+
module DatabaseStatements
|
|
6
|
+
def initialize
|
|
7
|
+
super
|
|
8
|
+
reset_transaction
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Converts an arel AST to SQL
|
|
12
|
+
def to_sql(arel_or_sql_string, binds = [])
|
|
13
|
+
sql, _ = to_sql_and_binds(arel_or_sql_string, binds)
|
|
14
|
+
sql
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_sql_and_binds(arel_or_sql_string, binds = [], preparable = nil, allow_retry = false) # :nodoc:
|
|
18
|
+
# Arel::TreeManager -> Arel::Node
|
|
19
|
+
if arel_or_sql_string.respond_to?(:ast)
|
|
20
|
+
arel_or_sql_string = arel_or_sql_string.ast
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if Arel.arel_node?(arel_or_sql_string) && !(String === arel_or_sql_string)
|
|
24
|
+
unless binds.empty?
|
|
25
|
+
raise "Passing bind parameters with an arel AST is forbidden. " \
|
|
26
|
+
"The values must be stored on the AST directly"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
collector = collector()
|
|
30
|
+
collector.retryable = true
|
|
31
|
+
|
|
32
|
+
if prepared_statements
|
|
33
|
+
collector.preparable = true
|
|
34
|
+
sql, binds = visitor.compile(arel_or_sql_string, collector)
|
|
35
|
+
|
|
36
|
+
if binds.length > bind_params_length
|
|
37
|
+
unprepared_statement do
|
|
38
|
+
return to_sql_and_binds(arel_or_sql_string)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
preparable = collector.preparable
|
|
42
|
+
else
|
|
43
|
+
sql = visitor.compile(arel_or_sql_string, collector)
|
|
44
|
+
end
|
|
45
|
+
allow_retry = collector.retryable
|
|
46
|
+
[sql.freeze, binds, preparable, allow_retry]
|
|
47
|
+
else
|
|
48
|
+
arel_or_sql_string = arel_or_sql_string.dup.freeze unless arel_or_sql_string.frozen?
|
|
49
|
+
[arel_or_sql_string, binds, preparable, allow_retry]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
private :to_sql_and_binds
|
|
53
|
+
|
|
54
|
+
# This is used in the StatementCache object. It returns an object that
|
|
55
|
+
# can be used to query the database repeatedly.
|
|
56
|
+
def cacheable_query(klass, arel) # :nodoc:
|
|
57
|
+
if prepared_statements
|
|
58
|
+
sql, binds = visitor.compile(arel.ast, collector)
|
|
59
|
+
query = klass.query(sql)
|
|
60
|
+
else
|
|
61
|
+
collector = klass.partial_query_collector
|
|
62
|
+
parts, binds = visitor.compile(arel.ast, collector)
|
|
63
|
+
query = klass.partial_query(parts)
|
|
64
|
+
end
|
|
65
|
+
[query, binds]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns an ActiveRecord::Result instance.
|
|
69
|
+
def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false)
|
|
70
|
+
arel = arel_from_relation(arel)
|
|
71
|
+
sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable, allow_retry)
|
|
72
|
+
|
|
73
|
+
select(sql, name, binds,
|
|
74
|
+
prepare: prepared_statements && preparable,
|
|
75
|
+
async: async && FutureResult::SelectAll,
|
|
76
|
+
allow_retry: allow_retry
|
|
77
|
+
)
|
|
78
|
+
rescue ::RangeError
|
|
79
|
+
ActiveRecord::Result.empty(async: async)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Returns a record hash with the column names as keys and column values
|
|
83
|
+
# as values.
|
|
84
|
+
def select_one(arel, name = nil, binds = [], async: false)
|
|
85
|
+
select_all(arel, name, binds, async: async).then(&:first)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Returns a single value from a record
|
|
89
|
+
def select_value(arel, name = nil, binds = [], async: false)
|
|
90
|
+
select_rows(arel, name, binds, async: async).then { |rows| single_value_from_rows(rows) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Returns an array of the values of the first column in a select:
|
|
94
|
+
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
|
95
|
+
def select_values(arel, name = nil, binds = [])
|
|
96
|
+
select_rows(arel, name, binds).map(&:first)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns an array of arrays containing the field values.
|
|
100
|
+
# Order is the same as that returned by +columns+.
|
|
101
|
+
def select_rows(arel, name = nil, binds = [], async: false)
|
|
102
|
+
select_all(arel, name, binds, async: async).then(&:rows)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def query_value(...) # :nodoc:
|
|
106
|
+
single_value_from_rows(query(...))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def query_values(...) # :nodoc:
|
|
110
|
+
query(...).map(&:first)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def query(...) # :nodoc:
|
|
114
|
+
internal_exec_query(...).rows
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Determines whether the SQL statement is a write query.
|
|
118
|
+
def write_query?(sql)
|
|
119
|
+
raise NotImplementedError
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Executes the SQL statement in the context of this connection and returns
|
|
123
|
+
# the raw result from the connection adapter.
|
|
124
|
+
#
|
|
125
|
+
# Setting +allow_retry+ to true causes the db to reconnect and retry
|
|
126
|
+
# executing the SQL statement in case of a connection-related exception.
|
|
127
|
+
# This option should only be enabled for known idempotent queries.
|
|
128
|
+
#
|
|
129
|
+
# Note: the query is assumed to have side effects and the query cache
|
|
130
|
+
# will be cleared. If the query is read-only, consider using #select_all
|
|
131
|
+
# instead.
|
|
132
|
+
#
|
|
133
|
+
# Note: depending on your database connector, the result returned by this
|
|
134
|
+
# method may be manually memory managed. Consider using #exec_query
|
|
135
|
+
# wrapper instead.
|
|
136
|
+
def execute(sql, name = nil, allow_retry: false)
|
|
137
|
+
internal_execute(sql, name, allow_retry: allow_retry)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Executes +sql+ statement in the context of this connection using
|
|
141
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
|
142
|
+
# the executed +sql+ statement.
|
|
143
|
+
#
|
|
144
|
+
# Note: the query is assumed to have side effects and the query cache
|
|
145
|
+
# will be cleared. If the query is read-only, consider using #select_all
|
|
146
|
+
# instead.
|
|
147
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
|
148
|
+
internal_exec_query(sql, name, binds, prepare: prepare)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Executes insert +sql+ statement in the context of this connection using
|
|
152
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
|
153
|
+
# the executed +sql+ statement.
|
|
154
|
+
# Some adapters support the `returning` keyword argument which allows to control the result of the query:
|
|
155
|
+
# `nil` is the default value and maintains default behavior. If an array of column names is passed -
|
|
156
|
+
# the result will contain values of the specified columns from the inserted row.
|
|
157
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
|
|
158
|
+
sql, binds = sql_for_insert(sql, pk, binds, returning)
|
|
159
|
+
internal_exec_query(sql, name, binds)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Executes delete +sql+ statement in the context of this connection using
|
|
163
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
|
164
|
+
# the executed +sql+ statement.
|
|
165
|
+
def exec_delete(sql, name = nil, binds = [])
|
|
166
|
+
affected_rows(internal_execute(sql, name, binds))
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Executes update +sql+ statement in the context of this connection using
|
|
170
|
+
# +binds+ as the bind substitutes. +name+ is logged along with
|
|
171
|
+
# the executed +sql+ statement.
|
|
172
|
+
def exec_update(sql, name = nil, binds = [])
|
|
173
|
+
affected_rows(internal_execute(sql, name, binds))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def exec_insert_all(sql, name) # :nodoc:
|
|
177
|
+
internal_exec_query(sql, name)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def explain(arel, binds = [], options = []) # :nodoc:
|
|
181
|
+
raise NotImplementedError
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Executes an INSERT query and returns the new record's ID
|
|
185
|
+
#
|
|
186
|
+
# +id_value+ will be returned unless the value is +nil+, in
|
|
187
|
+
# which case the database will attempt to calculate the last inserted
|
|
188
|
+
# id and return that value.
|
|
189
|
+
#
|
|
190
|
+
# If the next id was calculated in advance (as in Oracle), it should be
|
|
191
|
+
# passed in as +id_value+.
|
|
192
|
+
# Some adapters support the `returning` keyword argument which allows defining the return value of the method:
|
|
193
|
+
# `nil` is the default value and maintains default behavior. If an array of column names is passed -
|
|
194
|
+
# an array of is returned from the method representing values of the specified columns from the inserted row.
|
|
195
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
|
|
196
|
+
sql, binds = to_sql_and_binds(arel, binds)
|
|
197
|
+
value = exec_insert(sql, name, binds, pk, sequence_name, returning: returning)
|
|
198
|
+
|
|
199
|
+
return returning_column_values(value) unless returning.nil?
|
|
200
|
+
|
|
201
|
+
id_value || last_inserted_id(value)
|
|
202
|
+
end
|
|
203
|
+
alias create insert
|
|
204
|
+
|
|
205
|
+
# Executes the update statement and returns the number of rows affected.
|
|
206
|
+
def update(arel, name = nil, binds = [])
|
|
207
|
+
sql, binds = to_sql_and_binds(arel, binds)
|
|
208
|
+
exec_update(sql, name, binds)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Executes the delete statement and returns the number of rows affected.
|
|
212
|
+
def delete(arel, name = nil, binds = [])
|
|
213
|
+
sql, binds = to_sql_and_binds(arel, binds)
|
|
214
|
+
exec_delete(sql, name, binds)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Executes the truncate statement.
|
|
218
|
+
def truncate(table_name, name = nil)
|
|
219
|
+
execute(build_truncate_statement(table_name), name)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def truncate_tables(*table_names) # :nodoc:
|
|
223
|
+
table_names -= [pool.schema_migration.table_name, pool.internal_metadata.table_name]
|
|
224
|
+
|
|
225
|
+
return if table_names.empty?
|
|
226
|
+
|
|
227
|
+
disable_referential_integrity do
|
|
228
|
+
statements = build_truncate_statements(table_names)
|
|
229
|
+
execute_batch(statements, "Truncate Tables")
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Runs the given block in a database transaction, and returns the result
|
|
234
|
+
# of the block.
|
|
235
|
+
#
|
|
236
|
+
# == Transaction callbacks
|
|
237
|
+
#
|
|
238
|
+
# #transaction yields an ActiveRecord::Transaction object on which it is
|
|
239
|
+
# possible to register callback:
|
|
240
|
+
#
|
|
241
|
+
# ActiveRecord::Base.transaction do |transaction|
|
|
242
|
+
# transaction.before_commit { puts "before commit!" }
|
|
243
|
+
# transaction.after_commit { puts "after commit!" }
|
|
244
|
+
# transaction.after_rollback { puts "after rollback!" }
|
|
245
|
+
# end
|
|
246
|
+
#
|
|
247
|
+
# == Nested transactions support
|
|
248
|
+
#
|
|
249
|
+
# #transaction calls can be nested. By default, this makes all database
|
|
250
|
+
# statements in the nested transaction block become part of the parent
|
|
251
|
+
# transaction. For example, the following behavior may be surprising:
|
|
252
|
+
#
|
|
253
|
+
# ActiveRecord::Base.transaction do
|
|
254
|
+
# Post.create(title: 'first')
|
|
255
|
+
# ActiveRecord::Base.transaction do
|
|
256
|
+
# Post.create(title: 'second')
|
|
257
|
+
# raise ActiveRecord::Rollback
|
|
258
|
+
# end
|
|
259
|
+
# end
|
|
260
|
+
#
|
|
261
|
+
# This creates both "first" and "second" posts. Reason is the
|
|
262
|
+
# ActiveRecord::Rollback exception in the nested block does not issue a
|
|
263
|
+
# ROLLBACK. Since these exceptions are captured in transaction blocks,
|
|
264
|
+
# the parent block does not see it and the real transaction is committed.
|
|
265
|
+
#
|
|
266
|
+
# Most databases don't support true nested transactions. At the time of
|
|
267
|
+
# writing, the only database that supports true nested transactions that
|
|
268
|
+
# we're aware of, is MS-SQL.
|
|
269
|
+
#
|
|
270
|
+
# In order to get around this problem, #transaction will emulate the effect
|
|
271
|
+
# of nested transactions, by using savepoints:
|
|
272
|
+
# https://dev.mysql.com/doc/refman/en/savepoint.html.
|
|
273
|
+
#
|
|
274
|
+
# It is safe to call this method if a database transaction is already open,
|
|
275
|
+
# i.e. if #transaction is called within another #transaction block. In case
|
|
276
|
+
# of a nested call, #transaction will behave as follows:
|
|
277
|
+
#
|
|
278
|
+
# - The block will be run without doing anything. All database statements
|
|
279
|
+
# that happen within the block are effectively appended to the already
|
|
280
|
+
# open database transaction.
|
|
281
|
+
# - However, if +:requires_new+ is set, the block will be wrapped in a
|
|
282
|
+
# database savepoint acting as a sub-transaction.
|
|
283
|
+
#
|
|
284
|
+
# In order to get a ROLLBACK for the nested transaction you may ask for a
|
|
285
|
+
# real sub-transaction by passing <tt>requires_new: true</tt>.
|
|
286
|
+
# If anything goes wrong, the database rolls back to the beginning of
|
|
287
|
+
# the sub-transaction without rolling back the parent transaction.
|
|
288
|
+
# If we add it to the previous example:
|
|
289
|
+
#
|
|
290
|
+
# ActiveRecord::Base.transaction do
|
|
291
|
+
# Post.create(title: 'first')
|
|
292
|
+
# ActiveRecord::Base.transaction(requires_new: true) do
|
|
293
|
+
# Post.create(title: 'second')
|
|
294
|
+
# raise ActiveRecord::Rollback
|
|
295
|
+
# end
|
|
296
|
+
# end
|
|
297
|
+
#
|
|
298
|
+
# only post with title "first" is created.
|
|
299
|
+
#
|
|
300
|
+
# See ActiveRecord::Transactions to learn more.
|
|
301
|
+
#
|
|
302
|
+
# === Caveats
|
|
303
|
+
#
|
|
304
|
+
# MySQL doesn't support DDL transactions. If you perform a DDL operation,
|
|
305
|
+
# then any created savepoints will be automatically released. For example,
|
|
306
|
+
# if you've created a savepoint, then you execute a CREATE TABLE statement,
|
|
307
|
+
# then the savepoint that was created will be automatically released.
|
|
308
|
+
#
|
|
309
|
+
# This means that, on MySQL, you shouldn't execute DDL operations inside
|
|
310
|
+
# a #transaction call that you know might create a savepoint. Otherwise,
|
|
311
|
+
# #transaction will raise exceptions when it tries to release the
|
|
312
|
+
# already-automatically-released savepoints:
|
|
313
|
+
#
|
|
314
|
+
# Model.lease_connection.transaction do # BEGIN
|
|
315
|
+
# Model.lease_connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
|
316
|
+
# Model.lease_connection.create_table(...)
|
|
317
|
+
# # active_record_1 now automatically released
|
|
318
|
+
# end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
|
|
319
|
+
# end
|
|
320
|
+
#
|
|
321
|
+
# == Transaction isolation
|
|
322
|
+
#
|
|
323
|
+
# If your database supports setting the isolation level for a transaction, you can set
|
|
324
|
+
# it like so:
|
|
325
|
+
#
|
|
326
|
+
# Post.transaction(isolation: :serializable) do
|
|
327
|
+
# # ...
|
|
328
|
+
# end
|
|
329
|
+
#
|
|
330
|
+
# Valid isolation levels are:
|
|
331
|
+
#
|
|
332
|
+
# * <tt>:read_uncommitted</tt>
|
|
333
|
+
# * <tt>:read_committed</tt>
|
|
334
|
+
# * <tt>:repeatable_read</tt>
|
|
335
|
+
# * <tt>:serializable</tt>
|
|
336
|
+
#
|
|
337
|
+
# You should consult the documentation for your database to understand the
|
|
338
|
+
# semantics of these different levels:
|
|
339
|
+
#
|
|
340
|
+
# * https://www.postgresql.org/docs/current/static/transaction-iso.html
|
|
341
|
+
# * https://dev.mysql.com/doc/refman/en/set-transaction.html
|
|
342
|
+
#
|
|
343
|
+
# An ActiveRecord::TransactionIsolationError will be raised if:
|
|
344
|
+
#
|
|
345
|
+
# * The adapter does not support setting the isolation level
|
|
346
|
+
# * You are joining an existing open transaction
|
|
347
|
+
# * You are creating a nested (savepoint) transaction
|
|
348
|
+
#
|
|
349
|
+
# The mysql2, trilogy, and postgresql adapters support setting the transaction
|
|
350
|
+
# isolation level.
|
|
351
|
+
# :args: (requires_new: nil, isolation: nil, &block)
|
|
352
|
+
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
|
|
353
|
+
if !requires_new && current_transaction.joinable?
|
|
354
|
+
if isolation
|
|
355
|
+
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
|
356
|
+
end
|
|
357
|
+
yield current_transaction.user_transaction
|
|
358
|
+
else
|
|
359
|
+
within_new_transaction(isolation: isolation, joinable: joinable, &block)
|
|
360
|
+
end
|
|
361
|
+
rescue ActiveRecord::Rollback
|
|
362
|
+
# rollbacks are silently swallowed
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
attr_reader :transaction_manager # :nodoc:
|
|
366
|
+
|
|
367
|
+
delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction,
|
|
368
|
+
:commit_transaction, :rollback_transaction, :materialize_transactions,
|
|
369
|
+
:disable_lazy_transactions!, :enable_lazy_transactions!, :dirty_current_transaction,
|
|
370
|
+
to: :transaction_manager
|
|
371
|
+
|
|
372
|
+
def mark_transaction_written_if_write(sql) # :nodoc:
|
|
373
|
+
transaction = current_transaction
|
|
374
|
+
if transaction.open?
|
|
375
|
+
transaction.written ||= write_query?(sql)
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def transaction_open?
|
|
380
|
+
current_transaction.open?
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def reset_transaction(restore: false) # :nodoc:
|
|
384
|
+
# Store the existing transaction state to the side
|
|
385
|
+
old_state = @transaction_manager if restore && @transaction_manager&.restorable?
|
|
386
|
+
|
|
387
|
+
@transaction_manager = ConnectionAdapters::TransactionManager.new(self)
|
|
388
|
+
|
|
389
|
+
if block_given?
|
|
390
|
+
# Reconfigure the connection without any transaction state in the way
|
|
391
|
+
result = yield
|
|
392
|
+
|
|
393
|
+
# Now the connection's fully established, we can swap back
|
|
394
|
+
if old_state
|
|
395
|
+
@transaction_manager = old_state
|
|
396
|
+
@transaction_manager.restore_transactions
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
result
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
|
|
404
|
+
# can be called.
|
|
405
|
+
def add_transaction_record(record, ensure_finalize = true)
|
|
406
|
+
current_transaction.add_record(record, ensure_finalize)
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Begins the transaction (and turns off auto-committing).
|
|
410
|
+
def begin_db_transaction() end
|
|
411
|
+
|
|
412
|
+
def begin_deferred_transaction(isolation_level = nil) # :nodoc:
|
|
413
|
+
if isolation_level
|
|
414
|
+
begin_isolated_db_transaction(isolation_level)
|
|
415
|
+
else
|
|
416
|
+
begin_db_transaction
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def transaction_isolation_levels
|
|
421
|
+
{
|
|
422
|
+
read_uncommitted: "READ UNCOMMITTED",
|
|
423
|
+
read_committed: "READ COMMITTED",
|
|
424
|
+
repeatable_read: "REPEATABLE READ",
|
|
425
|
+
serializable: "SERIALIZABLE"
|
|
426
|
+
}
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# Begins the transaction with the isolation level set. Raises an error by
|
|
430
|
+
# default; adapters that support setting the isolation level should implement
|
|
431
|
+
# this method.
|
|
432
|
+
def begin_isolated_db_transaction(isolation)
|
|
433
|
+
raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Hook point called after an isolated DB transaction is committed
|
|
437
|
+
# or rolled back.
|
|
438
|
+
# Most adapters don't need to implement anything because the isolation
|
|
439
|
+
# level is set on a per transaction basis.
|
|
440
|
+
# But some databases like SQLite set it on a per connection level
|
|
441
|
+
# and need to explicitly reset it after commit or rollback.
|
|
442
|
+
def reset_isolation_level
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Commits the transaction (and turns on auto-committing).
|
|
446
|
+
def commit_db_transaction() end
|
|
447
|
+
|
|
448
|
+
# Rolls back the transaction (and turns on auto-committing). Must be
|
|
449
|
+
# done if the transaction block raises an exception or returns false.
|
|
450
|
+
def rollback_db_transaction
|
|
451
|
+
exec_rollback_db_transaction
|
|
452
|
+
rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::ConnectionFailed
|
|
453
|
+
# Connection's gone; that counts as a rollback
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def exec_rollback_db_transaction() end # :nodoc:
|
|
457
|
+
|
|
458
|
+
def restart_db_transaction
|
|
459
|
+
exec_restart_db_transaction
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def exec_restart_db_transaction() end # :nodoc:
|
|
463
|
+
|
|
464
|
+
def rollback_to_savepoint(name = nil)
|
|
465
|
+
exec_rollback_to_savepoint(name)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def default_sequence_name(table, column)
|
|
469
|
+
nil
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# Set the sequence to the max value of the table's column.
|
|
473
|
+
def reset_sequence!(table, column, sequence = nil)
|
|
474
|
+
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
# Inserts the given fixture into the table. Overridden in adapters that require
|
|
478
|
+
# something beyond a simple insert (e.g. Oracle).
|
|
479
|
+
# Most of adapters should implement +insert_fixtures_set+ that leverages bulk SQL insert.
|
|
480
|
+
# We keep this method to provide fallback
|
|
481
|
+
# for databases like SQLite that do not support bulk inserts.
|
|
482
|
+
def insert_fixture(fixture, table_name)
|
|
483
|
+
execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
|
487
|
+
fixture_inserts = build_fixture_statements(fixture_set)
|
|
488
|
+
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
|
|
489
|
+
statements = table_deletes + fixture_inserts
|
|
490
|
+
|
|
491
|
+
transaction(requires_new: true) do
|
|
492
|
+
disable_referential_integrity do
|
|
493
|
+
execute_batch(statements, "Fixtures Load")
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def empty_insert_statement_value(primary_key = nil)
|
|
499
|
+
"DEFAULT VALUES"
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
|
503
|
+
#
|
|
504
|
+
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
|
505
|
+
# should look like an integer, or an Arel SQL literal.
|
|
506
|
+
#
|
|
507
|
+
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
|
508
|
+
def sanitize_limit(limit)
|
|
509
|
+
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
|
|
510
|
+
limit
|
|
511
|
+
else
|
|
512
|
+
Integer(limit)
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
# Fixture value is quoted by Arel, however scalar values
|
|
517
|
+
# are not quotable. In this case we want to convert
|
|
518
|
+
# the column value to YAML.
|
|
519
|
+
def with_yaml_fallback(value) # :nodoc:
|
|
520
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
|
521
|
+
YAML.dump(value)
|
|
522
|
+
else
|
|
523
|
+
value
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# This is a safe default, even if not high precision on all databases
|
|
528
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP = Arel.sql("CURRENT_TIMESTAMP", retryable: true).freeze # :nodoc:
|
|
529
|
+
private_constant :HIGH_PRECISION_CURRENT_TIMESTAMP
|
|
530
|
+
|
|
531
|
+
# Returns an Arel SQL literal for the CURRENT_TIMESTAMP for usage with
|
|
532
|
+
# arbitrary precision date/time columns.
|
|
533
|
+
#
|
|
534
|
+
# Adapters supporting datetime with precision should override this to
|
|
535
|
+
# provide as much precision as is available.
|
|
536
|
+
def high_precision_current_timestamp
|
|
537
|
+
HIGH_PRECISION_CURRENT_TIMESTAMP
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# Same as raw_execute but returns an ActiveRecord::Result object.
|
|
541
|
+
def raw_exec_query(...) # :nodoc:
|
|
542
|
+
cast_result(raw_execute(...))
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# Execute a query and returns an ActiveRecord::Result
|
|
546
|
+
def internal_exec_query(...) # :nodoc:
|
|
547
|
+
cast_result(internal_execute(...))
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
private
|
|
551
|
+
# Lowest level way to execute a query. Doesn't check for illegal writes, doesn't annotate queries, yields a native result object.
|
|
552
|
+
def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
|
|
553
|
+
type_casted_binds = type_casted_binds(binds)
|
|
554
|
+
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
|
555
|
+
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
556
|
+
perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:)
|
|
562
|
+
raise NotImplementedError
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# Receive a native adapter result object and returns an ActiveRecord::Result object.
|
|
566
|
+
def cast_result(raw_result)
|
|
567
|
+
raise NotImplementedError
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def affected_rows(raw_result)
|
|
571
|
+
raise NotImplementedError
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def preprocess_query(sql)
|
|
575
|
+
check_if_write_query(sql)
|
|
576
|
+
mark_transaction_written_if_write(sql)
|
|
577
|
+
|
|
578
|
+
# We call tranformers after the write checks so we don't add extra parsing work.
|
|
579
|
+
# This means we assume no transformer whille change a read for a write
|
|
580
|
+
# but it would be insane to do such a thing.
|
|
581
|
+
ActiveRecord.query_transformers.each do |transformer|
|
|
582
|
+
sql = transformer.call(sql, self)
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
sql
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
# Same as #internal_exec_query, but yields a native adapter result
|
|
589
|
+
def internal_execute(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, &block)
|
|
590
|
+
sql = preprocess_query(sql)
|
|
591
|
+
raw_execute(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions, &block)
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def execute_batch(statements, name = nil, **kwargs)
|
|
595
|
+
statements.each do |statement|
|
|
596
|
+
raw_execute(statement, name, **kwargs)
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
|
|
601
|
+
private_constant :DEFAULT_INSERT_VALUE
|
|
602
|
+
|
|
603
|
+
def default_insert_value(column)
|
|
604
|
+
DEFAULT_INSERT_VALUE
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def build_fixture_sql(fixtures, table_name)
|
|
608
|
+
columns = schema_cache.columns_hash(table_name).reject { |_, column| supports_virtual_columns? && column.virtual? }
|
|
609
|
+
|
|
610
|
+
values_list = fixtures.map do |fixture|
|
|
611
|
+
fixture = fixture.stringify_keys
|
|
612
|
+
|
|
613
|
+
unknown_columns = fixture.keys - columns.keys
|
|
614
|
+
if unknown_columns.any?
|
|
615
|
+
raise Fixture::FixtureError, %(table "#{table_name}" has no columns named #{unknown_columns.map(&:inspect).join(', ')}.)
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
columns.map do |name, column|
|
|
619
|
+
if fixture.key?(name)
|
|
620
|
+
type = lookup_cast_type_from_column(column)
|
|
621
|
+
with_yaml_fallback(type.serialize(fixture[name]))
|
|
622
|
+
else
|
|
623
|
+
default_insert_value(column)
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
table = Arel::Table.new(table_name)
|
|
629
|
+
manager = Arel::InsertManager.new(table)
|
|
630
|
+
|
|
631
|
+
if values_list.size == 1
|
|
632
|
+
values = values_list.shift
|
|
633
|
+
new_values = []
|
|
634
|
+
columns.each_key.with_index { |column, i|
|
|
635
|
+
unless values[i].equal?(DEFAULT_INSERT_VALUE)
|
|
636
|
+
new_values << values[i]
|
|
637
|
+
manager.columns << table[column]
|
|
638
|
+
end
|
|
639
|
+
}
|
|
640
|
+
values_list << new_values
|
|
641
|
+
else
|
|
642
|
+
columns.each_key { |column| manager.columns << table[column] }
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
manager.values = manager.create_values_list(values_list)
|
|
646
|
+
visitor.compile(manager.ast)
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
def build_fixture_statements(fixture_set)
|
|
650
|
+
fixture_set.filter_map do |table_name, fixtures|
|
|
651
|
+
next if fixtures.empty?
|
|
652
|
+
build_fixture_sql(fixtures, table_name)
|
|
653
|
+
end
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
def build_truncate_statement(table_name)
|
|
657
|
+
"TRUNCATE TABLE #{quote_table_name(table_name)}"
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def build_truncate_statements(table_names)
|
|
661
|
+
table_names.map do |table_name|
|
|
662
|
+
build_truncate_statement(table_name)
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def combine_multi_statements(total_sql)
|
|
667
|
+
total_sql.join(";\n")
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
# Returns an ActiveRecord::Result instance.
|
|
671
|
+
def select(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false)
|
|
672
|
+
if async && async_enabled?
|
|
673
|
+
if current_transaction.joinable?
|
|
674
|
+
raise AsynchronousQueryInsideTransactionError, "Asynchronous queries are not allowed inside transactions"
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
# We make sure to run query transformers on the orignal thread
|
|
678
|
+
sql = preprocess_query(sql)
|
|
679
|
+
future_result = async.new(
|
|
680
|
+
pool,
|
|
681
|
+
sql,
|
|
682
|
+
name,
|
|
683
|
+
binds,
|
|
684
|
+
prepare: prepare,
|
|
685
|
+
)
|
|
686
|
+
if supports_concurrent_connections? && !current_transaction.joinable?
|
|
687
|
+
future_result.schedule!(ActiveRecord::Base.asynchronous_queries_session)
|
|
688
|
+
else
|
|
689
|
+
future_result.execute!(self)
|
|
690
|
+
end
|
|
691
|
+
future_result
|
|
692
|
+
else
|
|
693
|
+
result = internal_exec_query(sql, name, binds, prepare: prepare, allow_retry: allow_retry)
|
|
694
|
+
if async
|
|
695
|
+
FutureResult.wrap(result)
|
|
696
|
+
else
|
|
697
|
+
result
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
def sql_for_insert(sql, pk, binds, returning) # :nodoc:
|
|
703
|
+
if supports_insert_returning?
|
|
704
|
+
if pk.nil?
|
|
705
|
+
# Extract the table from the insert sql. Yuck.
|
|
706
|
+
table_ref = extract_table_ref_from_insert_sql(sql)
|
|
707
|
+
pk = primary_key(table_ref) if table_ref
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
returning_columns = returning || Array(pk)
|
|
711
|
+
|
|
712
|
+
returning_columns_statement = returning_columns.map { |c| quote_column_name(c) }.join(", ")
|
|
713
|
+
sql = "#{sql} RETURNING #{returning_columns_statement}" if returning_columns.any?
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
[sql, binds]
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
def last_inserted_id(result)
|
|
720
|
+
single_value_from_rows(result.rows)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
def returning_column_values(result)
|
|
724
|
+
[last_inserted_id(result)]
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
def single_value_from_rows(rows)
|
|
728
|
+
row = rows.first
|
|
729
|
+
row && row.first
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
def arel_from_relation(relation)
|
|
733
|
+
if relation.is_a?(Relation)
|
|
734
|
+
relation.arel
|
|
735
|
+
else
|
|
736
|
+
relation
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
def extract_table_ref_from_insert_sql(sql)
|
|
741
|
+
if sql =~ /into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im
|
|
742
|
+
$1.delete('"').strip
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
end
|
|
746
|
+
end
|
|
747
|
+
end
|