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,249 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
###
|
|
5
|
+
# = Active Record \Result
|
|
6
|
+
#
|
|
7
|
+
# This class encapsulates a result returned from calling
|
|
8
|
+
# {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
|
|
9
|
+
# on any database connection adapter. For example:
|
|
10
|
+
#
|
|
11
|
+
# result = ActiveRecord::Base.lease_connection.exec_query('SELECT id, title, body FROM posts')
|
|
12
|
+
# result # => #<ActiveRecord::Result:0xdeadbeef>
|
|
13
|
+
#
|
|
14
|
+
# # Get the column names of the result:
|
|
15
|
+
# result.columns
|
|
16
|
+
# # => ["id", "title", "body"]
|
|
17
|
+
#
|
|
18
|
+
# # Get the record values of the result:
|
|
19
|
+
# result.rows
|
|
20
|
+
# # => [[1, "title_1", "body_1"],
|
|
21
|
+
# [2, "title_2", "body_2"],
|
|
22
|
+
# ...
|
|
23
|
+
# ]
|
|
24
|
+
#
|
|
25
|
+
# # Get an array of hashes representing the result (column => value):
|
|
26
|
+
# result.to_a
|
|
27
|
+
# # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
|
|
28
|
+
# {"id" => 2, "title" => "title_2", "body" => "body_2"},
|
|
29
|
+
# ...
|
|
30
|
+
# ]
|
|
31
|
+
#
|
|
32
|
+
# # ActiveRecord::Result also includes Enumerable.
|
|
33
|
+
# result.each do |row|
|
|
34
|
+
# puts row['title'] + " " + row['body']
|
|
35
|
+
# end
|
|
36
|
+
class Result
|
|
37
|
+
include Enumerable
|
|
38
|
+
|
|
39
|
+
class IndexedRow
|
|
40
|
+
def initialize(column_indexes, row)
|
|
41
|
+
@column_indexes = column_indexes
|
|
42
|
+
@row = row
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def size
|
|
46
|
+
@column_indexes.size
|
|
47
|
+
end
|
|
48
|
+
alias_method :length, :size
|
|
49
|
+
|
|
50
|
+
def each_key(&block)
|
|
51
|
+
@column_indexes.each_key(&block)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def keys
|
|
55
|
+
@column_indexes.keys
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def ==(other)
|
|
59
|
+
if other.is_a?(Hash)
|
|
60
|
+
to_hash == other
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def key?(column)
|
|
67
|
+
@column_indexes.key?(column)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def fetch(column)
|
|
71
|
+
if index = @column_indexes[column]
|
|
72
|
+
@row[index]
|
|
73
|
+
elsif block_given?
|
|
74
|
+
yield
|
|
75
|
+
else
|
|
76
|
+
raise KeyError, "key not found: #{column.inspect}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def [](column)
|
|
81
|
+
if index = @column_indexes[column]
|
|
82
|
+
@row[index]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_h
|
|
87
|
+
@column_indexes.transform_values { |index| @row[index] }
|
|
88
|
+
end
|
|
89
|
+
alias_method :to_hash, :to_h
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
attr_reader :columns, :rows, :column_types
|
|
93
|
+
|
|
94
|
+
def self.empty(async: false) # :nodoc:
|
|
95
|
+
if async
|
|
96
|
+
EMPTY_ASYNC
|
|
97
|
+
else
|
|
98
|
+
EMPTY
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def initialize(columns, rows, column_types = nil)
|
|
103
|
+
# We freeze the strings to prevent them getting duped when
|
|
104
|
+
# used as keys in ActiveRecord::Base's @attributes hash
|
|
105
|
+
@columns = columns.each(&:-@).freeze
|
|
106
|
+
@rows = rows
|
|
107
|
+
@hash_rows = nil
|
|
108
|
+
@column_types = column_types || EMPTY_HASH
|
|
109
|
+
@column_indexes = nil
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Returns true if this result set includes the column named +name+
|
|
113
|
+
def includes_column?(name)
|
|
114
|
+
@columns.include? name
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns the number of elements in the rows array.
|
|
118
|
+
def length
|
|
119
|
+
@rows.length
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Calls the given block once for each element in row collection, passing
|
|
123
|
+
# row as parameter. Each row is a Hash-like, read only object.
|
|
124
|
+
#
|
|
125
|
+
# To get real hashes, use +.to_a.each+.
|
|
126
|
+
#
|
|
127
|
+
# Returns an +Enumerator+ if no block is given.
|
|
128
|
+
def each(&block)
|
|
129
|
+
if block_given?
|
|
130
|
+
indexed_rows.each(&block)
|
|
131
|
+
else
|
|
132
|
+
indexed_rows.to_enum { @rows.size }
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Returns true if there are no records, otherwise false.
|
|
137
|
+
def empty?
|
|
138
|
+
rows.empty?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Returns an array of hashes representing each row record.
|
|
142
|
+
def to_ary
|
|
143
|
+
hash_rows
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
alias :to_a :to_ary
|
|
147
|
+
|
|
148
|
+
def [](idx)
|
|
149
|
+
hash_rows[idx]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Returns the last record from the rows collection.
|
|
153
|
+
def last(n = nil)
|
|
154
|
+
n ? hash_rows.last(n) : hash_rows.last
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def result # :nodoc:
|
|
158
|
+
self
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def cancel # :nodoc:
|
|
162
|
+
self
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def cast_values(type_overrides = {}) # :nodoc:
|
|
166
|
+
if columns.one?
|
|
167
|
+
# Separated to avoid allocating an array per row
|
|
168
|
+
|
|
169
|
+
type = if type_overrides.is_a?(Array)
|
|
170
|
+
type_overrides.first
|
|
171
|
+
else
|
|
172
|
+
column_type(columns.first, 0, type_overrides)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
rows.map do |(value)|
|
|
176
|
+
type.deserialize(value)
|
|
177
|
+
end
|
|
178
|
+
else
|
|
179
|
+
types = if type_overrides.is_a?(Array)
|
|
180
|
+
type_overrides
|
|
181
|
+
else
|
|
182
|
+
columns.map.with_index { |name, i| column_type(name, i, type_overrides) }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
rows.map do |values|
|
|
186
|
+
Array.new(values.size) { |i| types[i].deserialize(values[i]) }
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def initialize_copy(other)
|
|
192
|
+
@rows = rows.dup
|
|
193
|
+
@column_types = column_types.dup
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def freeze # :nodoc:
|
|
197
|
+
hash_rows.freeze
|
|
198
|
+
indexed_rows.freeze
|
|
199
|
+
super
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def column_indexes # :nodoc:
|
|
203
|
+
@column_indexes ||= begin
|
|
204
|
+
index = 0
|
|
205
|
+
hash = {}
|
|
206
|
+
length = columns.length
|
|
207
|
+
while index < length
|
|
208
|
+
hash[columns[index]] = index
|
|
209
|
+
index += 1
|
|
210
|
+
end
|
|
211
|
+
hash.freeze
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
private
|
|
216
|
+
def column_type(name, index, type_overrides)
|
|
217
|
+
type_overrides.fetch(name) do
|
|
218
|
+
column_types.fetch(index) do
|
|
219
|
+
column_types.fetch(name, Type.default_value)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def indexed_rows
|
|
225
|
+
@indexed_rows ||= begin
|
|
226
|
+
columns = column_indexes
|
|
227
|
+
@rows.map { |row| IndexedRow.new(columns, row) }.freeze
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def hash_rows
|
|
232
|
+
# We use transform_values to rows.
|
|
233
|
+
# This is faster because we avoid any reallocs and avoid hashing entirely.
|
|
234
|
+
@hash_rows ||= @rows.map do |row|
|
|
235
|
+
column_indexes.transform_values { |index| row[index] }
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
empty_array = [].freeze
|
|
240
|
+
EMPTY_HASH = {}.freeze
|
|
241
|
+
private_constant :EMPTY_HASH
|
|
242
|
+
|
|
243
|
+
EMPTY = new(empty_array, empty_array, EMPTY_HASH).freeze
|
|
244
|
+
private_constant :EMPTY
|
|
245
|
+
|
|
246
|
+
EMPTY_ASYNC = FutureResult.wrap(EMPTY).freeze
|
|
247
|
+
private_constant :EMPTY_ASYNC
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
# This is a thread locals registry for Active Record. For example:
|
|
5
|
+
#
|
|
6
|
+
# ActiveRecord::RuntimeRegistry.sql_runtime
|
|
7
|
+
#
|
|
8
|
+
# returns the connection handler local to the current unit of execution (either thread of fiber).
|
|
9
|
+
module RuntimeRegistry # :nodoc:
|
|
10
|
+
extend self
|
|
11
|
+
|
|
12
|
+
def sql_runtime
|
|
13
|
+
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] ||= 0.0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def sql_runtime=(runtime)
|
|
17
|
+
ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def async_sql_runtime
|
|
21
|
+
ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] ||= 0.0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def async_sql_runtime=(runtime)
|
|
25
|
+
ActiveSupport::IsolatedExecutionState[:active_record_async_sql_runtime] = runtime
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def queries_count
|
|
29
|
+
ActiveSupport::IsolatedExecutionState[:active_record_queries_count] ||= 0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def queries_count=(count)
|
|
33
|
+
ActiveSupport::IsolatedExecutionState[:active_record_queries_count] = count
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def cached_queries_count
|
|
37
|
+
ActiveSupport::IsolatedExecutionState[:active_record_cached_queries_count] ||= 0
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def cached_queries_count=(count)
|
|
41
|
+
ActiveSupport::IsolatedExecutionState[:active_record_cached_queries_count] = count
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def reset
|
|
45
|
+
reset_runtimes
|
|
46
|
+
reset_queries_count
|
|
47
|
+
reset_cached_queries_count
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def reset_runtimes
|
|
51
|
+
rt, self.sql_runtime = sql_runtime, 0.0
|
|
52
|
+
self.async_sql_runtime = 0.0
|
|
53
|
+
rt
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def reset_queries_count
|
|
57
|
+
qc = queries_count
|
|
58
|
+
self.queries_count = 0
|
|
59
|
+
qc
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def reset_cached_queries_count
|
|
63
|
+
qc = cached_queries_count
|
|
64
|
+
self.cached_queries_count = 0
|
|
65
|
+
qc
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
ActiveSupport::Notifications.monotonic_subscribe("sql.active_record") do |name, start, finish, id, payload|
|
|
71
|
+
unless ["SCHEMA", "TRANSACTION"].include?(payload[:name])
|
|
72
|
+
ActiveRecord::RuntimeRegistry.queries_count += 1
|
|
73
|
+
ActiveRecord::RuntimeRegistry.cached_queries_count += 1 if payload[:cached]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
runtime = (finish - start) * 1_000.0
|
|
77
|
+
|
|
78
|
+
if payload[:async]
|
|
79
|
+
ActiveRecord::RuntimeRegistry.async_sql_runtime += (runtime - payload[:lock_wait])
|
|
80
|
+
end
|
|
81
|
+
ActiveRecord::RuntimeRegistry.sql_runtime += runtime
|
|
82
|
+
end
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module Sanitization
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
# Accepts an array of SQL conditions and sanitizes them into a valid
|
|
9
|
+
# SQL fragment for a WHERE clause.
|
|
10
|
+
#
|
|
11
|
+
# sanitize_sql_for_conditions(["name=? and group_id=?", "foo'bar", 4])
|
|
12
|
+
# # => "name='foo''bar' and group_id=4"
|
|
13
|
+
#
|
|
14
|
+
# sanitize_sql_for_conditions(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
|
15
|
+
# # => "name='foo''bar' and group_id='4'"
|
|
16
|
+
#
|
|
17
|
+
# sanitize_sql_for_conditions(["name='%s' and group_id='%s'", "foo'bar", 4])
|
|
18
|
+
# # => "name='foo''bar' and group_id='4'"
|
|
19
|
+
#
|
|
20
|
+
# This method will NOT sanitize an SQL string since it won't contain
|
|
21
|
+
# any conditions in it and will return the string as is.
|
|
22
|
+
#
|
|
23
|
+
# sanitize_sql_for_conditions("name='foo''bar' and group_id='4'")
|
|
24
|
+
# # => "name='foo''bar' and group_id='4'"
|
|
25
|
+
#
|
|
26
|
+
# Note that this sanitization method is not schema-aware, hence won't do any type casting
|
|
27
|
+
# and will directly use the database adapter's +quote+ method.
|
|
28
|
+
# For MySQL specifically this means that numeric parameters will be quoted as strings
|
|
29
|
+
# to prevent query manipulation attacks.
|
|
30
|
+
#
|
|
31
|
+
# sanitize_sql_for_conditions(["role = ?", 0])
|
|
32
|
+
# # => "role = '0'"
|
|
33
|
+
def sanitize_sql_for_conditions(condition)
|
|
34
|
+
return nil if condition.blank?
|
|
35
|
+
|
|
36
|
+
case condition
|
|
37
|
+
when Array; sanitize_sql_array(condition)
|
|
38
|
+
else condition
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
alias :sanitize_sql :sanitize_sql_for_conditions
|
|
42
|
+
|
|
43
|
+
# Accepts an array or hash of SQL conditions and sanitizes them into
|
|
44
|
+
# a valid SQL fragment for a SET clause.
|
|
45
|
+
#
|
|
46
|
+
# sanitize_sql_for_assignment(["name=? and group_id=?", nil, 4])
|
|
47
|
+
# # => "name=NULL and group_id=4"
|
|
48
|
+
#
|
|
49
|
+
# sanitize_sql_for_assignment(["name=:name and group_id=:group_id", name: nil, group_id: 4])
|
|
50
|
+
# # => "name=NULL and group_id=4"
|
|
51
|
+
#
|
|
52
|
+
# Post.sanitize_sql_for_assignment({ name: nil, group_id: 4 })
|
|
53
|
+
# # => "`posts`.`name` = NULL, `posts`.`group_id` = 4"
|
|
54
|
+
#
|
|
55
|
+
# This method will NOT sanitize an SQL string since it won't contain
|
|
56
|
+
# any conditions in it and will return the string as is.
|
|
57
|
+
#
|
|
58
|
+
# sanitize_sql_for_assignment("name=NULL and group_id='4'")
|
|
59
|
+
# # => "name=NULL and group_id='4'"
|
|
60
|
+
#
|
|
61
|
+
# Note that this sanitization method is not schema-aware, hence won't do any type casting
|
|
62
|
+
# and will directly use the database adapter's +quote+ method.
|
|
63
|
+
# For MySQL specifically this means that numeric parameters will be quoted as strings
|
|
64
|
+
# to prevent query manipulation attacks.
|
|
65
|
+
#
|
|
66
|
+
# sanitize_sql_for_assignment(["role = ?", 0])
|
|
67
|
+
# # => "role = '0'"
|
|
68
|
+
def sanitize_sql_for_assignment(assignments, default_table_name = table_name)
|
|
69
|
+
case assignments
|
|
70
|
+
when Array; sanitize_sql_array(assignments)
|
|
71
|
+
when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
|
|
72
|
+
else assignments
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Accepts an array, or string of SQL conditions and sanitizes
|
|
77
|
+
# them into a valid SQL fragment for an ORDER clause.
|
|
78
|
+
#
|
|
79
|
+
# sanitize_sql_for_order([Arel.sql("field(id, ?)"), [1,3,2]])
|
|
80
|
+
# # => "field(id, 1,3,2)"
|
|
81
|
+
#
|
|
82
|
+
# sanitize_sql_for_order("id ASC")
|
|
83
|
+
# # => "id ASC"
|
|
84
|
+
def sanitize_sql_for_order(condition)
|
|
85
|
+
if condition.is_a?(Array) && condition.first.to_s.include?("?")
|
|
86
|
+
disallow_raw_sql!(
|
|
87
|
+
[condition.first],
|
|
88
|
+
permit: adapter_class.column_name_with_order_matcher
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Ensure we aren't dealing with a subclass of String that might
|
|
92
|
+
# override methods we use (e.g. Arel::Nodes::SqlLiteral).
|
|
93
|
+
if condition.first.kind_of?(String) && !condition.first.instance_of?(String)
|
|
94
|
+
condition = [String.new(condition.first), *condition[1..-1]]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
Arel.sql(sanitize_sql_array(condition))
|
|
98
|
+
else
|
|
99
|
+
condition
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
|
|
104
|
+
#
|
|
105
|
+
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
|
106
|
+
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
|
107
|
+
def sanitize_sql_hash_for_assignment(attrs, table)
|
|
108
|
+
with_connection do |c|
|
|
109
|
+
attrs.map do |attr, value|
|
|
110
|
+
type = type_for_attribute(attr)
|
|
111
|
+
value = type.serialize(type.cast(value))
|
|
112
|
+
"#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
|
|
113
|
+
end.join(", ")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Sanitizes a +string+ so that it is safe to use within an SQL
|
|
118
|
+
# LIKE statement. This method uses +escape_character+ to escape all
|
|
119
|
+
# occurrences of itself, "_" and "%".
|
|
120
|
+
#
|
|
121
|
+
# sanitize_sql_like("100% true!")
|
|
122
|
+
# # => "100\\% true!"
|
|
123
|
+
#
|
|
124
|
+
# sanitize_sql_like("snake_cased_string")
|
|
125
|
+
# # => "snake\\_cased\\_string"
|
|
126
|
+
#
|
|
127
|
+
# sanitize_sql_like("100% true!", "!")
|
|
128
|
+
# # => "100!% true!!"
|
|
129
|
+
#
|
|
130
|
+
# sanitize_sql_like("snake_cased_string", "!")
|
|
131
|
+
# # => "snake!_cased!_string"
|
|
132
|
+
def sanitize_sql_like(string, escape_character = "\\")
|
|
133
|
+
if string.include?(escape_character) && escape_character != "%" && escape_character != "_"
|
|
134
|
+
string = string.gsub(escape_character, '\0\0')
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
string.gsub(/(?=[%_])/, escape_character)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Accepts an array of conditions. The array has each value
|
|
141
|
+
# sanitized and interpolated into the SQL statement. If using named bind
|
|
142
|
+
# variables in SQL statements where a colon is required verbatim use a
|
|
143
|
+
# backslash to escape.
|
|
144
|
+
#
|
|
145
|
+
# sanitize_sql_array(["name=? and group_id=?", "foo'bar", 4])
|
|
146
|
+
# # => "name='foo''bar' and group_id=4"
|
|
147
|
+
#
|
|
148
|
+
# sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4])
|
|
149
|
+
# # => "name='foo''bar' and group_id=4"
|
|
150
|
+
#
|
|
151
|
+
# sanitize_sql_array(["TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12\\:MI\\:SS')", date: "foo"])
|
|
152
|
+
# # => "TO_TIMESTAMP('foo', 'YYYY/MM/DD HH12:MI:SS')"
|
|
153
|
+
#
|
|
154
|
+
# sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4])
|
|
155
|
+
# # => "name='foo''bar' and group_id='4'"
|
|
156
|
+
#
|
|
157
|
+
# Note that this sanitization method is not schema-aware, hence won't do any type casting
|
|
158
|
+
# and will directly use the database adapter's +quote+ method.
|
|
159
|
+
# For MySQL specifically this means that numeric parameters will be quoted as strings
|
|
160
|
+
# to prevent query manipulation attacks.
|
|
161
|
+
#
|
|
162
|
+
# sanitize_sql_array(["role = ?", 0])
|
|
163
|
+
# # => "role = '0'"
|
|
164
|
+
def sanitize_sql_array(ary)
|
|
165
|
+
statement, *values = ary
|
|
166
|
+
if values.first.is_a?(Hash) && /:\w+/.match?(statement)
|
|
167
|
+
with_connection do |c|
|
|
168
|
+
replace_named_bind_variables(c, statement, values.first)
|
|
169
|
+
end
|
|
170
|
+
elsif statement.include?("?")
|
|
171
|
+
with_connection do |c|
|
|
172
|
+
replace_bind_variables(c, statement, values)
|
|
173
|
+
end
|
|
174
|
+
elsif statement.blank?
|
|
175
|
+
statement
|
|
176
|
+
else
|
|
177
|
+
with_connection do |c|
|
|
178
|
+
statement % values.collect { |value| c.quote_string(value.to_s) }
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def disallow_raw_sql!(args, permit: adapter_class.column_name_matcher) # :nodoc:
|
|
184
|
+
unexpected = nil
|
|
185
|
+
args.each do |arg|
|
|
186
|
+
next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
|
|
187
|
+
(unexpected ||= []) << arg
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
if unexpected
|
|
191
|
+
raise(ActiveRecord::UnknownAttributeReference,
|
|
192
|
+
"Dangerous query method (method whose arguments are used as raw " \
|
|
193
|
+
"SQL) called with non-attribute argument(s): " \
|
|
194
|
+
"#{unexpected.map(&:inspect).join(", ")}." \
|
|
195
|
+
"This method should not be called with user-provided values, such as request " \
|
|
196
|
+
"parameters or model attributes. Known-safe values can be passed " \
|
|
197
|
+
"by wrapping them in Arel.sql()."
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
private
|
|
203
|
+
def replace_bind_variables(connection, statement, values)
|
|
204
|
+
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
|
|
205
|
+
bound = values.dup
|
|
206
|
+
statement.gsub(/\?/) do
|
|
207
|
+
replace_bind_variable(connection, bound.shift)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def replace_bind_variable(connection, value)
|
|
212
|
+
if ActiveRecord::Relation === value
|
|
213
|
+
value.to_sql
|
|
214
|
+
else
|
|
215
|
+
quote_bound_value(connection, value)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def replace_named_bind_variables(connection, statement, bind_vars)
|
|
220
|
+
statement.gsub(/([:\\]?):([a-zA-Z]\w*)/) do |match|
|
|
221
|
+
if $1 == ":" # skip PostgreSQL casts
|
|
222
|
+
match # return the whole match
|
|
223
|
+
elsif $1 == "\\" # escaped literal colon
|
|
224
|
+
match[1..-1] # return match with escaping backlash char removed
|
|
225
|
+
elsif bind_vars.include?(match = $2.to_sym)
|
|
226
|
+
replace_bind_variable(connection, bind_vars[match])
|
|
227
|
+
else
|
|
228
|
+
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def quote_bound_value(connection, value)
|
|
234
|
+
if value.respond_to?(:map) && !value.acts_like?(:string)
|
|
235
|
+
values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
|
|
236
|
+
if values.empty?
|
|
237
|
+
connection.quote(connection.cast_bound_value(nil))
|
|
238
|
+
else
|
|
239
|
+
values.map! { |v| connection.quote(connection.cast_bound_value(v)) }.join(",")
|
|
240
|
+
end
|
|
241
|
+
else
|
|
242
|
+
value = value.id_for_database if value.respond_to?(:id_for_database)
|
|
243
|
+
connection.quote(connection.cast_bound_value(value))
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def raise_if_bind_arity_mismatch(statement, expected, provided)
|
|
248
|
+
unless expected == provided
|
|
249
|
+
raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
# = Active Record \Schema
|
|
5
|
+
#
|
|
6
|
+
# Allows programmers to programmatically define a schema in a portable
|
|
7
|
+
# DSL. This means you can define tables, indexes, etc. without using SQL
|
|
8
|
+
# directly, so your applications can more easily support multiple
|
|
9
|
+
# databases.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
#
|
|
13
|
+
# ActiveRecord::Schema[7.0].define do
|
|
14
|
+
# create_table :authors do |t|
|
|
15
|
+
# t.string :name, null: false
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# add_index :authors, :name, :unique
|
|
19
|
+
#
|
|
20
|
+
# create_table :posts do |t|
|
|
21
|
+
# t.integer :author_id, null: false
|
|
22
|
+
# t.string :subject
|
|
23
|
+
# t.text :body
|
|
24
|
+
# t.boolean :private, default: false
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# add_index :posts, :author_id
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# ActiveRecord::Schema is only supported by database adapters that also
|
|
31
|
+
# support migrations, the two features being very similar.
|
|
32
|
+
class Schema < Migration::Current
|
|
33
|
+
module Definition
|
|
34
|
+
extend ActiveSupport::Concern
|
|
35
|
+
|
|
36
|
+
module ClassMethods
|
|
37
|
+
# Eval the given block. All methods available to the current connection
|
|
38
|
+
# adapter are available within the block, so you can easily use the
|
|
39
|
+
# database definition DSL to build up your schema (
|
|
40
|
+
# {create_table}[rdoc-ref:ConnectionAdapters::SchemaStatements#create_table],
|
|
41
|
+
# {add_index}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_index], etc.).
|
|
42
|
+
#
|
|
43
|
+
# The +info+ hash is optional, and if given is used to define metadata
|
|
44
|
+
# about the current schema (currently, only the schema's version):
|
|
45
|
+
#
|
|
46
|
+
# ActiveRecord::Schema[7.0].define(version: 2038_01_19_000001) do
|
|
47
|
+
# ...
|
|
48
|
+
# end
|
|
49
|
+
def define(info = {}, &block)
|
|
50
|
+
new.define(info, &block)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def define(info, &block) # :nodoc:
|
|
55
|
+
connection_pool.with_connection do |connection|
|
|
56
|
+
instance_eval(&block)
|
|
57
|
+
|
|
58
|
+
connection_pool.schema_migration.create_table
|
|
59
|
+
if info[:version].present?
|
|
60
|
+
connection.assume_migrated_upto_version(info[:version])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
connection_pool.internal_metadata.create_table_and_set_flags(connection_pool.migration_context.current_environment)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
include Definition
|
|
69
|
+
|
|
70
|
+
def self.[](version)
|
|
71
|
+
@class_for_version ||= {}
|
|
72
|
+
@class_for_version[version] ||= Class.new(Migration::Compatibility.find(version)) do
|
|
73
|
+
include Definition
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|