20200219のMySQLに関する記事は12件です。

【初学者のつまずきを記録】RailsでのMySQL接続:mysql.sock問題

はじめに

RailsでのMySQL接続手順について、備忘録的にまとめようと思います。
作業自体はごく基本的な内容かと思いますが、
初学者である僕が実際につまずいたことを重点的に記載しました。
僕と同じ初学者の方のご参考の一助となればうれしいです。
後半部分ではつまずいた原因について検討してみました。

※僕自身は「RailsのDBを(初めから| |後から)MySQLに変更する」を参考にしながら進めました。

前準備:MySQLを指定してRailsアプリを作成

Railsの仕様上、データベースを指定しない場合は自動的にSQLiteとなる。

$ rails new アプリ名 -d mysql

作成されたconfig/database.ymlを確認。

config/database.yml
default: &default
  adapter: mysql2
          ()

確かにMySQLとなっていることを確認。

MySQLとの接続

手順通り接続を試みる。が、エラー発生。

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

mysql.sockを通じての接続ができなかったとのこと。

調べてみると、mysql.sockファイル自体が存在しないことが問題らしい。
確かに、tmpフォルダ内を確認してみても存在しない。下記実行で作成。

$ sudo touch /tmp/mysql.sock

その上で接続を試みても同じようなエラー発生。

$ mysql -u root             
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (38)

そもそも、mysql.sockファイルとは何だろう?
調べてみると、サーバーとクライアントの間で通信を行う際の仲介役的な役割らしい。
ここで、MySQLサーバーを起動していないことに気づきました。
というかエラー分にもしっかり MySQL serverとありますね。

MySQLサーバーを起動。

$ mysql.server start
Starting MySQL
.. ERROR! The server quit without updating PID file (/usr/local/var/mysql/xxxx.pid).

PIDファイルがないとのお達し。
指定された場所に作成をする。

$ touch /usr/local/var/mysql/xxxx.pid

その上でサーバー起動を試みるも、、、

$ mysql.server start                            
Starting MySQL
.rm: /tmp/mysql.sock: Permission denied
2020-02-17T12:13:34.6NZ mysqld_safe Fatal error: Can't remove the socket file:
/tmp/mysql.sock.
Please remove the file manually
         (略)

別のエラーが発生。
mysql.sockを削除しろと??
ないと言うから作ったのに。

言われるがままに削除し、
どこか不本意な気持ちのまま再度サーバー起動。成功。

$ mysql.server start                            
Starting MySQL
. SUCCESS! 
$ mysql -u root                                 
Welcome to the MySQL monitor. 
      (略)
mysql> 

MySQLへの接続も成功したようだ。釈然としない。

成功した理由を考えてみた

調べてみると、mysql.sockはMySQLサーバー起動時に自動的に作成されるファイルであり、
手動で作成する類のものではないらしい。
つまり、一番始めに発生した下記エラーは、mysql.sockを作成していないことが問題ではなく、
Can't connect:つなげない状態」であったことが問題であったと思われる。

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

よって、mysql.sockがない状態でも、
PIDファイルを作成してサーバー起動を成功させることによって(=ここでmysql.sockが自動作成され)
MySQLへの接続も成功した、、、という理解でいいのだろうか。

では手動作成のmysql.sockは何が問題であったのか?

調べてみるとどうやら手動で作成したmysql.sockは権限に問題があるようだ。
検証のために再度手動でmysql.sockを作成し、
自動作成(サーバー起動時)されたmysql.sockと$ ls -lの出力を比較してみた。

#手動
-rw-r--r--  1 root            wheel   0  2 17 22:56 mysql.sock

#自動作成
srwxrwxrwx  1 xxxx(ユーザー名)  wheel   0  2 17 22:45 mysql.sock

なるほど違う。

手動で作成された方はrootユーザーのみが書き込みの権限を持っている。
書き込みの権限がないとうまくいかないので、(参考:MySQL の設定上の考慮事項)
rootユーザー以外での実行ができない状態となっている??

ならばと手動作成した上でrootユーザー権限でサーバー起動を試みてみる。

$ sudo mysql.server start   
Starting MySQL
. ERROR! The server quit without updating PID file (/usr/local/var/mysql/xxxx.pid).

だめかー。

では手動作成したものを自動作成と同じ権限に変えてみる。
これでダメな理由はないはず。

$ sudo chmod 777 mysql.sock  #権限を変更
$ sudo chown xxxx(ユーザー名) mysql.sock  #所有者も変更
$ mysql.server start                            
Starting MySQL
. SUCCESS! 

成功した!

手動でmysql.sockを作成した場合でも
権限周りを調整することによりサーバー起動ができることの確認がとれました。

まとめ

解決方法を色々とググりながら進めましたが、
「まずはじっくりとエラー分を解釈すること」の大切さを痛感しました。
またlinux等の知識が不足しており、後半の検証部分は気持ちとしてしこりが残っております。
(rootユーザー権限でサーバー起動できなかったあたり等)
今後の課題にするようにしたいと思います。

はじめての記事投稿でしたが、思考を整理するとても良い機会となりました。
誤り・認識違い、アドバイス等ご指摘いただけましたらとてもありがたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Microsoft SQL Server から MySQL(MariaDB) へテーブル移行

いろいろ方法があるらしいが、今回は練習がてら自分で作ったスクリプトで変換。

ここではテーブルを作る。

データの移行はこちら
MS SQL Server からMySQL(MariaDB) へテーブルデータ移行 - Qiita

環境

移行元

  • Microsoft SQL Server 2008 R2
  • Windows 2003 Foundation

移行先

  • MariaDB (mysql Ver 15.1 Distrib 10.1.44-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2)
  • Ubuntu 18.04

元データ

CREATE TABLE [dbo].[会員](
        [stid] [int] IDENTITY(1,1) NOT NULL,
        [会員番号] [char](10) NOT NULL,
        [漢字氏名] [nvarchar](255) NULL,
        [フリガナ] [nvarchar](255) NULL,
        [性別] [tinyint] NULL,
        [生年月日] [datetime] NULL,
        [入会年度] [smallint] NULL,
        [所属コード] [nvarchar](8) NULL,
        [年次] [tinyint] NULL,
        [種別コード] [nvarchar](5) NULL,
        [郵便番号] [int] NULL,
        [現住所] [nvarchar](255) NULL,
        [携帯番号] [nvarchar](50) NULL,
 CONSTRAINT [PK_会員1] PRIMARY KEY CLUSTERED
(
        [stid] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
EXEC sys.sp_addextendedproperty @name=N'MS_AggregateType', @value=-1 , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'会員', @level2type=N'COLUMN',@level2name=N'stid'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_ColumnHidden', @value=0 , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'会員', @level2type=N'COLUMN',@level2name=N'stid'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_ColumnOrder', @value=1 , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'会員', @level2type=N'COLUMN',@level2name=N'stid'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_ColumnWidth', @value=-1 , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'会員', @level2type=N'COLUMN',@level2name=N'stid'
GO



変換スクリプト

まず、nkfで文字コードをutfにしておく。

nkf -Lu 会員.sql > 会員.sql.utf8

あとは sed で

cat  会員.sql.utf8 | sed 's/[][]/`/g' | sed 's/`hogehoge`/`test`/' |  sed 's/`dbo`/`test`/' | sed 's/^GO//' | sed 's/^SET ANSI_NULLS ON//'  | sed 's/^SET QUOTED_IDENTIFIER ON//'  | sed 's/^SET ANSI_PADDING ON//' | sed 's/^SET ANSI_PADDING OFF//'|  sed 's/"dbo".//' | sed 's/IDENTITY(1,1) NOT NULL/AUTO_INCREMENT NOT NULL PRIMARY KEY/' | sed 's/^EXEC.*//' | sed '/^$/d' | sed 's/`char`/char/g'| sed 's/`nvarchar`/nvarchar/' |sed 's/`tinyint`/tinyint/'| sed 's/`datetime`/datetime/' |sed 's/`smallint`/smallint/'| sed 's/`int`/int/' |sed 's/"/`/g' | sed '/^ CONSTRAINT/,/^) ON /d' | sed '$s/.$/)/'  > 会員.mysql

↑はワンライナーで行ったもの。改行を入れたもの↓。

cat  会員.sql.utf8 | \
sed 's/[][]/`/g' | \
sed 's/`hogehoge`/`test`/' | \
sed 's/`dbo`/`test`/' \
sed 's/^GO//' | \
sed 's/^SET ANSI_NULLS ON//'  | \
sed 's/^SET QUOTED_IDENTIFIER ON//'  | \
sed 's/^SET ANSI_PADDING ON//' | \
sed 's/^SET ANSI_PADDING OFF//'|  \
sed 's/"dbo".//' | \
sed 's/IDENTITY(1,1) NOT NULL/AUTO_INCREMENT NOT NULL PRIMARY KEY/' | \
sed 's/^EXEC.*//' | \
sed '/^$/d' | \
sed 's/`char`/char/g'| \
sed 's/`nvarchar`/nvarchar/' | \
sed 's/`tinyint`/tinyint/'| \
sed 's/`datetime`/datetime/' | \
sed 's/`smallint`/smallint/'| \
sed 's/`int`/int/' |sed 's/"/`/g' | \
sed '/^ CONSTRAINT/,/^) ON /d' | \
sed '$s/.$/)/' |  \

> 会員.mysql

会員.mysqlができる

CREATE TABLE `test`.`会員`(
    `stid` int AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `会員番号` char(10) NOT NULL,
    `漢字氏名` nvarchar(255) NULL,
    `フリガナ` nvarchar(255) NULL,
    `性別` tinyint NULL,
    `生年月日` datetime NULL,
    `入会年度` smallint NULL,
    `所属コード` nvarchar(8) NULL,
    `年次` tinyint NULL,
    `種別コード` nvarchar(5) NULL,
    `郵便番号` int NULL,
    `現住所` nvarchar(255) NULL,
    `携帯番号` nvarchar(50) NULL)


これをmysql(MariaDB)から

MariaDB [test]> source 会員.mysql

でOK.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Microsoft SQL Server から MySQL へテーブル移行

いろいろ方法があるらしいが、今回は練習がてら自分で作ったスクリプトで変換。

元データ

CREATE TABLE [dbo].[会員](
        [stid] [int] IDENTITY(1,1) NOT NULL,
        [会員番号] [char](10) NOT NULL,
        [漢字氏名] [nvarchar](255) NULL,
        [フリガナ] [nvarchar](255) NULL,
        [性別] [tinyint] NULL,
        [生年月日] [datetime] NULL,
        [入会年度] [smallint] NULL,
        [所属コード] [nvarchar](8) NULL,
        [年次] [tinyint] NULL,
        [種別コード] [nvarchar](5) NULL,
        [郵便番号] [int] NULL,
        [現住所] [nvarchar](255) NULL,
        [携帯番号] [nvarchar](50) NULL,
 CONSTRAINT [PK_会員1] PRIMARY KEY CLUSTERED
(
        [stid] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
EXEC sys.sp_addextendedproperty @name=N'MS_AggregateType', @value=-1 , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'会員', @level2type=N'COLUMN',@level2name=N'stid'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_ColumnHidden', @value=0 , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'会員', @level2type=N'COLUMN',@level2name=N'stid'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_ColumnOrder', @value=1 , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'会員', @level2type=N'COLUMN',@level2name=N'stid'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_ColumnWidth', @value=-1 , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'会員', @level2type=N'COLUMN',@level2name=N'stid'
GO
・
・
・

変換スクリプト

まず、nkfで文字コードをutfにしておく。

nkf -Lu 会員.sql > 会員.sql.utf8

あとは sed で

cat  会員.sql.utf8 | sed 's/[][]/`/g' | sed 's/gaku2009_cs/test/' | sed 's/^GO//' | sed 's/^SET ANSI_NULLS ON//'  | sed 's/^SET QUOTED_IDENTIFIER ON//'  | sed 's/^SET ANSI_PADDING ON//' | sed 's/^SET ANSI_PADDING OFF//'|  sed 's/"dbo".//' | sed 's/IDENTITY(1,1) NOT NULL/AUTO_INCREMENT NOT NULL PRIMARY KEY/' | sed 's/^EXEC.*//' | sed '/^$/d' | sed 's/`char`/char/g'| sed 's/`nvarchar`/nvarchar/' |sed 's/`tinyint`/tinyint/'| sed 's/`datetime`/datetime/' |sed 's/`smallint`/smallint/'| sed 's/`int`/int/' |sed 's/"/`/g' | sed '/^ CONSTRAINT/,/^) ON /d' | sed '$s/.$/)/' |  sed 's/`dbo`/`test`/' > 会員.mysql

会員.mysqlができる

CREATE TABLE `test`.`会員`(
    `stid` int AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `会員番号` char(10) NOT NULL,
    `漢字氏名` nvarchar(255) NULL,
    `フリガナ` nvarchar(255) NULL,
    `性別` tinyint NULL,
    `生年月日` datetime NULL,
    `入会年度` smallint NULL,
    `所属コード` nvarchar(8) NULL,
    `年次` tinyint NULL,
    `種別コード` nvarchar(5) NULL,
    `郵便番号` int NULL,
    `現住所` nvarchar(255) NULL,
    `携帯番号` nvarchar(50) NULL)


これをmysqlから

MariaDB [test]> source 会員.mysql

でOK.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laradoc環境作成でmysql接続でつまずいた話

laradock環境にて::ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock'

事象

php artisan migrate

上記コマンドを流すと、タイトルのエラーが発生。。。

いやぁまいったな。。。。

とりあえずググるか。。。

某記事1 DB_HOST=localhostとしてください。

ふむふむやってみよう。。。

数十秒後

できねぇな、、、

某記事2 DB_HOST=127.0.0.1としてください。

ほんとか。。!?

数十秒後

やっぱりできんやないかい。。。

解決策

どうやら立ち上がっているコンテナに紐付けられている
HOSTにしなければいけないみたい。。。
なるほどね。
ってことで下記手順

プロジェクト配下で

docker-compose exec mysql /bin/bash

下記で一番下に出てきたIPをDB_HOSTに記載!

cat /etc/hosts

んで改めてmigrateコマンド実行するとうまくいきました!!!

いやぁまいったね。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ナイーブツリーにおける任意階層にある複数ノード配下の末端ノードを1クエリで全て取得する

目的

  • ナイーブツリーで構成されている多階層テーブルにおいて
  • 任意の階層に所属しているノード群(= 親ノード)に所属している末端ノードの一覧を
  • 親ノードとの関連が分かるように
  • 1クエリで

取得したい。

つまり

親ノードid 末端ノードid
3 20
3 21
3 22
4 25
4 26
4 27

みたいな結果を取得したい。

環境

MySQL 8~

MySQLだと8系以降でしか再帰クエリが使えないため。
他DBMSでも再帰クエリが使えれば同じことはできるはず。多分。

前提のテーブル構造

tableName: naive_tree

id parent_id depth is_leaf
プライマリ 親id 階層 末端ノードならtrue

再帰クエリについて

ざっくり。

-- ここから再帰クエリ。`temporary_table_name`という名前で、hoge, fuga, piyo, ...の構造を持つ一時的なテーブルを作成する
WITH RECURSIVE <temporary_table_name> (<hoge>, <fuga>, <piyo>, ...) AS (
    -- ここから非再帰項
    -- 最初に実行され、再帰項を実行するベースデータを取得する
    SELECT <hoge>, <fuga>, <piyo>, ... -- 上記で定義したものと同じ構造である必要がある
    -- ここまで非再帰項

        UNION ALL -- 非再帰項と再帰項それぞれで取得できた結果をつなげる

    -- ここから再帰項
    -- 非再帰項の取得結果をベースに実行される
    SELECT <hoge>, <fuga>, <piyo>, ... -- 上記で定義したものと同じ構造である必要がある
    FROM <temporary_table_name> WHERE <condition> -- 一時テーブル自身(temporary_table_name)を使う
    -- ここまで再帰項
)
-- ここまで再帰クエリ
SELECT *
FROM temporary_table_name

クエリ

SET @target_parent_depth = 1; -- 階層数を指定

WITH RECURSIVE tree(id, parent_id, depth, is_leaf, target_depth_parent_id) AS (
    -- 指定された階層のレコードを全て取得
    -- 自分自身が親なのでtarget_depth_parent_idは0としておく
    SELECT id, parent_id, depth, is_leaf, 0 as target_depth_parent_id
    FROM naive_tree
    WHERE id IN (
        SELECT n.id
        FROM naive_tree n
        WHERE n.depth = @target_parent_depth
    )
      UNION ALL
    SELECT
        n2.id,
        n2.parent_id,
        n2.depth,
        n2.is_leaf,
        (
            CASE
                WHEN n2.depth = @target_parent_depth + 1 THEN n2.parent_id
                ELSE t.target_depth_parent_id
            END
        ) as target_depth_parent_id
        -- ここがキモ
        -- JOINされたレコードが非再帰項で取得された階層の「次の」階層だった場合には、JOINされた側のレコードが持つparent_idを使う。
        -- それ以外のケースでは、JOIN元のレコードのtarget_depth_parent_idをそのまま使う。
    FROM tree t
    INNER JOIN naive_tree n2 ON t.id = n2.parent_id
)
SELECT
    t.id,
    t.target_depth_parent_id
FROM tree t
WHERE t.is_leaf = 1 AND target_depth_parent_id != 0 -- 末端ノードであり、かつ親レコードではないことを条件とする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【MySQL】改行を取り除く

はじめに

DBのレコード中に改行が含まれていると、コンソール上で見た時に以下のようにずれて表示されてしまい、とても見づらくなります。

このようにコンソールで見る場合は「見づらい」だけで済みますが、改行が含まれるレコードをCSVとしてエクスポートした場合はCSVのフォーマットが崩れてしまうため、エクスポートした後に改行を取り除く処理を入れることになって手間がかかります。

mysql> SELECT id, name, note FROM nkojima.user;
+----+----------+----------------------------+
| id | name     | note                       |
+----+----------+----------------------------+
|  1 | nkojima  | Hello,world!               |
|  2 | nkojima2 | Hello,world!
 Hello,world! |
+----+----------+----------------------------+
2 rows in set (0.01 sec)

使用した環境

  • OS
    • CentOS-7.7
  • DB
    • MySQL-8.0.19

改行を取り除く方法

以下のようにREPLACE関数を用いると、改行コードを除去した結果が簡単に得られます。
以下の例では改行コードを空文字と置き換えて単純に除去しただけですが、例えば|など他の文字に置き換えることも可能です。

また、「あるカラムの先頭もしくは末尾に改行コードがある」というケースであれば、代わりにTRIM関数を使うことも出来ると思いますが、汎用性が高いREPLACE関数を覚えておけば良いと思います。

mysql> SELECT id, name, REPLACE(note, '\n', '') FROM nkojima.user;
+----+----------+---------------------------+
| id | name     | REPLACE(note, '\n', '')   |
+----+----------+---------------------------+
|  1 | nkojima  | Hello,world!              |
|  2 | nkojima2 | Hello,world! Hello,world! |
+----+----------+---------------------------+
2 rows in set (0.00 sec)

参考URL

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【MySQL】レコード中の改行を取り除く

はじめに

DBのレコード中に改行が含まれていると、コンソール上で見た時に以下のようにずれて表示されてしまい、とても見づらくなります。

このようにコンソールで見る場合は「見づらい」だけで済みますが、改行が含まれるレコードをCSVとしてエクスポートした場合はCSVのフォーマットが崩れてしまうため、エクスポートした後に改行を取り除く処理を入れることになって手間がかかります。

mysql> SELECT id, name, note FROM nkojima.user;
+----+----------+----------------------------+
| id | name     | note                       |
+----+----------+----------------------------+
|  1 | nkojima  | Hello,world!               |
|  2 | nkojima2 | Hello,world!
 Hello,world! |
+----+----------+----------------------------+
2 rows in set (0.01 sec)

使用した環境

  • OS
    • CentOS-7.7
  • DB
    • MySQL-8.0.19

改行を取り除く方法

以下のようにREPLACE関数を用いると、改行コードを除去した結果が簡単に得られます。
以下の例では改行コードを空文字と置き換えて単純に除去しただけですが、例えば|など他の文字に置き換えることも可能です。

また、「あるカラムの先頭もしくは末尾に改行コードがある」というケースであれば、代わりにTRIM関数を使うことも出来ると思いますが、汎用性が高いREPLACE関数を覚えておけば良いと思います。

mysql> SELECT id, name, REPLACE(note, '\n', '') FROM nkojima.user;
+----+----------+---------------------------+
| id | name     | REPLACE(note, '\n', '')   |
+----+----------+---------------------------+
|  1 | nkojima  | Hello,world!              |
|  2 | nkojima2 | Hello,world! Hello,world! |
+----+----------+---------------------------+
2 rows in set (0.00 sec)

参考URL

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでプレースホルダを使ったフォーム作成

PHPでフォーム作成

以前の記事で、DVWAでは変数をSQL文にそのままいれたことから、SQLインジェクションが可能なコードとなってしまっていました。

そこで今回は、そんなSQLインジェクションに対処するための方法として、プレースホルダについて紹介したいと思います。

プレースホルダとは?

プレースホルダはその名の通り、SQL文に対して後から値をセットするための場所を確保する機能となっています。

プレースホルダは、疑問符プレースホルダの?、名前付きプレースホルダの:値、で表現されまます。

また、後から入れる値のバインド値のことを、プリペアドステートメント(予約文)と言います。

このプレースホルダを使うことによって、入力値から直接SQLインジェクションが行われることを、回避できるというわけです。

HTML/PHP/MySQLでフォーム作成

ここでは、実際にフォームを作成し、プレースホルダを使うコードについて記載していきたいと思います。

コードは下記の記事を参考にしています。
https://noumenon-th.net/programming/2016/01/18/mysql-2/

前提条件

  • PHP 5.46
  • MySQL 14.14

MySQLでDB、テーブル、カラムの設定

まずは、MySQLでDBとテーブルとカラムをセットしていきます。

ここではDBをtest1、テーブルをname_list、カラムをid・nameでセットします。

# mysql -u root -p

mysql> CREATE DATABASE test1;
mysql> USE test1;
mysql> CREATE TABLE name (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,name TEXT NOT NULL) DEFAULT CHARACTER SET=utf8;

フォームのHTMLファイルを作成

まず基本となる簡単な、フォームをHTMLで記載していきます。

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>データを入力する</title>
    <meta charset="utf-8">
    </head>
<body>
  <h1>フォーム画面</h1> 
      <form action="form.php" method="post">
          名前を入力:<input type="text" name="name">
          <input type="submit" value="登録する">
      </form>
</body>
</html>

フォーム情報を登録するPHPファイル作成

次に、フォーム情報をMySQLに登録するPHPファイルを作成していきます。

SQL文はプレースホルダを使って表示していきます。

form.php
<?php

header("Content-type: text/html; charset=utf-8");

//データベース接続
$server = "IPアドレス";  
$userName = "MySQLのユーザー名"; 
$password = "MySQLのパスワード"; 
$dbName = "test1";

$mysqli = new mysqli($server, $userName, $password, $dbName);

if ($mysqli->connect_error){
    echo $mysqli->connect_error;
    exit();
}else{
    $mysqli->set_charset("utf-8");
}

if(empty($_POST)) {
    echo "<a href='index.php'>index.php</a>←登録はこのページから";
}else{
    //名前入力判定
    if (!isset($_POST['name'])  || $_POST['name'] === "" ){
        echo "名前が入力されていません。";
    }else{
        //プリペアドステートメントの設定(SQL文を最初に用意して、その後はクエリ内のパラメーターの値だけを変更する)
        $stmt = $mysqli->prepare("INSERT INTO name_list (name) VALUES (?)");

        if($stmt){
            //プレースホルダに値を設定する、bind_paramsの第一引数は文字列が入るので's'
            $stmt->bind_param('s', $name);
            $name = $_POST['name'];

            //登録する名前をhtml表記、クオテーションを取った状態で表示
            if($stmt->execute()){
                echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8')."さんを登録しました";
            }else{
                echo $stmt->errno . $stmt->error;
            }

            $stmt->close();
        }else{
            echo $mysqli->errno . $mysqli->error;
        }
    }
}

// データベース切断
$mysqli->close();

?>

実際にはこんな感じに動作します。

demo

これで、プレースホルダを利用したPHPのフォーム作成は完了です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQL8でrootのパスワード忘れた?

Mysql8のインストール作業をしていたのですが、
rootでログインできず、パスワード忘れ?メモ間違えた?
みたいになり対処したのでメモします。

my.cnfの一番下にskip-grant-tablesを追加

パスワードを入力しなくでもログインできるように、my.cnfにskip-grant-tablesを追加します。

sudo vi /etc/my.cnf

[mysqld]
.
.
skip-grant-tables

再起動

systemctl restart mysqld.service

パスワード入力なしでログインする

mysql -u root

パスワード入力しなくてもログインできるようにする

mysql> use mysql
mysql> UPDATE user SET authentication_string=null WHERE user='root';
mysql> flush privileges;
mysql> quit

skip-grant-tablesを無効にする

sudo vi /etc/my.cnf

[mysqld]
.
.
#skip-grant-tables

再起動してパスワード入力なしでログインしてみる

systemctl restart mysqld.service

mysql -u root -p

これでログインできればOKなので、
あとmysql_secure_installationなどで再設定する。

こちらの記事を参考にさせて頂きました。

http://www.petatec.co.jp/achives/495/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQL8でrootのパスワード忘れてしまったので対処

Mysql8のインストール作業をしていたのですが、
rootでログインできず、パスワード忘れ?メモ間違えた?
みたいになり対処したのでメモします。

my.cnfの一番下にskip-grant-tablesを追加

パスワードを入力しなくでもログインできるように、my.cnfにskip-grant-tablesを追加します。

sudo vi /etc/my.cnf

[mysqld]
.
.
skip-grant-tables

再起動

systemctl restart mysqld.service

パスワード入力なしでログインする

mysql -u root

パスワード入力しなくてもログインできるようにする

mysql> use mysql
mysql> UPDATE user SET authentication_string=null WHERE user='root';
mysql> flush privileges;
mysql> quit

skip-grant-tablesを無効にする

sudo vi /etc/my.cnf

[mysqld]
.
.
#skip-grant-tables

再起動してパスワード入力なしでログインしてみる

systemctl restart mysqld.service

mysql -u root -p

これでログインできればOKなので、
あとmysql_secure_installationなどで再設定する。

こちらの記事を参考にさせて頂きました。

http://www.petatec.co.jp/achives/495/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveRecord::PendingMigrationErrorの解決方法

1.エラーメッセージの内容

下記のようなエラーの場合の解決方法を記載します
スクリーンショット 2020-02-19 7.45.15.png

2.エラーの原因

エラーの理由はmigrateし忘れてますよというものです。
考えられる理由は下記の3つです

1.単純にrails db:createのあとrails db:migrateし忘れている
2.すでにmigrationが完了している(up)状態でマイグレーションファイルを更新しrails db:migrateしてしまった
3.マイグレーションファイルの中に入れた外部参照キーが参照するテーブルがない状態でrails db:creatしてしまった(マイグレーションファイルの記述に誤りがある)

3.エラーの解決方法

1.単純にrails db:createのあとrails db:migrateし忘れている

マイグレーション実行の下記のコマンドを打ち込みましょう

$ rails db:migrate

2.すでにmigrationが完了している(up)状態でマイグレーションファイルを更新しrails db:migrateしてしまった

方法1 既存のテーブルを全削除し、再度マイグレーションの状態をupにする下記のコマンドを打ち込みましょう

$ rails db:migrate:reset

方法2 マイグレーションの最新のものだけ実行を取りやめて編集可能(down)にするコマンドrails db:rollbackののち、マイグレーションファイルの更新コマンドrails db:migrateを打ちましょう

$ rails db:rollback
$ rails db:migrate

※方法1と方法2の違いは処理速度(工程)の量の違いです。テーブルの数が多い場合は方法2としましょう。テーブルが数十程度なら方法1でも2でも大差はありません

※マイグレーションの状態を調べるコマンドは下記の通りです

$ rails db:migrate:status

3.マイグレーションファイルの中に入れた外部参照キーが参照するテーブルがない状態でrails db:creatしてしまった

①-1外部参照されるマイグレーションファイルを作成し、app/modelの中のファイルにアソシエーションを追記(または修正)する
①-2マイグレーションファイルから不必要な外部参照キーを消す

②上記①のどちらかの修正のあとに下記コマンドを打ち込みましょう

$ rails db:migrate:reset

4.すでにデータベースにレコードを入れてしまい、それを残したまま修正する方法(補足)

新たに追加したい項目のテーブルを作成し、本来追加したかったテーブルのidを外部キーとする方法をとりましょう。

参考(railsガイド): https://railsguides.jp/active_record_migrations.html#%E6%97%A2%E5%AD%98%E3%81%AE%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RubyのMysql2の各種情報(バージョン情報やオプション)を取得する方法

背景

Ruby on Rails 等で MySQLを利用している時、低レイヤーの根深い問題や、よくわからないエラーに遭遇したことはありますでしょうか?

RubyでMySQLを利用する際によく使われるライブラリである Mysql2 では、C拡張を利用して libmysqlclient とリンクし、データベースサーバ上の mysqld に対してアクセスしている都合上、複数の概念に対してのバージョン情報やオプションを扱う必要があります。

これらのバージョン情報は、問題の原因究明に重要であるため、取得方法を知っておくことで、効率的な作業が可能になります。自分が調べる時は勿論、他の人に手伝ってもらう時にも重要なヒントになります。

TL; DR

以下のようなデータを記録しておくと、調査が捗ります。

# Railsの場合の Mysql2::Client のオブジェクトの取得方法の例
client = ActiveRecord::Base.connection.raw_connection
# 自前の場合の Mysql2::Client のオブジェクトの作成方法の例
client = Mysql2::Client.new(host: 'localhost', username: 'root')

puts JSON.pretty_generate(
  'RUBY_DESCRIPTION' =>  RUBY_DESCRIPTION,            # Rubyの詳細
  'Mysql2::VERSION' =>  Mysql2::VERSION,              # gemのバージョン
  'Mysql2::Client#info' => client.info,               # libmysqlclient のバージョン
  'Mysql2::Client#server_info' => client.server_info, # 接続先の MySQL Server のバージョン
)

# 接続に関するオプション (おまけ)
puts JSON.pretty_generate(client.query_options.reject { |k, _v| k == :password })  # 他にも重要な情報はあるが、最低限、パスワードは取り除く

出力例

{
  "RUBY_DESCRIPTION": "ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]",
  "Mysql2::VERSION": "0.4.10",
  "Mysql2::Client#info": {
    "id": 80016,
    "version": "8.0.16",
    "header_version": "8.0.16"
  },
  "Mysql2::Client#server_info": {
    "id": 50637,
    "version": "5.6.37"
  }
}

各種バージョン

Rubyの詳細情報

ruby -v の詳細が記述されます。

https://docs.ruby-lang.org/ja/latest/method/Object/c/RUBY_DESCRIPTION.html

gem のバージョン

gem のバージョンは、以下の Mysql2 自体のバージョン Mysql2::VERSION になります。

https://github.com/brianmario/mysql2

コードは以下のような形になっています。

https://github.com/brianmario/mysql2/blob/982dbdb1d521c668547cfd4ab927558c689b8bfd/lib/mysql2/version.rb#L2

module Mysql2
  VERSION = "0.5.3".freeze
end

libmysqlclient のバージョン

Mysql2 は、独自に MySQLサーバ との通信プロトコルを実装しているわけではなく、 MySQLの提供している、Cで書かれたクライアントライブラリを利用してアクセスしています。
この時のライブラリのバージョンによって、微妙な挙動の差異が発生する場合があるので、この情報も重要です。

Mysql2 では、以下のようにC拡張で Mysql2::Client#info が実装されています。

https://github.com/brianmario/mysql2/blob/f8560c551bf1999baf7df43290e8f89471e77af4/ext/mysql2/client.c#L948-L968

  version = rb_str_new2(mysql_get_client_info());

これが呼び出している mysql_get_client_info() が何を返すかは、MySQLのドキュメントに記述されています。

https://dev.mysql.com/doc/refman/5.6/en/mysql-get-client-info.html

Returns a string that represents the MySQL client library version (for example, "5.6.48").

ということで、クライアントライブラリ libmysqlclient のバージョンが返却されます。

接続先の MySQL Server のバージョン

問題が発生した時は、クライアントバージョンは勿論ですが、相手側のサーババージョンも気になるところです。

Mysql2 では、以下のようにC拡張で Mysql2::Client#server_info が実装されています。

https://github.com/brianmario/mysql2/blob/f8560c551bf1999baf7df43290e8f89471e77af4/ext/mysql2/client.c#L970-L994

  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));

これが呼び出している mysql_get_server_info() が何を返すかは、MySQLのドキュメントに記述されています。

https://dev.mysql.com/doc/refman/5.6/en/mysql-get-server-info.html

Returns a string that represents the MySQL server version (for example, "5.6.48").

ということで、接続先の MySQL サーバのバージョンが返却されます。

接続に関するオプション

MySQLのサーバ側で設定されているオプションも勿論大切ですが、各クライアント(コネクション)毎に維持されているオプションも、細かい挙動を知る上では重要になってきます。

Mysql2 では、以下のようにインスタンス変数へのアクセッサーが生やされて、 Mysql2::Client#query_options があるので、それを利用する形が良いと思われます。

※ このアクセッサーにはパスワードを含む情報が格納されているので、情報共有の際には、それをしっかり取り除くことを留意しておいてください。

https://github.com/brianmario/mysql2/blob/98304cbbc0c9a964c833474a06f55c433ec26432/lib/mysql2/client.rb#L3

参考

https://github.com/brianmario/mysql2
https://dev.mysql.com/doc/refman/5.6/en/c-api-server-client-versions.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む