アプリの開発の時はMySQLを使っているのだが、文字列型の適切な選択や、判断方法についてはあやふやだったので、まずはcharとvarcharについて復習してみた
MySQLの文字列型の種類
MySQLには大きく分けて、以下の8つの型が存在する。
- CHAR
- VARCHAR
- BINARY
- VARBINARY
- BLOB
- TEXT
- ENUM
- SET
CHAR型(M)
- 固定長文字列
- Mで文字数を指定
- 0から255文字まで可能
- 取得する時に末尾についた空白は削除された上で取得される
[補足] 固定長文字列
指定した文字数以下の文字を格納した場合には文字列の末尾に空白を必要なだけ付け加えて指定の長さの文字列として格納する
consoleで確認(Rails)
childテーブルにはchar型のcharカラムが存在するとする
irb(main):005:0> a.char = "a"*254
irb(main):006:0> a.save! #=> 成功
irb(main):005:0> a.char = "a"*255
irb(main):006:0> a.save! #=> 失敗
irb(main):007:0> a.char = "あ"*254
irb(main):006:0> a.save! #=> 成功
irb(main):009:0> a.char = "あ"*255
irb(main):006:0> a.save! #=> 失敗
文字数に依存していることがわかる
VARCHAR型(M)
- 可変長文字列
- Mはバイト数を指定する
- 0~65535バイト
[補足] 可変長文字列
末尾に空白を付けるようなことはしません。また現行のバージョンでは末尾に空白がある文字列であっても空白が付いたまま格納される
consoleで確認(Rails)
aモデルにはvarchar型のvarcharカラムが存在するとする
varchar(21800) DEFAULT NULLで設定した
irb(main):024:0> child.varchar = 'b'*21800
irb(main):025:0> child.varchar.bytesize #=> 21800
irb(main):027:0> child.save! #=> 成功
irb(main):024:0> child.varchar = 'b'*21801
irb(main):028:0> child.varchar.bytesize #=> 21801
irb(main):027:0> child.save! #=> 失敗
irb(main):024:0> child.varchar = 'あ'*21800
irb(main):030:0> child.varchar.bytesize #=> 65400
irb(main):027:0> child.save! #=> 成功
irb(main):024:0> child.varchar = 'b'*21801
irb(main):032:0> child.varchar.bytesize #=> 65403
irb(main):027:0> child.save! #=> 失敗
bytesizeメソッドでbyteサイズが測れる
VARCHARは65532までが最大有効長なのに、なぜ今回は21800にしているか
UTF-8は最大3バイト
食うので、utf-8を使っている場合は、入力文字が全て3バイトだった時の文字数しか使えない!!
なのでMAXでもVARCHARでは、 65532➗3 = 21845文字
になります。
VARCHARで、バイト数を指定する時の決め方
tokensテーブルにtokenをvarcharで保存するとする時、Railsではマイグレーションを下記のように記述する
class CreateTokens < ActiveRecord::Migration[5.2]
def change
create_table :tokens do |t|
t.string :token, limit:1500
t.timestamps
end
end
end
今回は、tokenのバイト数の指定を1500としているが、バイト数の決め方の判断として
サンプルtokenのバイト数を計測し、保存するであろうtokenが必ず治るバイト数を指定するのが望ましいと考えられます。
$ sample_token = "hoge..........."
$ sample_token.bytesize #=> 15
# sample_tokenが暗号化前の時は、
$ len = ActiveSupport::MessageEncryptor.key_len
$ salt = SecureRandom.random_bytes(len)
$ key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, len)
$ crypt = ActiveSupport::MessageEncryptor.new(key)
$ crypt.encrypt_and_sign(sample_token).bytesize # => 138になる!
暗号化の方法に関してはこちらを参照にしました
https://api.rubyonrails.org/v5.2.1/classes/ActiveSupport/MessageEncryptor.html