プログラミング学習ノート

プログラミング学習の記録用です。

クラス内のメソッドAの変数をメソッドBの引数で受け取る

実現したいこと

あるメソッドのデータを別のデータの引数で受け取りたい。

エラー

Uncaught SyntaxError: Identifier 'name' has already been declared

同じスコープ内で変数名が2回以上宣言されていることを示している。
JavaScriptでは、同じスコープ内で変数名を再宣言することはできない。

ソースコード

class Student {
  constructor(name) {
    let name = name;
  }
  hello (name) {
    return `こんにちは、${name}です。`
  }
}

let student = new Student('田中');
console.log(student.hello());

問題

ただletで変数に代入するだけでは、インスタンスの持ち物になっていない。
インスタンスのプロパティとするには、thisを使う必要がある。

試したこと

メソッドの引数をthis.nameとした。

class Student {
  constructor(name) {
    this.name = name;
  }
  hello (this.name) {
    return `こんにちは、${this.name}です。`
  }
}

let student = new Student('田中');
console.log(student.hello());

Uncaught SyntaxError: Unexpected token 'this'構文エラーとなった。

解決法

メソッドの定義の中でthis.songsを参照した。

class Student {
  constructor(name) {
    this.name = name;
  }
  hello () {
    return `こんにちは、${this.name}です。`
  }
}

let student = new Student('田中');
console.log(student.hello());

プロパティへのアクセス方法が間違っていた。
プロパティを参照するだけなら引数で受け渡しする必要はなかった。

イベントバブリングの解消

実現したいこと

TODOリストのうち、選択したタスクだけに取り消し線を引きたい。

問題

複数のタスクを作成すると、取り消し線が引かれるタスクと引かれないタスクがある。

ソースコード

// チェックを入れるとタスクに取り消し線を引く
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach(function(checkbox){
  checkbox.addEventListener('change', function(event){
    const taskElement = event.target.parentNode.querySelector('span');
    console.log(event.target);
    taskElement.classList.toggle('done');
  })
});

原因

イベントバブリングが生じた。

イベントバブリングとは?

イベントバブリングは、DOM内でイベントが発生した要素から親要素へと順番に伝播する現象。
HTML要素のツリー構造によって生じるもので、イベントが発生した要素から親要素、その親要素の親要素へと順にイベントが伝わっていくため、複数の要素が同じイベントをキャッチすることができる。

今回起きたイベントバブリングの概要

今回のイベントのターゲット要素は<input type="checkbox">。その親要素は<li>。さらにその親要素は<ul>
本来はチェックを入れたタスクごと(<li>ごと)に処理を実行したいのに、親要素の<ul>にもイベントが伝播していたと考えられる。

解決法

イベントデリゲーションを使う。

イベントデリゲーションとは

親要素に1つのイベントリスナーを追加し、子要素で発生したイベントを親要素でキャッチし、その後、適切な子要素を特定して処理を行う手法。デリゲートは委任という意味。子要素の共通の親要素にイベントキャッチ処理を委任する。

//ulタグの要素を取得
const todoList = document.getElementById('todo-list');
// チェックを入れるとタスクに取り消し線を引く
todoList.addEventListener('click', function(event){
  if (event.target.tagName === 'INPUT' && event.target.type === 'checkbox') {
    const listItem = event.target.parentNode;
    listItem.classList.toggle('done');
  }
})

チェックボックスにチェックが入ったら(ifの条件)、チェックボックスの親要素である<li>を取得して、クラス名「done」を設定。 CSSファイルでdoneクラスに取り消し線をしているので、チェックが入ると取り消し線が引かれる。 toggleメソッドは、クラス名がなければ新たに設定し、あれば変更するという便利なメソッド。

.done {
  text-decoration: line-through;
}

学んだこと

  • イベントデリゲーションは子とその直接の親じゃなくても設定できる。 子とその親の親でも可。
  • addEventListenerは対象とするオブジェクトが必ずしもeventと同じとは限らない。

今回addEventListenerを<ul>の要素ノードに指定しているが、eventのターゲットはチェックボックス。addEventListenerの対象となるオブジェクトが広い場合、eventがその子要素となることもある。addEventListenerでイベントリスナーを設定した要素はその子孫要素に対しても影響を及ぼす。

ループ処理から抜けられない(決着がつかない)

実現したいこと

どちらかの残り枚数がゼロになって勝敗がつくようにしたい。

問題

何回やってもどちらかの残り枚数がゼロにならない。

考えたこと

  1. カードがシャッフルできていない?
  2. 引き分けたときのカードが正しく渡っていない?
  3. 最初に均等に分けて配られて、その後固定した順番で回しているのが問題?

自分でやったこと

1. カードがシャッフルされているか?

pメソッドで2つに分けたカード束の中身がシャッフルされているかを確認しました。

# models/deck.rb
deck = Deck.new
p deck.separate_cards
[card_game]$ ruby models/deck.rb
{true=>[["スペード", "9"], ["クラブ", "J"], ["ダイヤ", "6"], ["スペード", "A"], ["ハート", "2"], ["クラブ", "6"], ["クラブ", "4"], ["クラブ", "3"], ["クラブ", "Q"], ["クラブ", "8"], ["スペード", "7"], ["ハート", "8"], ["ダイヤ", "K"], ["ハート", "K"], ["スペード", "5"], ["クラブ", "7"], ["クラブ", "10"], ["ダイヤ", "9"], ["ダイヤ", "2"], ["ハート", "9"], ["ハート", "3"], ["ハート", "Q"], ["ダイヤ", "A"], ["スペード", "Q"], ["クラブ", "A"], ["ハート", "4"]], false=>[["スペード", "6"], ["ハート", "5"], ["ダイヤ", "5"], ["スペード", "8"], ["スペード", "J"], ["クラブ", "2"], ["ダイヤ", "10"], ["クラブ", "5"], ["スペード", "4"], ["ダイヤ", "3"], ["ダイヤ", "J"], ["ダイヤ", "4"], ["スペード", "10"], ["スペード", "2"], ["ハート", "7"], ["ハート", "J"], ["ダイヤ", "Q"], ["クラブ", "9"], ["スペード", "K"], ["スペード", "3"], ["ダイヤ", "8"], ["ハート", "A"], ["ダイヤ", "7"], ["ハート", "10"], ["ハート", "6"], ["クラブ", "K"]]}

2. 引き分けたときのカードが正しく渡っているか?

pメソッドで引き分けたときにたまったカードが勝者の手札束配列の最後尾に追加されていることを確認しました。

戦争!
プレイヤー1のカードはクラブの2です。
プレイヤー2のカードはスペードの2です。
引き分けです。
[["クラブ", "2"]]
[["クラブ", "2"], ["スペード", "2"]] #引き分けのカードは@draw_cardsに保存される
戦争!
プレイヤー1のカードはクラブのAです。
プレイヤー2のカードはクラブのQです。
今回はプレイヤー1が勝ちました。
# カードが付与される前
[["スペード", "J"], ["ハート", "Q"], ["スペード", "A"], ["ダイヤ", "J"], ["クラブ", "3"], ["ダイヤ", "3"], ["ダイヤ", "Q"], ["クラブ", "K"], ["ハート", "3"], ["ハート", "8"], ["ダイヤ", "9"], ["クラブ", "10"], ["ハート", "2"], ["スペード", "Q"], ["ハート", "7"], ["クラブ", "4"], ["ハート", "6"], ["スペード", "10"], ["クラブ", "J"], ["スペード", "8"], ["スペード", "4"], ["ハート", "9"], ["スペード", "6"], ["スペード", "3"], ["ダイヤ", "2"]]
# カードが付与された後
[["スペード", "J"], ["ハート", "Q"], ["スペード", "A"], ["ダイヤ", "J"], ["クラブ", "3"], ["ダイヤ", "3"], ["ダイヤ", "Q"], ["クラブ", "K"], ["ハート", "3"], ["ハート", "8"], ["ダイヤ", "9"], ["クラブ", "10"], ["ハート", "2"], ["スペード", "Q"], ["ハート", "7"], ["クラブ", "4"], ["ハート", "6"], ["スペード", "10"], ["クラブ", "J"], ["スペード", "8"], ["スペード", "4"], ["ハート", "9"], ["スペード", "6"], ["スペード", "3"], ["ダイヤ", "2"], ["クラブ", "A"], ["クラブ", "Q"]] # 直近で出したカードと引き分けのカードが付与されている
プレイヤー1は29枚もっています。
プレイヤー2は23枚もっています。

3. 毎回出すカードをランダムで選択するとどうなるか?

固定した順番ではなく、毎回ランダムでカードを選ぶようにした結果、勝敗がつきました。

class Player
  def submit_card
    puts "#{name}のカードは#{hand.first[0]}#{hand.first[1]}です。"
    # 変更前:裏向きの手札束の一番上のカードを出す(問題文のとおり)
    @submitted_card = hand.first
    # 変更後:シャッフルして選ぶ(問題文と異なる)
    @submitted_card = hand.shuffle!.first
    # 場に出すカードは手札束からなくなる
    hand.shift(1)
  end
end
(省略)
戦争!
プレイヤー1のカードはスペードの2です。
プレイヤー2のカードはダイヤの6です。
今回はプレイヤー2が勝ちました。
プレイヤー1は0枚もっています。
プレイヤー2は52枚もっています。
プレイヤー2が勝ちました。
戦争を終了します。

他に確認したこと

1. 毎回の合計枚数が52枚になっているかを確認

毎回2人合計して52枚になっていることを確認しました。

戦争を開始します。
カードが配られました。
戦争!
プレイヤー1のカードはハートのJです。
プレイヤー2のカードはスペードの2です。
今回はプレイヤー1が勝ちました。
プレイヤー1は27枚もっています。
プレイヤー2は25枚もっています。
戦争!
プレイヤー1のカードはダイヤの4です。
プレイヤー2のカードはダイヤの10です。
今回はプレイヤー2が勝ちました。
プレイヤー1は26枚もっています。
プレイヤー2は26枚もっています。
戦争!
プレイヤー1のカードはダイヤの3です。
プレイヤー2のカードはダイヤのKです。
今回はプレイヤー2が勝ちました。
プレイヤー1は25枚もっています。
プレイヤー2は27枚もっています。
戦争!
プレイヤー1のカードはスペードのAです。
プレイヤー2のカードはハートの10です。
今回はプレイヤー1が勝ちました。
プレイヤー1は26枚もっています。
プレイヤー2は26枚もっています。
戦争!
プレイヤー1のカードはクラブの10です。
プレイヤー2のカードはハートの5です。
今回はプレイヤー1が勝ちました。
プレイヤー1は27枚もっています。
プレイヤー2は25枚もっています。
戦争!
プレイヤー1のカードはスペードの8です。
プレイヤー2のカードはクラブの6です。
今回はプレイヤー1が勝ちました。
プレイヤー1は28枚もっています。
プレイヤー2は24枚もっています。
戦争!
プレイヤー1のカードはダイヤの8です。
プレイヤー2のカードはクラブのKです。
今回はプレイヤー2が勝ちました。
プレイヤー1は27枚もっています。
プレイヤー2は25枚もっています。
戦争!
プレイヤー1のカードはダイヤのJです。
プレイヤー2のカードはダイヤのAです。
今回はプレイヤー2が勝ちました。
プレイヤー1は26枚もっています。
プレイヤー2は26枚もっています。
戦争!
プレイヤー1のカードはスペードのQです。
プレイヤー2のカードはハートのQです。
引き分けです。
戦争!
プレイヤー1のカードはスペードの5です。
プレイヤー2のカードはスペードの6です。
今回はプレイヤー2が勝ちました。
プレイヤー1は24枚もっています。
プレイヤー2は28枚もっています。

2.毎回の枚数の増減は正しいか?

勝った人は1枚増え、負けた人は1枚減ります。
引き分けをはさんで勝敗がついた場合、引き分け回数分のカードが付与されます。

[card_game]$ ruby main.rb
戦争を開始します。
カードが配られました。
戦争!
プレイヤー1のカードはスペードのQです。
プレイヤー2のカードはスペードのKです。
今回はプレイヤー2が勝ちました。
プレイヤー1は25枚もっています。
プレイヤー2は27枚もっています。
戦争!
プレイヤー1のカードはダイヤの9です。
プレイヤー2のカードはスペードの2です。
今回はプレイヤー1が勝ちました。
プレイヤー1は26枚もっています。
プレイヤー2は26枚もっています。
戦争!
プレイヤー1のカードはスペードのAです。
プレイヤー2のカードはダイヤのAです。
引き分けです。
戦争!
プレイヤー1のカードはクラブのJです。
プレイヤー2のカードはクラブの6です。
今回はプレイヤー1が勝ちました。
プレイヤー1は28枚もっています。
プレイヤー2は24枚もっています。

何ができていて、何ができていないのか

できていること

勝敗はつくようになった。

できていないこと

カードを出すたびにシャッフルしてから出すのは不自然なので、一度配り終えたら順番を変えずに勝敗がつくようにしたい。

何がわからないのか

なぜシャッフルして配っているのに、勝敗がつかないのか?
なぜ毎回ランダムでカードを配ると勝敗がつくのか?

MySQLにログインできない問題でやったことと結果

問題

MySQLにログインできない

エラー

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock.lock' (2)

考えられる原因

MySQLサーバーが正しく起動していない
②ソケットファイルが存在しないか、違う場所にある
ファイアウォールやセキュリティ設定がMySQLのポートへのアクセスをブロックしている

やったこと

MySQLサーバーが正しく起動しているか確認する
MySQLを起動する
MySQLのデータディレクトリを作成
MySQLデータディレクトリに対してmysqlユーザーとmysqlグループに書き込み権限を付与
⑤--secure-file-privオプションの設定を確認し、安全なディレクトリに設定
⑥/var/lib/mysql-filesディレクトリを作成し、アクセス許可を設定
⑦バイナリログの保存場所を指定
⑧/var/lib/mysql/mysql-bin.indexファイルを作成し、権限を付与
⑨datadir=/var/lib/mysqlを削除
⑩データディレクトリのパーミッションを設定

①の結果

プロセスが表示されていないので、MySQLサーバーが起動していないと考えられる。

[~]$ ps aux | grep mysqld
username        48076   0.0  0.0 408626896   1360 s000  S+   11:36AM   0:00.01 grep mysqld

MySQLサービスが起動しているときは、mysqldという名前のプロセスが実行される。

②の結果

mysqld コマンドを使用してMySQLサーバーを手動で起動した。

[~]$ /usr/local/mysql/bin/mysqld
2024-03-10T02:40:31.536909Z 0 [System] [MY-015015] [Server] MySQL Server - start.
(省略)
2024-03-10T02:40:31.705050Z 0 [System] [MY-010116] [Server] /usr/local/mysql/bin/mysqld (mysqld 8.3.0) starting as process 52605
(省略)
2024-03-10T02:40:31.708422Z 0 [ERROR] [MY-013276] [Server] Failed to set datadir to '/var/lib/mysql/' (OS errno: 2 - No such file or directory)
2024-03-10T02:40:31.708452Z 0 [ERROR] [MY-010119] [Server] Aborting

その結果、以下のエラーが出た。

Failed to set datadir to '/var/lib/mysql/' (OS errno: 2 - No such file or directory)

上記エラーは、MySQLがデータディレクトリを見つけることができないため、起動に失敗したことを示している。

実際に確認してみると、/var/lib/mysql/がない。

[~]$ ls -la /var/lib/mysql
ls: /var/lib/mysql: No such file or directory

③の結果

/var/lib/mysql/が作成された。

[~]$ sudo mkdir -p /var/lib/mysql
[~]$ ls -la /var/lib/mysql
total 0
drwxr-xr-x  2 root  wheel   64  3 10 12:01 .
drwxr-xr-x  4 root  wheel  128  3 10 12:01 ..

いくつか警告が出た。

[Warning] [MY-010097] [Server] Insecure configuration for --secure-file-priv: Current value does not restrict location of generated files. Consider setting it to a valid, non-empty path.
 [System] [MY-010116] [Server] /usr/local/mysql/bin/mysqld (mysqld 8.3.0) starting as process 83142
[Warning] [MY-010091] [Server] Can't create test file /var/lib/mysql/mysqld_tmp_file_case_insensitive_test.lower-test
[Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
mysqld: File './binlog.index' not found (OS errno 13 - Permission denied)
[ERROR] [MY-010119] [Server] Aborting
[Warning] [MY-010097] [Server] Insecure configuration for --secure-file-priv: Current value does not restrict location of generated files. Consider setting it to a valid, non-empty path.

secure-file-privの設定が安全でない:現在の値では、生成されるファイルの場所が制限されません。有効で空でないパスに設定することを検討してください。

このオプションは、MySQLサーバーがデータの書き込みや読み取りを許可するディレクトリを制限するために使用される。現在の設定では、デフォルトの値が指定されておらず、すべての場所でファイルの生成が可能となっている。安全性の向上のために、適切なディレクトリを設定する必要がある。

 [Warning] [MY-010091] [Server] Can't create test file /var/lib/mysql/mysqld_tmp_file_case_insensitive_test.lower-test

テスト・ファイル /var/lib/mysql/mysqld_tmp_file_case_insensitive_test.lower-test を作成できない。

MySQLが一時テストファイルを作成しようとした際に、パーミッションの問題が発生したことを警告している。MySQLのデータディレクトリに書き込み権限がない可能性がある。

 [Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
mysqld: File './binlog.index' not found (OS errno 13 - Permission denied)

var/lib/mysql/のファイルシステムは大文字小文字を区別しないのでlower_case_table_names=2 を設定する。 mysqld:ファイル './binlog.index' が見つかりません (OS errno 13 - Permission denied)

./binlog.index ファイルが見つからず、パーミッションが拒否されたことを示している。バイナリログファイルのインデックスが見つからなかったため、MySQLサーバーの起動が中止された。

④の結果

MySQLサーバーが正常に起動するためには、MySQLデータディレクトリに対してmysqlユーザーと mysqlグループに書き込み権限を付与する必要がある。

MySQLデータディレクトリの所有者とグループをmysqlに変更し、書き込み権限を付与した。

 [~]$ sudo chown -R mysql:mysql /var/lib/mysql
Password:
[~]$ ls -la /var/lib/mysql
total 0
drwxr-xr-x  2 _mysql  _mysql   64  3 10 12:01 .
drwxr-xr-x  4 root    wheel   128  3 10 12:01 ..

しかし、先ほどと同じ警告が出てMySQLは起動できなかった。

⑤の結果

/etc/my.cnfで以下のように設定していた。 [mysqld] secure-file-priv = "" secure-file-priv オプションが空の文字列 ("") に設定されている場合、MySQL サーバーは制限なしでファイルの生成と読み取りを許可する。これだとセキュリティ上のリスクを伴う可能性がある。

このオプションを空の文字列に設定すると、MySQLサーバーは任意の場所にファイルを生成できるため、悪意のあるユーザーがデータベースからデータを抜き出す可能性がある。

セキュリティを向上させるために、secure-file-priv オプションを適切なディレクトリに設定し、MySQL サーバーがファイルを生成および読み取り可能なディレクトリを指定する。

[mysqld]
secure-file-priv=/var/lib/mysql-files

上記設定により、MySQLサーバーは/var/lib/mysql-filesディレクトリ内でのみファイルの生成と読み取りを許可する。

再度MySQLの起動を試みた結果、先ほどより表示されるエラーが減った。

[ERROR] [MY-010095] [Server] Failed to access directory for --secure-file-priv. Please make sure that directory exists and is accessible by MySQL Server. Supplied value : /var/lib/mysql-files
[ERROR] [MY-010119] [Server] Aborting

--secure-file-priv のディレクトリにアクセスできませんでした。ディレクトリが存在し、MySQL サーバからアクセス可能であることを確認してください。指定された値 : /var/lib/mysql-files

MySQLサーバーが --secure-file-privオプションで指定されたディレクトリにアクセスできないため、起動に失敗している。エラーメッセージに Failed to access directory for --secure-file-priv とあるように、指定されたディレクトリへのアクセスが拒否されている。

⑥の結果

/var/lib/mysql-files ディレクトリが存在しているかを確認する。

[~]$ ls -la /var/lib/mysql-files
ls: /var/lib/mysql-files: No such file or directory

存在しないので、新しく作成する。 MySQL サーバーがこのディレクトリにアクセスできるように、所有者を mysql ユーザーと mysql グループに変更し、パーミッションを付与する。

sudo mkdir /var/lib/mysql-files
sudo chown -R mysql:mysql /var/lib/mysql-files
sudo chmod 750 /var/lib/mysql-files

再度MySQLを起動した結果、まだいくつか問題が残っている。

[Warning] [MY-010091] [Server] Can't create test file /private/var/lib/mysql/mysqld_tmp_file_case_insensitive_test.lower-test
[System] [MY-010116] [Server] /usr/local/mysql/bin/mysqld (mysqld 8.3.0) starting as process 57252
[Warning] [MY-010091] [Server] Can't create test file /var/lib/mysql/mysqld_tmp_file_case_insensitive_test.lower-test
[Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
mysqld: File './binlog.index' not found (OS errno 13 - Permission denied)
[ERROR] [MY-010119] [Server] Aborting
Can't create test file /private/var/lib/mysql/mysqld_tmp_file_case_insensitive_test.lower-test

テスト ファイル /private/var/lib/mysql/mysqld_tmp_file_case_insensitive_test.lower-test を作成できない。

この警告メッセージは、MySQL サーバーがテストファイルを作成できないことを示している。通常、MySQLサーバーは起動時に一時ファイルを作成し、ファイルシステムのアクセス権限をテストする。

①ディスクスペースを確認する

ディスクがいっぱいになっている場合、新しいファイルを作成できない可能性がある。

[~]$ df -h /var/lib/mysql
Filesystem      Size    Used   Avail Capacity iused ifree %iused  Mounted on
/dev/disk3s5   228Gi   101Gi   104Gi    50%    1.3M  1.1G    0%   /System/Volumes/Data

まだ十分な空き容量があるため、容量不足が問題ではないと考えられる。

ディレクトリのパーミッションを確認する

MySQLデータディレクトリの所有者がmysql ユーザーとmysqlグループになっており、パーミッションの問題もなさそうに見える。

[~]$ ls -al /private/var/lib/mysql/
total 0
drwxr-xr-x  2 _mysql  _mysql   64  3 10 12:01 .
drwxr-xr-x  5 root    wheel   160  3 10 12:55 ..

エラー文で検索してみると、公式リファレンスに以下の記載があった。

起動時に次のタイプのエラーを受け取る場合は、データファイルの格納に使用されるファイルシステムまたはディレクトリが書き込み保護されていることを示しています。 書き込みエラーがテストファイルに対するものであれば、このエラーは重大ではなく、無視しても安全です。 Can't create test file /usr/local/mysql/data/master.lower-test 引用元:MySQL公式リファレンス

いったん無視して、他の警告の解消に取り組む。 残っている問題はこの2つだ。

[Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
mysqld: File './binlog.index' not found (OS errno 13 - Permission denied)

①var/lib/mysql/のファイルシステムは大文字小文字を区別しないので、lower_case_table_names=2 を設定する。 ②mysqld:ファイル './binlog.index' が見つかりません (OS errno 13 - Permission denied)

①についてはアプリケーションやデータベースの設計によっては、この設定が問題を引き起こす場合もあるらしいので、さきに②に取り組む。

⑦の結果

[mysqld]
log_bin=/var/lib/mysql/mysql-bin

再度MySQLを起動した結果、同じ警告が表示された。

[Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
mysqld: File '/var/lib/mysql/mysql-bin.index' not found (OS errno 13 - Permission denied)

/var/lib/mysql/mysql-bin.indexファイルを手動で作成し、権限を付与。

sudo touch /var/lib/mysql/mysql-bin.index
sudo chown _mysql:_mysql /var/lib/mysql/mysql-bin.index

⑧の結果

再度MySQLを起動した結果、同じ警告が表示された。

[Warning] [MY-010159] [Server] Setting lower_case_table_names=2 because file system for /var/lib/mysql/ is case insensitive
mysqld: File '/var/lib/mysql/mysql-bin.index' not found (OS errno 13 - Permission denied)

セーフモードで起動して、エラーを確認する。

[~]$ sudo /usr/local/mysql/bin/mysqld_safe
2024-03-10T05:52:45.6NZ mysqld_safe Logging to '/var/log/mysqld.log'.
2024-03-10T05:52:45.6NZ mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
2024-03-10T05:52:45.6NZ mysqld_safe mysqld from pid file /var/lib/mysql/takahashinaotanoMacBook-Air.local.pid ended

セーフモードでは、MySQLが起動中にエラーが発生しても、そのエラーを処理して継続する。MySQLが正常に起動しない場合でも、エラーログを確認できる。

[ERROR] [MY-011011] [Server] Failed to find valid data directory.
[ERROR] [MY-010020] [Server] Data Dictionary initialization failed.
[ERROR] [MY-010119] [Server] Aborting

MySQLがデータディレクトリを見つけられないため、起動に失敗している。

MySQLは自身の設定ファイル (my.cnf または my.ini) を参照してデータディレクトリを決定するため、以下を削除する。

[mysqld]
datadir=/var/lib/mysql

⑨の結果

セーフモードでは起動できるようになった。 セーフモードで起動中、クライアント側で接続できるようになった。

通常モードで起動を試みると、依然として以下エラーが出る。

[ERROR] [MY-013276] [Server] Failed to set datadir to '/usr/local/mysql-8.3.0-macos14-arm64/data/' (OS errno: 13 - Permission denied)

データディレクトリ(/usr/local/mysql-8.3.0-macos14-arm64/data/)のパーミッションを設定する必要がある。

[~]$ sudo chown -R _mysql:_mysql /usr/local/mysql-8.3.0-macos14-arm64/data
[~]$ sudo chmod -R 750 /usr/local/mysql-8.3.0-macos14-arm64/data

⑩の結果

依然として同じエラーが出た。

[ERROR] [MY-013276] [Server] Failed to set datadir to '/usr/local/mysql-8.3.0-macos14-arm64/data/' (OS errno: 13 - Permission denied)

mysqlが2つ存在していることがわかった。

[~]$ cd /usr/local
[local]$ ls
mysql
mysql-8.3.0-macos14-arm64 →こっちを削除

パスを確認すると、前者に設定されている。

[local]$ which mysql
/usr/local/mysql/bin/mysql

データディレクトリの場所を修正する。以下を追加。

[mysqld]
datadir=/usr/local/mysql/data

MySQLの指定は変わったが、同じエラーが出る。

[ERROR] [MY-013276] [Server] Failed to set datadir to '/usr/local/mysql/data/' (OS errno: 13 - Permission denied)

ディレクトリの権限を変更。

sudo chmod -R 755 /usr/local/mysql/data

エラーが変わった。

mysqld: File '/var/lib/mysql/mysql-bin.index' not found (OS errno 13 - Permission denied)

ファイルは存在する。となると、権限の問題か?

[~]$ ls -l /var/lib/mysql/mysql-bin.index
-rw-r-----  1 _mysql  _mysql  96  3 10 16:07 /var/lib/mysql/mysql-bin.index

ファイルの権限を変更。

sudo chmod 644 /var/lib/mysql/mysql-bin.index

エラーの原因 バイナリログファイルを正しい場所に配置していなかったから。

バイナリログファイルは、デフォルトの設定ではdatadirディレクトリ内に保存される。 通常と違う場所に配置する場合は、どこに配置したかを設定ファイル(my.cnf)に書く必要がある。

MySQLサーバーは設定ファイルの指示にしたがってファイルを探しに行く。 lsコマンドで表示されるのにnot foundとなる理由は、物理的に存在していても、設定ファイルで指定していないと探せないから。

データディレクトリはデフォルトの設定だと/var/lib/mysql/らしい。 データディレクトリをから/usr/local/mysql/data/から/var/lib/mysql/に変更する。

通常モードだと同じエラーが出る。 セーブモードでも起動できなくなった。

[ERROR] [MY-011011] [Server] Failed to find valid data directory.
[ERROR] [MY-010020] [Server] Data Dictionary initialization failed.

データディレクトリを見つけられないと。

他の解決記事を見ると、データディレクトリを初期化して解決している。 https://worktoolsmith.com/mysql-error-01/

バックアップをとってからデータディレクトリを削除する。

sudo rm -rf /var/lib/mysql

MySQLの初期化を実行する。

sudo mysqld --initialize --user=mysql --datadir=/var/lib/mysql

データディレクトリの所有者をmysqlに設定する。

sudo chown -R mysql:mysql /var/lib/mysql

セーフモードで再起動できたが、パスワードが違うと言われた。

ERROR 1045 (28000): Access denied for user 'test_user'@'localhost' (using password: NO)

結末

作業中に誤って既存のmysqlディレクトリを丸ごと削除してしまったため、Homebrewで再インストールした。
しかし、mysql_secure_installationを実行すると表題のエラーが出てしまい、結局解決できていない。
次のカリキュラムが始まっているので、あとで時間を見つけて再インストール完了させたい。