Railsチュートリアル 第6章 ユーザーのモデルを作成する - セキュアなパスワードを追加する

# セキュアなパスワード

## ハッシュ化とは

* 何らかの演算を行うことによって、入力されたデータを元に戻せないデータに変換する処理
    * こうした演算を行うための関数は「ハッシュ関数」と呼ばれる
* Rubyのデータ型であるハッシュとは直接関係ない
* 本節における「セキュアなパスワード」というのは、ハッシュ化技術を基盤として構成される
    * パスワードをハッシュ化したものをRDBに保存する
    * ハッシュ化されたパスワードを用いてユーザー認証を行う

## 全体の処理手順

1. ユーザーはRailsアプリケーションにパスワードを送信する[^クライアント側でハッシュ化しない理由]
2. Railsアプリケーションは送信されてきたパスワードをハッシュ化する
3. 2.でハッシュ化された文字列とRDBMS内に格納されたパスワードを比較し、一致すればユーザー認証OK

仮にRDBMSの内容が漏洩したとしても、「ユーザーが送信する文字列」という意味でのパスワードの安全性は保たれます。一方で、ユーザーがRailsアプリケーションにパスワードを送出する段階においては、通信経路の安全性を何らかの形で担保しなければなりません。

[^クライアント側でハッシュ化しない理由]: [セキュリティー - ログイン時のパスワード送信に関して｜teratail](https://teratail.com/questions/76309)等のやり取りを見るに、「クライアント側でハッシュ化する」というのはアンチパターンであるそうです。その理由については、「ユーザーがRailsアプリケーションに対して送信する文字列と、Railsアプリケーション側に保存されている文字列が異なるというのがポイントである」「ハッシュ化処理については、Railsアプリケーション側で脆弱にならないように手を打つものである」と解釈しました。

# ハッシュ化されたパスワード

## `has_secure_password`メソッド

Railsにおけるセキュアなパスワードの実装は、`has_secure_password`宣言のみでほぼ完結してしまいます。`has_secure_password`は、以下のような機能を含んでいます。

* セキュアにハッシュ化したパスワードを、データベース内の`password_digest`という属性に保存する
* `password`と`password_confirmation`という属性のペアに対するバリデーションを定義する
* `authenticate`メソッド
    * 引数の文字列がパスワードと一致すれば、モデルオブジェクトを返す
    * 引数の文字列とパスワードが一致しなければ、`false`を返す

`has_secure_password`は、モデルオブジェクト内で以下のように呼び出します。

```ruby:
class User < ApplicationRecord
  ...略
  has_secure_password
end
```

## `password_digest`属性

`has_secure_password`メソッドを使用するためには、対象となるモデルオブジェクトに`password_digest`という属性が定義されている必要があります。今回はUserモデルで用いるので、Userのデータモデルを以下のように変更します。

![User_full.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/24b33007-34b1-fa2d-e8a1-73507e927ff5.png)


## `password_digest`属性を定義するために、Userモデルを更新する

`password_digest`カラムを用いるために、Userモデルを更新する必要が出てきました。今回Userモデルの更新に用いるマイグレーションを生成するためのコマンドは、以下の通りになります。

```
# rails generate migration add_password_digest_to_users password_digest:string
Running via Spring preloader in process 2628
      invoke  active_record
      create    db/migrate/[timestamp]_add_password_digest_to_users.rb
```

このコマンドのポイントは以下です。

* マイグレーション名の末尾が`_to_users`である
    * こうした名前にしておけば、`users`テーブルにカラムを追加するマイグレーションが、Railsの機能によって自動で追加される
* `password_digest:string`は、属性名と型情報を表す
    * 属性名…`password_digest`
    * 型…`string`

生成されたマイグレーションの内容は以下の通りになります。

```ruby:db/migrate/[timestamp]_add_password_digest_to_users.rb
class AddPasswordDigestToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :password_digest, :string
  end
end
```

`add_column`メソッドにより、`users`テーブルに`string`型の`password_digest`カラムを生成する処理です。

生成したマイグレーションをRDBMSに適用するコマンドは以下のとおりです。

```
# rails db:migrate  
== [timestamp] AddPasswordDigestToUsers: migrating =========================
-- add_column(:users, :password_digest, :string)
   -> 0.0211s
== [timestamp] AddPasswordDigestToUsers: migrated (0.0231s) ================
```

確かに`add_column(:users, :password_digest, :string)`が実行されていますね。

## 必要なGemをインストールし、ハッシュ関数そのものを使用できるようにする

先にRDBMS側の準備を整えました。しかしながら、現時点ではではまだハッシュ関数を処理するために必要なGemがインストールされておらず、そのままではハッシュ関数を使うことができません。

今回は、ハッシュ関数を処理するためのGemとして[bcrypt](https://ja.wikipedia.org/wiki/Bcrypt)を用います。

必要となるのは、「Gemfileを更新し、bundleを実行する」という操作ですね。

```diff:/gemfile
  source 'https://rubygems.org'

  gem 'rails',        '5.1.6'
+ gem 'bcrypt',       '3.1.12'
  ...略
```

続いて、`bundle install`コマンドを実行します。

```
# bundle install
...略
Fetching bcrypt 3.1.12
Installing bcrypt 3.1.12 with native extensions
...略
Bundle complete! 23 Gemfile dependencies, 81 gems now installed.
Gems in the group production were not installed.
Bundled gems are installed into `/usr/local/bundle`
```

無事bcryptがインストールされたようです。

# ユーザーがセキュアなパスワードを持っている

* Userモデルにpassword_digest属性を追加する
* Gemfileにbcryptを追加する

以上の操作により、Userモデル内で`has_secure_password`が使えるようになりました。早速Userモデルに`has_secure_password`メソッドを追加しましょう。

```diff:app/models/user.rb
  class User < ApplicationRecord
    before_save { email.downcase! }
    validates :name,  presence: true, length: { maximum: 50 }
    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
    validates :email, presence: true, length: { maximum: 255 },
                      format: { with: VALID_EMAIL_REGEX },
                      uniqueness: { case_sensitive: false }
+   has_secure_password
  end
```

## `has_secure_password`を追加することにより、テストが通らなくなる

この時点では、テストが通りません。

```
# rails test:models
Started with run options --seed 60576

 FAIL["test_should_be_valid", UserTest, 0.06872039998415858]
 test_should_be_valid#UserTest (0.07s)
        Expected false to be truthy.
        test/models/user_test.rb:10:in `block in <class:UserTest>'

ERROR["test_email_addresses_should_be_saved_as_lower-case", UserTest, 0.09123980000731535]
 test_email_addresses_should_be_saved_as_lower-case#UserTest (0.09s)
ActiveRecord::RecordNotFound:         ActiveRecord::RecordNotFound: Couldn't find User without an ID
            test/models/user_test.rb:60:in `block in <class:UserTest>'

 FAIL["test_email_validation_should_be_accept_valid_addresses", UserTest, 0.13576069998089224]
 test_email_validation_should_be_accept_valid_addresses#UserTest (0.14s)
        "user@example.com" should be valid
        test/models/user_test.rb:37:in `block (2 levels) in <class:UserTest>'
        test/models/user_test.rb:35:in `each'
        test/models/user_test.rb:35:in `block in <class:UserTest>'

  9/9: [===================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.15226s
9 tests, 13 assertions, 2 failures, 1 errors, 0 skips
```

「`has_secure_password`の追加とは直接関係しなさそうなところ（`email_addresses_should_be_saved_as_lower-case`など）でテストが通らなくなっている」ことに注意が必要です。何らかのライブラリ追加等を行った場合、このように原因がわかりづらい形でテストが通らなくなることがあります。

Railsチュートリアル本文によれば、テストが通らなくなった理由は以下のとおりです。

* `has_secure_password`には、以下の仮想的な属性に対してバリデーションを行う機能が暗黙的に追加されている
    * `password`属性
    * `password_confirmation`属性
* 一方で、現時点のテストデータでは、`@user`変数に`password`属性および`password_confirmation`属性が定義されていない

というわけで、現時点でテストを通すために必要なのは「`@user`変数に`password`属性および`password_confirmation`属性を追加する」という処理ですね。

```diff:test/models/user_test.rb
  class UserTest < ActiveSupport::TestCase

    def setup
-     @user = User.new(name: "Example User", email: "user@example.com")
+     @user = User.new(name: "Example User", email: "user@example.com",
+                     password: "foobar", password_confirmation: "foobar")
    end

  ...略
  end
```

これでテストが通るようになります。

```
# rails test:models
Started with run options --seed 17255

  9/9: [===================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.23941s
9 tests, 18 assertions, 0 failures, 0 errors, 0 skips
```

# 演習 - ハッシュ化されたパスワード

## 1. この時点では、userオブジェクトに有効な名前とメールアドレスを与えても、`valid?`で失敗してしまうことを確認してみてください。

Railsコンソールをサンドボックスモードで起動して確かめてみます。

```
# rails console --sandbox

>> u = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil>

>> u.name = "foo"
=> "foo"

>> u.email = "foo@foobar.com"
=> "foo@foobar.com"

>> u.valid?
  User Exists (0.8ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ?  [["email", "foo@foobar.com"], ["LIMIT", 1]]
=> false
```

確かに`valid?`が`false`を返していますね。

## 2. なぜ失敗してしまうのでしょうか? エラーメッセージを確認してみてください。

ユーザーオブジェクトの`valid?`が`false`を返した時点で、副作用としてユーザーオブジェクトの`errors`オブジェクトにエラー情報が書き込まれています。エラーメッセージの内容を確認するためには、`errors.full_messages`メソッドを使います。

```
>> u.errors.full_messages
=> ["Password can't be blank"]
```

「パスワードが空白であってはならない」という趣旨のエラーメッセージですね。

# パスワードの最小文字数

## パスワードの最小文字数が定義されていることのテストの実装

パスワードの最小文字数を強制するための機能の実装は、まず、パスワードの最小文字数が定義されていることのテストの実装から始まります。今回は、パスワードの最小文字数について、以下のルールを設定することとします。

* パスワードが空でないこと
* パスワードの最小文字数が6文字以上であること

実際にテストを定義してみます。

```diff:test/models/user_test.rb
  require 'test_helper'

  class UserTest < ActiveSupport::TestCase

    def setup
      @user = User.new(name: "Example User", email: "user@example.com",
                      password: "foobar", password_confirmation: "foobar")
    end

  ...略
+
+   test "password should be present (nonblank)" do
+     @user.password = @user.password_confirmation = " " * 6
+     assert_not @user.valid?
+   end
+
+   test "password should have a minimum length" do
+     @user.password = @user.password_confirmation = "a" * 5
+     assert_not @user.valid?
+   end
  end
```

### Rubyにおける代入は、繋げてまとめて行うことができる 

今回追加したコードの中で、Rubyの文法上注目すべきポイントは「代入は、`a = b = c = 1`のように繋げてまとめて行うことができる[^多重代入？]」という点です。「代入演算子の評価は、右から左に向かって行われる」「代入式の値は、代入された値そのものになる」という仕様により、このような連鎖的な代入が可能となっています。

```ruby:
@user.password = @user.password_confirmation = "a" * 5
```

上述コードでは、パスワードとパスワード確認に対し、同時に代入を行っています。文字列の乗算を使った5文字の文字列の代入ですね。

[^多重代入？]: Railsチュートリアル本文では、このような演算を「多重代入」と言及していました。しかしながら、今回言及した特性は、Rubyの多重代入とはまた別の概念を指しています。

## モデルオブジェクトの`validation`における`minimum`オプション

Name属性やEmail属性のバリデーションで、最大文字数を制約する`maximum`というオプションを使いました。これに対し、最小文字数を制約する`minimum`というオプションも存在します。今回は、`minimum`オプションを使って最小文字数のバリデーションを実装します。

```diff:app/models/user.rb
  class User < ApplicationRecord
    before_save { email.downcase! }
    validates :name,  presence: true, length: { maximum: 50 }
    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
    validates :email, presence: true, length: { maximum: 255 },
                      format: { with: VALID_EMAIL_REGEX },
                      uniqueness: { case_sensitive: false }
    has_secure_password
+   validates :password, presence: true, length: { minimum: 6 }
  end
```

この実装はまた、「空のパスワードを許容しない」という制約を実装するために、`presence: true`というオプションも追加しています。`has_secure_password`は、新しくレコードが追加された時点でのみ適用されるため、ユーザーが後から`' '`（6文字分の半角スペース）といった文字列でパスワードを書き換えることを拒絶できません。なので、`presence: true`が別途必要になるわけなのです。

## テストが通ることの確認

この時点でテストは通ります。

```
# rails test:models
Started with run options --seed 22788

  11/11: [=================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.30158s
11 tests, 20 assertions, 0 failures, 0 errors, 0 skips
```

先程「9 tests, 18 assertions」だったのが、今回は「11 tests, 20 assertions」に増えています。

# 演習 - パスワードの最小文字数

## 1. 有効な名前とメールアドレスでも、パスワードが短すぎるとuserオブジェクトが有効にならないことを確認してみましょう。

```
>> u = User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil, password_digest: nil>
>> u.name = "foo"
=> "foo"
>> u.email = "foo@foobar.com"
=> "foo@foobar.com"
>> u.password = "abc12"
=> "abc12"
>> u.valid?
  User Exists (0.3ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ?  [["email", "foo@foobar.com"], ["LIMIT", 1]]
=> false
```

「`password_digest`属性の値に直接代入しない」ということが一つのポイントです。`has_secure_password`によって定義された仮想的な属性である`password`属性に代入するのが正しい扱いです。

結果として、確かにusersオブジェクトの`valid?`は`false`を返しています。

## 2. 上で失敗した時、どんなエラーメッセージになるでしょうか? 確認してみましょう。 

```
>> u.errors.full_messages
=> ["Password is too short (minimum is 6 characters)"]
```

"Password is too short (minimum is 6 characters)"というメッセージが返ってきています。

なお、ここで"Password can't be blank"というメッセージが返ってくる場合、おそらく「`password_digest`属性に直接代入する」という処理をしてしまっていると思います。正しくは`password`属性です。

# ユーザーの作成と認証

## Railsコンソールを使って、データベースに新規ユーザーを作成する

ここまでの手順で、Userモデルの基本部分が完成しました。今度は実際にデータベースに新規ユーザーを1人作成してみます。

現在のところ、ユーザー登録に用いるWebフォームはまだ作られていません。そのため、「データベースにユーザーを追加する」という操作は、「Railsコンソールからユーザーを手動で作成する」という手法で実現することとします。

今回は、「実際にRDBMSに変更を反映する必要がある」という条件です。そのため、Railsコンソールはサンドボックスモードではない状態で起動する必要があります。

```
# rails console

>> User.create(name: "Michael Hartl", email: "mhartl@example.com",
?> password: "foobar", password_confirmation: "foobar")
   (0.1ms)  begin transaction
  User Exists (2.5ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
  SQL (11.5ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?)  [["name", "Michael Hartl"], ["email", "mhartl@example.com"], ["created_at", "2019-10-13 06:43:53.935111"], ["updated_at", "2019-10-13 06:43:53.935111"], ["password_digest", "$2a$10$rb26ZPyWkUh8T/cWML/OM.VhDiuJaii877zf3SvsfN56IOU/ibHei"]]
   (9.4ms)  commit transaction
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-10-13 06:43:53", updated_at: "2019-10-13 06:43:53", password_digest: "$2a$10$rb26ZPyWkUh8T/cWML/OM.VhDiuJaii877zf3SvsfN5...">
```

## 実際にユーザー情報が作成されたことを確認する

実際にユーザー情報が開発環境のRDBMSに保存されたことを確かめるために、Visual Studio CodeのSQLite Explorerで確認してみます。結果は以下の通りでした。

<img width="1058" alt="スクリーンショット 2019-10-13 15.45.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323778/fbc7f7d5-4bef-b75d-8a76-9b1b50df9e05.png">

`id`, `name`, `email`, `created_at`, `updated_at`, `password_digest`。ここまでに定義したUserモデルの属性に対応するカラムがきちんと存在しています。

再びRailsコンソールで、今しがた生成したuserオブジェクトの`password_digest`属性を参照してみましょう。

```
>> user = User.find_by(email: "mhartl@example.com")
  User Load (3.5ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-10-13 06:43:53", updated_at: "2019-10-13 06:43:53", password_digest: "$2a$10$rb26ZPyWkUh8T/cWML/OM.VhDiuJaii877zf3SvsfN5...">
>> user.password_digest
=> "$2a$10$rb26ZPyWkUh8T/cWML/OM.VhDiuJaii877zf3SvsfN56IOU/ibHei"
```

`user.password_digest`属性の値として格納されているのは、`"foobar"`という文字列をbcryptでハッシュ化した結果の文字列です。

## `authenticate`メソッドを使ってみる

先ほど、Userモデルに`has_secure_password`を追加しました。そのため、Userモデルのオブジェクトで`authenticate`メソッドが使えるようになっています。このメソッドは、先ほども説明したとおり、「引数として与えた文字列をハッシュ化した値と、データベース内にある`password_digest`の値を比較する」というメソッドです。

```
>> user.authenticate("not_the_right_password")
=> false

>> user.authenticate("foobaz")
=> false
```

以上は、`authenticate`メソッドに間違ったパスワードを与えた結果です。戻り値は`false`になっています。

```
user.authenticate("foobar")
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-10-13 06:43:53", updated_at: "2019-10-13 06:43:53", password_digest: "$2a$10$rb26ZPyWkUh8T/cWML/OM.VhDiuJaii877zf3SvsfN5...">
```

以上は、`authenticate`メソッドに正しいパスワードを与えた結果です。今度は、`authenticate`メソッドが当該ユーザーオブジェクト自身を返しています。

Rubyにおいて、`false`でも`nil`でもないすべてのオブジェクトは、真理値として評価すると`true`となります。当然ながらユーザーオブジェクトも真理値は`true`となります。

```
>> !!user.authenticate("foobar")
=> true
```

# 演習 - ユーザーの作成と認証

## 1. コンソールを一度再起動して (userオブジェクトを消去して)、このセクションで作ったuserオブジェクトを検索してみてください。

```
>> exit

# rails console --sandbox

>> User.find_by(email: "mhartl@example.com")
  User Load (1.4ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-10-13 06:43:53", updated_at: "2019-10-13 06:43:53", password_digest: "$2a$10$rb26ZPyWkUh8T/cWML/OM.VhDiuJaii877zf3SvsfN5...">
```

## 2. オブジェクトが検索できたら、名前を新しい文字列に置き換え、`save`メソッドで更新してみてください。うまくいきませんね...、なぜうまくいかなかったのでしょうか?

### その1

```
>> (User.find_by(email: "mhartl@example.com").name = "Reimu Hakurei").save
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
Traceback (most recent call last):
        1: from (irb):3
NoMethodError (undefined method `save' for "Reimu Hakurei":String)
```

「このようなやり方では、ユーザーオブジェクトを更新することはできない」という意味であれば、「代入式の戻り値は、代入した値そのものになる」というのが理由です。`String`型に対して`save`メソッドは使えないですよね。

### その2

```
>> user = User.find_by(email: "mhartl@example.com")
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-10-13 06:43:53", updated_at: "2019-10-13 06:43:53", password_digest: "$2a$10$rb26ZPyWkUh8T/cWML/OM.VhDiuJaii877zf3SvsfN5...">

>> user.name = "Reimu Hakurei"
=> "Reimu Hakurei"

>> user.save
   (1.3ms)  SAVEPOINT active_record_1
  User Exists (0.2ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) AND ("users"."id" != ?) LIMIT ?  [["email", "mhartl@example.com"], ["id", 1], ["LIMIT", 1]]
   (0.1ms)  ROLLBACK TO SAVEPOINT active_record_1
=> false

>> user.errors.messages
=> {:password=>["can't be blank", "is too short (minimum is 6 characters)"]}
```

`errors.messages`の内容を見るに、「仮想属性`password`の値が空であること」が原因のようです。

## 3. 今度は[6.1.5](https://railstutorial.jp/chapters/modeling_users?version=5.1#sec-updating_user_objects)で紹介したテクニックを使って、`user`の名前を更新してみてください。 

```
>> user = User.find_by(email: "mhartl@example.com")
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-10-13 06:43:53", updated_at: "2019-10-13 06:43:53", password_digest: "$2a$10$rb26ZPyWkUh8T/cWML/OM.VhDiuJaii877zf3SvsfN5...">

>> user.name = "Reimu Hakurei"
=> "Reimu Hakurei"

>> user.password = "foobar"
=> "foobar"

>> user.save
   (0.2ms)  SAVEPOINT active_record_1
  User Exists (0.3ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) AND ("users"."id" != ?) LIMIT ?  [["email", "mhartl@example.com"], ["id", 1], ["LIMIT", 1]]
  SQL (10.5ms)  UPDATE "users" SET "name" = ?, "updated_at" = ?, "password_digest" = ? WHERE "users"."id" = ?  [["name", "Reimu Hakurei"], ["updated_at", "2019-10-13 07:17:16.160555"], ["password_digest", "$2a$10$wqQF.ZaFM.8HHI0mhpyPWe5Iv0fntWa33kHBjL9MDWeSdv2B/GuRi"], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> true

>> user.reload
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Reimu Hakurei", email: "mhartl@example.com", created_at: "2019-10-13 06:43:53", updated_at: "2019-10-13 07:17:16", password_digest: "$2a$10$wqQF.ZaFM.8HHI0mhpyPWe5Iv0fntWa33kHBjL9MDWe...">
```

`user.password`属性の値を与えてやることにより、`user`を`save`することができるようになりました。RDBMS側の情報もきちんと更新されていますね。