1. 25 7月, 2019 1 次提交
    • E
      Make sure AR can load without Rails · 1d83ab93
      eileencodes 提交于
      In #36560 I accidentally re-introduced a bug where ActiveRecord can't be
      used without Rails. This returns an empty hash if we're outside the
      context of Rails since we can't create the database tasks without
      loading and reading the database yaml which is something only Railties
      can do.
      1d83ab93
  2. 27 6月, 2019 1 次提交
    • E
      Load initial database.yml once, and warn if we can't create tasks · df6b0de7
      eileencodes 提交于
      For multiple databases we attempt to generate the tasks by reading the
      database.yml before the Rails application is booted. This means that we
      need to strip out ERB since it could be reading Rails configs.
      
      In some cases like https://github.com/rails/rails/issues/36540 the ERB
      is too complex and we can't overwrite with the DummyCompilier we used in
      https://github.com/rails/rails/pull/35497. For the complex causes we
      simply issue a warning that says we couldn't infer the database tasks
      from the database.yml.
      
      While working on this I decided to update the code to only load the
      database.yml once initially so that we avoid having to issue the same
      warning multiple times. Note that this had no performance impact in my
      testing and is merely for not having to save the error off somewhere.
      Also this feels cleaner.
      
      Note that this will not break running tasks that exist, it will just
      mean that tasks for multi-db like `db:create:other_db` will not be
      generated. If the database.yml is actually unreadable it will blow up
      during normal rake task calls.
      
      Fixes #36540
      df6b0de7
  3. 14 6月, 2019 1 次提交
    • E
      Move SchemaMigration to migration_context · 7cc27d74
      eileencodes 提交于
      This PR moves the `schema_migration` to `migration_context` so that we
      can access the `schema_migration` per connection.
      
      This does not change behavior of the SchemaMigration if you are using
      one database. This also does not change behavior of any public APIs.
      `Migrator` is private as is `MigrationContext` so we can change these as
      needed.
      
      We now need to pass a `schema_migration` to `Migrator` so that we can
      run migrations on the right connection outside the context of a rake
      task.
      
      The bugs this fixes were discovered while debugging the issues around
      the SchemaCache on initialization with multiple database. It was clear
      that `get_all_versions` wouldn't work without these changes outside the
      context of a rake task (because in the rake task we establish a
      connection and change AR::Base.connection to the db we're running on).
      
      Because the `SchemaCache` relies on the `SchemaMigration` information we
      need to make sure we store it per-connection rather than on
      ActiveRecord::Base.
      
      [Eileen M. Uchitelle & Aaron Patterson]
      7cc27d74
  4. 07 6月, 2019 1 次提交
  5. 05 6月, 2019 1 次提交
  6. 20 4月, 2019 1 次提交
    • E
      Handle up/down for multiple databases · f9244c65
      eileencodes 提交于
      This change adds the ability to run up/down for a database in a multi-db
      environment.
      
      If you have an app with a primary and animals database the following
      tasks will be generated:
      
      ```
      VERSION=123 rake db:migrate:up:primary
      VERSION=123 rake db:migrate:up:primary
      
      VERSION=123 rake db:migrate:down:primary
      VERSION=123 rake db:migrate:up:animals
      ```
      
      I didn't generate descriptions with them since we don't generate a
      description for a single database application.
      
      In addition to this change I've made it so if your application has
      multiple databases Rails will raise if you try to run `up` or `down`
      without a namespace. This is because we don't know which DB you want to
      run `up` or `down` against unless the app tells us, so it's safer to
      just block it and recommend using namespaced versions of up/down
      respectively.
      
      The output for the raise looks like:
      
      ```
      You're using a multiple database application. To use `db:migrate:down`
      you must run the namespaced task with a VERSION. Available tasks are
      db:migrate:down:primary and db:migrate:down:animals.
      ```
      f9244c65
  7. 19 4月, 2019 1 次提交
    • E
      Ensure active record can load without Railties · 456c3f10
      eileencodes 提交于
      When I added the rake tasks for multiple databases I accidentally made
      it so you couldn't use Active Record without Railties.
      
      Since getting the database configuration depends on being able to load
      Railties (there is no other way to read and parse the yaml file and load
      it) I've decided that using multiple databases outside of Rails is a
      no-op.
      
      I've changed the code here to return if Rails isn't defined. Then I
      changed the other tasks to use `ActiveRecord::Tasks::DatabaseTasks.env`
      instead of `Rails.env`. The multi-db tasks can keep using `Rails.env`
      because they'll only be generated if we're using Rails and not just
      Active Record.
      456c3f10
  8. 04 4月, 2019 1 次提交
  9. 17 3月, 2019 3 次提交
  10. 06 3月, 2019 1 次提交
    • E
      Load YAML for rake tasks without parsing ERB · 37d1429a
      eileencodes 提交于
      This change adds a new method that loads the YAML for the database
      config without parsing the ERB. This may seem odd but bear with me:
      
      When we added the ability to have rake tasks for multiple databases we
      started looping through the configurations to collect the namespaces so
      we could do `rake db:create:my_second_db`. See #32274.
      
      This caused a problem where if you had `Rails.config.max_threads` set in
      your database.yml it will blow up because the environment that defines
      `max_threads` isn't loaded during `rake -T`. See #35468.
      
      We tried to fix this by adding the ability to just load the YAML and
      ignore ERB all together but that caused a bug in GitHub's YAML loading
      where if you used multi-line ERB the YAML was invalid. That led us to
      reverting some changes in #33748.
      
      After trying to resolve this a bunch of ways `@tenderlove` came up with
      replacing the ERB values so that we don't need to load the environment
      but we also can load the YAML.
      
      This change adds a DummyCompiler for ERB that will replace all the
      values so we can load the database yaml and create the rake tasks.
      Nothing else uses this method so it's "safe".
      
      DO NOT use this method in your application.
      
      Fixes #35468
      37d1429a
  11. 05 3月, 2019 1 次提交
  12. 08 11月, 2018 1 次提交
  13. 09 10月, 2018 1 次提交
  14. 23 9月, 2018 1 次提交
    • Y
      Enable `Performance/UnfreezeString` cop · 1b86d901
      yuuji.yaginuma 提交于
      In Ruby 2.3 or later, `String#+@` is available and `+@` is faster than `dup`.
      
      ```ruby
      # frozen_string_literal: true
      
      require "bundler/inline"
      
      gemfile(true) do
        source "https://rubygems.org"
      
        gem "benchmark-ips"
      end
      
      Benchmark.ips do |x|
        x.report('+@') { +"" }
        x.report('dup') { "".dup }
        x.compare!
      end
      ```
      
      ```
      $ ruby -v benchmark.rb
      ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
      Warming up --------------------------------------
                        +@   282.289k i/100ms
                       dup   187.638k i/100ms
      Calculating -------------------------------------
                        +@      6.775M (± 3.6%) i/s -     33.875M in   5.006253s
                       dup      3.320M (± 2.2%) i/s -     16.700M in   5.032125s
      
      Comparison:
                        +@:  6775299.3 i/s
                       dup:  3320400.7 i/s - 2.04x  slower
      
      ```
      1b86d901
  15. 01 9月, 2018 1 次提交
    • E
      Convert configs_for to kwargs, add include_replicas · a572d228
      Eileen Uchitelle 提交于
      Changes the `configs_for` method from using traditional arguments to
      using kwargs. This is so I can add the `include_replicas` kwarg without
      having to always include `env_name` and `spec_name` in the method call.
      
      `include_replicas` defaults to false because everywhere internally in
      Rails we don't want replicas. `configs_for` is for iterating over
      configurations to create / run rake tasks, so we really don't ever need
      replicas in that case.
      a572d228
  16. 30 8月, 2018 1 次提交
    • E
      Refactors Active Record connection management · fdf3f0b9
      Eileen Uchitelle 提交于
      While the three-tier config makes it easier to define databases for
      multiple database applications, it quickly became clear to offer full
      support for multiple databases we need to change the way the connections
      hash was handled.
      
      A three-tier config means that when Rails needed to choose a default
      configuration (in the case a user doesn't ask for a specific
      configuration) it wasn't clear to Rails which the default was. I
      [bandaid fixed this so the rake tasks could work](#32271) but that fix
      wasn't correct because it actually doubled up the configuration hashes.
      
      Instead of attemping to manipulate the hashes @tenderlove and I decided
      that it made more sense if we converted the hashes to objects so we can
      easily ask those object questions. In a three tier config like this:
      
      ```
      development:
        primary:
          database: "my_primary_db"
        animals:
          database; "my_animals_db"
      ```
      
      We end up with an object like this:
      
      ```
        @configurations=[
          #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
            @env_name="development",@spec_name="primary",
            @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
          #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90
            @env_name="development",@spec_name="animals",
            @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>
      ]>
      ```
      
      The configurations setter takes the database configuration set by your
      application and turns them into an
      `ActiveRecord::DatabaseConfigurations` object that has one getter -
      `@configurations` which is an array of all the database objects.
      
      The configurations getter returns this object by default since it acts
      like a hash in most of the cases we need. For example if you need to
      access the default `development` database we can simply request it as we
      did before:
      
      ```
      ActiveRecord::Base.configurations["development"]
      ```
      
      This will return primary development database configuration hash:
      
      ```
      { "database" => "my_primary_db" }
      ```
      
      Internally all of Active Record has been converted to use the new
      objects. I've built this to be backwards compatible but allow for
      accessing the hash if needed for a deprecation period. To get the
      original hash instead of the object you can either add `to_h` on the
      configurations call or pass `legacy: true` to `configurations.
      
      ```
      ActiveRecord::Base.configurations.to_h
      => { "development => { "database" => "my_primary_db" } }
      
      ActiveRecord::Base.configurations(legacy: true)
      => { "development => { "database" => "my_primary_db" } }
      ```
      
      The new configurations object allows us to iterate over the Active
      Record configurations without losing the known environment or
      specification name for that configuration. You can also select all the
      configs for an env or env and spec. With this we can always ask
      any object what environment it belongs to:
      
      ```
      db_configs = ActiveRecord::Base.configurations.configurations_for("development")
      => #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800
        @configurations=[
          #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
            @env_name="development",@spec_name="primary",
            @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
          #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90
            @env_name="development",@spec_name="animals",
            @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>
      ]>
      
      db_config.env_name
      => "development"
      
      db_config.spec_name
      => "primary"
      
      db_config.config
      => { "adapter"=>"sqlite3", "database"=>"db/development.sqlite3" }
      ```
      
      The configurations object is more flexible than the configurations hash
      and will allow us to build on top of the connection management in order
      to add support for primary/replica connections, sharding, and
      constructing queries for associations that live in multiple databases.
      fdf3f0b9
  17. 29 8月, 2018 2 次提交
    • E
      Drop load_database_yaml and fix test · 6b5df90f
      Eileen Uchitelle 提交于
      We originally did the whole `load_database_yaml` thing because this test
      wasn't cooperating and we needed to finish the namespaced rake tasks for
      multiple databases.
      
      However, it turns out that YAML can't eval ERB if you don't tell it it's
      ERB so you get Pysch parse errors if you're using multi-line ERB or
      ERB with conditionals. It's a hot mess.
      
      After trying a few things and thinking it over we decided that it wasn't
      worth bandaiding over, the test needed to be improved. The test was
      added in #31135 to test that the env is loaded in these tasks. But it
      was blowing up because we were trying to read a database name out of the
      configuration - however that's not the purpose of this change. We want
      to read environment files in the rake tasks, but not in the config
      file.
      
      In this PR we changed the test to test what the PR was actually fixing.
      We've also deleted the `load_database_yaml` because it caused more
      problems than it was worth. This should fix the issues described in
      https://github.com/rails/rails/pull/32274#issuecomment-384161057. We
      also had these problems at GitHub.
      Co-authored-by: Nalimi <aibrahim2k2@gmail.com>
      6b5df90f
    • B
      Prevent leaking of user's DB credentials on `rails db:create` failure · 9b455fe6
      bogdanvlviv 提交于
      Issue #27852 reports that when `rails db:create` fails, it causes
      leaking of user's DB credentials to $stderr.
      We print a DB's configuration hash in order to help users more quickly
      to figure out what could be wrong with his configuration.
      
      This commit changes message from
      "Couldn't create database for #{configuration.inspect}" to
      "Couldn't create '#{configuration['database']}' database. Please check your configuration.".
      
      There are two PRs that fixing it #27878, #27879, but they need a bit more work.
      I decided help to finish this and added Author of those PRs credit in this commit.
      
      Since it is a security issue, I think we should backport it to
      `5-2-stable`, and `5-1-stable`.
      Guided by https://edgeguides.rubyonrails.org/maintenance_policy.html#security-issues
      
      Fixes #27852
      Closes #27879
      Related to #27878
      
      [Alexander Marrs & bogdanvlviv]
      9b455fe6
  18. 02 8月, 2018 1 次提交
  19. 01 8月, 2018 1 次提交
  20. 10 4月, 2018 2 次提交
  21. 22 3月, 2018 3 次提交
    • E
      Refactor configs_for and friends · 4e663c1e
      eileencodes 提交于
      Moves the configs_for and DatabaseConfig struct into it's own file. I
      was considering doing this in a future refactoring but our set up forced
      me to move it now. You see there are `mattr_accessor`'s on the Core
      module that have default settings. For example the `schema_format`
      defaults to Ruby. So if I call `configs_for` or any methods in the Core
      module it will reset the `schema_format` to `:ruby`. By moving it to
      it's own class we can keep the logic contained and avoid this
      unfortunate issue.
      
      The second change here does a double loop over the yaml files. Bear with
      me...
      
      Our tests dictate that we need to load an environment before our rake
      tasks because we could have something in an environment that the
      database.yml depends on. There are side-effects to this and I think
      there's a deeper bug that needs to be fixed but that's for another
      issue. The gist of the problem is when I was creating the dynamic rake
      tasks if the yaml that that rake task is calling evaluates code (like
      erb) that calls the environment configs the code will blow up because
      the environment is not loaded yet.
      
      To avoid this issue we added a new method that simply loads the yaml and
      does not evaluate the erb or anything in it. We then use that yaml to
      create the task name. Inside the task name we can then call
      `load_config` and load the real config to actually call the code
      internal to the task. I admit, this is gross, but refactoring can't all
      be pretty all the time and I'm working hard with `@tenderlove` to
      refactor much more of this code to get to a better place re connection
      management and rake tasks.
      4e663c1e
    • E
      Update schema/structure dump tasks for multi db · 0f0aa6a2
      eileencodes 提交于
      Adds the ability to dump the schema or structure files for mulitple
      databases. Loops through the configs for a given env and sets a filename
      based on the format, then establishes a connection for that config and
      dumps into the file.
      0f0aa6a2
    • E
      Add ability to create/drop/migrate all dbs for a given env · 5eb4488d
      eileencodes 提交于
      `each_current_configuration` is used by create, drop, and other methods
      to find the configs for a given environment and returning those to the
      method calling them.
      
      The change here allows for the database commands to operate on all the
      configs in the environment. Previously we couldn't slice the hashes and
      iterate over them becasue they could be two tier or could be three
      tier. By using the database config structs we don't need to care whether
      we're dealing with a three tier or two tier, we can just parse all the
      configs based on the environment.
      
      This makes it possible for us to run `bin/rails db:create` and it will
      create all the configs for the dev and test environment ust like it does
      for a two tier - it creates the db for dev and test. Now `db:create`
      will create `primary` for dev and test, and `animals` for dev and test
      if our database.yml looks like:
      
      ```
      development:
        primary:
          etc
        animals:
          etc
      
      test:
        primary:
          etc
        animals:
          etc
      ```
      
      This means that `bin/rails db:create`, `bin/rails db:drop`, and
      `bin/rails db:migrate` will operate on the dev and test env for both
      primary and animals ds.
      5eb4488d
  22. 02 3月, 2018 1 次提交
  23. 23 2月, 2018 1 次提交
  24. 22 2月, 2018 1 次提交
    • E
      Delete default configuration · bf0495de
      eileencodes 提交于
      Because of this default configuration we're constantly checking if the
      database exists when looping through configurations. This is unnecessary
      and we should just delete it before we need to loop through
      configurations.
      bf0495de
  25. 06 2月, 2018 2 次提交
    • E
      Don't output information on drop if not verbose · 1dfc3a24
      eileencodes 提交于
      Followup to b988ecb9, when I cherry-picked from my parallel testing
      branch I didn't realize `drop` wasn't part of the commit.
      1dfc3a24
    • E
      Add ability to turn off verbose for database tasks · b988ecb9
      eileencodes 提交于
      You could use the `VERBOSE` env var to turn off output for migrations
      tasks but you couldn't use it for other tasks.
      
      This change moves the `verbose?` check to a method so we can also use it
      in create and drop respectively.
      
      tenderlove and I noticed this as part of the ongoing work in parallel
      testing. When the parallel tests boot the app needs to create new
      databases for each worker. The output from these is unnecessary but
      there was previously no way to turn it off. Now if `VERBOSE=false` is
      passes to `bin/rails db:create` the text "Created blah blah db" will no
      longer be output.
      b988ecb9
  26. 19 1月, 2018 1 次提交
  27. 18 1月, 2018 1 次提交
    • E
      Refactor migration to move migrations paths to connection · a2827ec9
      eileencodes 提交于
      Rails has some support for multiple databases but it can be hard to
      handle migrations with those. The easiest way to implement multiple
      databases is to contain migrations into their own folder ("db/migrate"
      for the primary db and "db/seconddb_migrate" for the second db). Without
      this you would need to write code that allowed you to switch connections
      in migrations. I can tell you from experience that is not a fun way to
      implement multiple databases.
      
      This refactoring is a pre-requisite for implementing other features
      related to parallel testing and improved handling for multiple
      databases.
      
      The refactoring here moves the class methods from the `Migrator` class
      into it's own new class `MigrationContext`. The goal was to move the
      `migrations_paths` method off of the `Migrator` class and onto the
      connection. This allows users to do the following in their
      `database.yml`:
      
      ```
      development:
        adapter: mysql2
        username: root
        password:
      
      development_seconddb:
        adapter: mysql2
        username: root
        password:
        migrations_paths: "db/second_db_migrate"
      ```
      
      Migrations for the `seconddb` can now be store in the
      `db/second_db_migrate` directory. Migrations for the primary database
      are stored in `db/migrate`".
      
      The refactoring here drastically reduces the internal API for migrations
      since we don't need to pass `migrations_paths` around to every single
      method. Additionally this change does not require any Rails applications
      to make changes unless they want to use the new public API. All of the
      class methods from the `Migrator` class were `nodoc`'d except for the
      `migrations_paths` and `migrations_path` getter/setters respectively.
      a2827ec9
  28. 07 11月, 2017 1 次提交
  29. 16 10月, 2017 2 次提交
    • B
      `ActiveRecord::Tasks::DatabaseTasks.load_schema` has always to establish database connection · 678e563d
      bogdanvlviv 提交于
        When load schema from `structure.sql`, database connection isn't
        established. `ActiveRecord::Tasks::DatabaseTasks.load_schema` has to
        establish database connection since it executes
        ```
        ActiveRecord::InternalMetadata.create_table
        ActiveRecord::InternalMetadata[:environment] = environment
        ```
      678e563d
    • B
      Fix `bin/rails db:setup` and `bin/rails db:test:prepare` create wrong... · 99b2bf8d
      bogdanvlviv 提交于
      Fix `bin/rails db:setup` and `bin/rails db:test:prepare` create  wrong ar_internal_metadata's data for a test database.
      
        Before:
        ```
        $ RAILS_ENV=test rails dbconsole
        > SELECT * FROM ar_internal_metadata;
        key|value|created_at|updated_at
        environment|development|2017-09-11 23:14:10.815679|2017-09-11 23:14:10.815679
        ```
      
        After:
        ```
        $ RAILS_ENV=test rails dbconsole
        > SELECT * FROM ar_internal_metadata;
        key|value|created_at|updated_at
        environment|test|2017-09-11 23:14:10.815679|2017-09-11 23:14:10.815679
        ```
      
        Fixes #26731.
      99b2bf8d
  30. 24 7月, 2017 1 次提交
    • E
      Avoid modifying frozen string in check_schema_file · 2b331e90
      Eugene Kenny 提交于
      This was missed when the frozen string literal pragma was added to this
      file because the string is only modified when running in the context of
      a full Rails app, which wasn't covered by the test suite.
      2b331e90
  31. 20 7月, 2017 1 次提交
  32. 11 7月, 2017 1 次提交
    • K
      * Don't eagerly require Rails' minitest plugin. · 0d72489b
      Kasper Timm Hansen 提交于
      By making the Rails minitest behave like a standard minitest plugin
      we're much more likely to not break when people use other minitest
      plugins. Like minitest-focus and pride.
      
      To do this, we need to behave like minitest: require files up front
      and then perform the plugin behavior via the at_exit hook.
      This also saves us a fair bit of wrangling with test file loading.
      
      Finally, since the environment and warnings options have to be applied
      as early as possible, and since minitest loads plugins at_exit, they
      have to be moved to the test command.
      
      * Don't expect the root method.
      
      It's likely this worked because we eagerly loaded the Rails minitest plugin
      and that somehow defined a root method on `Rails`.
      
      * Assign a backtrace to failed exceptions.
      
      Otherwise Minitest pukes when attempting to filter the backtrace (which
      Rails' backtrace cleaner then removes).
      
      Means the exception message test has to be revised too.
      
      This is likely caused by the rails minitest plugin now being loaded for
      these tests and assigning a default backtrace cleaner.
      0d72489b