がるの健忘録

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

アクアリウム始めました(笑

当初は「ボトルアクアリウム」の予定だったんだけどなぁ……なおいちゃんの、主にメモ書き(笑

1/21に、アクアリウムの先輩(笑)から「ウィローモスならちょうど増えすぎて刈らないとなんで、刈ったやつ送りましょうか?」との申し出があり、ほいほいと好意に甘える(笑

一発目のお買い物は1/22。
本当は、アスウォット株式会社さん( http://aswot.com/baghis/ )の「パンドルチェ」の空き瓶でボトルアクアリウムを作ろうか、と思ったのですが。
空きスペースはかって(最大で20cm*30cm)、お買い物に行って思案している内に方針変更がありまして。

・コトブキ レグラスR-200(200×200×200mmサイズ、水容量7L)
・テトラ オート ワンタッチフィルター AT-20
・コトブキ フラットLED SS(20~32㎝水槽、全光束190lm)
・プリセットオートAR30(25.5~27.0℃ ±1.5℃:4W)
・アクシータイマー(定格入出力容量:1500Wまで)
・水作 プロホース エクストラ Sサイズ

あたりを購入。
底は「サンディブライト」の、粒の粗めなほうを購入。
あとは「スドー 水草のソフトおもり 巻き型」「流木」を、水草用に購入。カルキ抜きは「水替えプラス」、あと一応「BICOM スターターキット」を購入。

一式、アクアフォレスト新宿店さん( http://aquaforest.tokyo/ )にお世話になりました(ってかそれ以降の買い物も大分とお世話になってます)。

で、送って貰える水草
南米ウィローモス
オレゴンリバーモス
マツモ
アマゾンフロッグピット
との事。色々調べて見て、ワクテカを募らせつつ、まずは水を回し始める。スターターキットも投入……ちょっと早まったかなぁ、とか思いつつ。


翌日
1/23 に、色々調べて、生体濾過系のブツが足りないなぁ、と思って
マツダ ストレーナースポンジ ミニ 細目
・パワーハウス (AAJ9Q) カスタムイン 50 ソフトタイプ
を追加で購入。あと、アンモニアがないなぁ、と思ったので、アクアフォレストさんが「ソイル少量」ってのを売ってたのでそれも購入……そういえばどの種類なんだろう? 今度聞いてみよう。

で、水草が届いたので早速レイアウト。
レイアウトには
・絹糸(木綿糸ってあったけど100均にあったのが木綿糸だった)
・入れて貰ったビニタイ
・買ったソフトおもり
を使って。ソイルは、砂利の上にうっすらと蒔きました。ソイルがくずれたらプロホースで吸い上げればいいかなぁ、とか思ってますが、ど~なんでしょう??

あとは
・小型のあみ
・でっかいスポイト(水合わせ用)
をアクアフォレストさんで購入。

100均で
・プラスチックのパッド(作業用)
・二リットルの麦茶とか作る容器(水替え用)
・500mlの計量カップ(水を注ぐとき用)
・鏡(側面において、光を反射させたら具合がよいかなぁ、と)
を購入。
ピンセット代わりは菜箸。いや便利便利。これでいいかなぁ、って思うくらいに(笑

水槽がちょいと白濁してる……けど、水草だけだしあんまり気にしない。


その翌日
1/24 に、「もう一つくらい水草が欲しいかなぁ」と思って「簡単そうで」「根っこをはってくれる子(底面の環境がよくなると聞いた)」ってことで
・ハイグロフィラ ポリスペルマ
を選んでいただいて購入。
すげぇ刺しやすい(笑
あと、水質チェックした方がよいよ、と言われて、「テトラ 6in1」も購入。
チェックするまでもなく白濁が消えた……思ったより早いなぁ。こーゆーものらしい。


1/25
パイロットフィッシュの投入時期に悩みつつ、一瞬「………アンモニア水買うか?」とか悩んだ経由、「パイロットフィッシュに与える餌を少量入れる」に落ち着いたので。
アカヒレ飼うつもりなので、アクアフォレストさんでアカヒレに与えてるって言われて
・お魚ぱくぱく
を購入。
15粒くらい投入(明日からもうちょっと控え目予定)、ふと「今の水を一回はかろう」と思ってはかってみると「亜硝酸 1mg」「硝酸 25~50mgの間」くらい………あれ? アンモニアの数値を図りたい所ではあるんだけど、なんか思ったより落ち着いてる???

とはいえまぁそこは「こってこてが好きなおいちゃん」なので。
色々調べて悩んだ挙げ句(結構色々、ろくでもないことを考えた経由)。とりあえず「バイオビーズ」なるものが比較的よろしそうなので、購入予定。さっき、チャームさん( https://www.shopping-charm.jp )でぽちった。
ついでに
水草メンテ用はさみ
・(安かったから)ピンセット
・具合の良さそうなスポイト(GEX ジェックス メダカ元気 スポイト)
を購入。
……なんか調べると、値段も色々あったり、モノによってはヨドバシさんで扱ってたりもするから、そろそろ「落ち着いて調べてから購入」とかしてもよいかも、とか思ってみたり。

 

とりあえず今後の予定としては
・1月末か2月頭に、パイロットフィッシュとしてアカヒレを一匹、召喚
・2月半ばに、アカヒレをもう一匹、召喚
・3月に入ったら、本命のミナミヌマエビを、5~10匹、召喚
する予定でございます。

いにしえ(かもしれない)技法:bit演算変

bit演算を使ったプログラミング、割と「知られてないのでは?」という感じがあったので。
ここでは少し抽象的な部分が混ざってしまうのだけど、まぁなんかの一助になるかなぁ、くらいの感じで、めも。

大まかには
・処理が「A」「B」「A+B」みたいな感じの時の処理の共通化
・いわゆるフラグ的に on/off 出来るステータスがあるようなもの
の時に便利だったりします。

前提

まず、2進数はよいよね?
2進数は、各桁が「bit」って言い方をします。
んで、1だと「立ってる」、0だと「寝てる/立ってない/落ちてる」とかって言い方をする事が、まぁ。
1の「立ってる」は割と共通な気がするなぁ。0はあちこち方言がありそうだけど。

んで。論理演算のANDは「あるbitが、立っているか寝ているか」を判定するのに割と便利につかえます。

例。
変数 $x*1の1桁目(2^0)が立っているか寝ているかを判定する場合は

if (0 !== ($x & 0b0001)) {
    // 立ってる
} else {
    // 寝てる
}

って判定が出来ます。まぁもうちょっと省略して

if ($x & 0b0001) {
    // 立ってる
} else {
    // 寝てる
}

って書く事が多いように思いますが。

同じように、2桁目(2^1)が立っているかどうかは

if ($x & 0b0010) {

3桁目(2^2)なら

if ($x & 0b0100) {

で判定が出来ます。
とりあえずわかりやすく二進数表記で書いてますが、実際には「1、2、4」といった、十進数で書いている事もちょいちょい。

これ自体は、2進数とbit演算を知っていれば「知識としては知っている」内容だと思うのですが、これを上手く使うと、プログラムが楽に出来ます。
いやどちらかというと「いにしえから伝わる技法」だと思うのですが、割と昨今でも普通に使う事が多々あるので。

ちなみに、こーゆーのを「マスクビット」とか「ビットマスク値」とかいう言い方をする事があるので、知っておくと、ググる時に楽……かも。

処理が「A」「B」「A+B」みたいな感じの時の処理の共通化

まぁ今回の元ネタがこっちなので、先にこっちで(笑
入ってくるパラメタによって「Aのみ」「Bのみ」「AとBの処理」をやる場合。
一番ざっくりと書くと

if (パラメタ==A) {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
} else if (パラメタ==B) {
    パラメタBの処理;
    パラメタBの処理;
} else if (パラメタ==A+B) {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
    パラメタBの処理;
    パラメタBの処理;
}

となるか、と思います。

で、少し共通化すると

function func_A() {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
}
function func_B() {
    パラメタBの処理;
    パラメタBの処理;
}

//
if (パラメタ==A) {
    func_A();
} else if (パラメタ==B) {
    func_B();
} else if (パラメタ==A+B) {
    func_A();
    func_B();
}

ってなるか、と思うのですが。

これを、bit演算を使うと

$type = 0;
if (パラメタ==A) {
    $type = 1; // 0b0001
} else if (パラメタ==B) {
    $type = 2; // 0b0010
} else if (パラメタ==A+B) {
    $type = 3; // 0b0011
}
//
if ($type & 1) { // 0b0001
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
}
if ($type & 2) { // 0b0010
    パラメタBの処理;
    パラメタBの処理;
}

って書く事ができます。

いやまぁ関数(メソッド)に切り出してもよいんですが。
「微妙に切り出しにくい(面倒な)処理」とかで、かつ「ここでしか使わない」ようなロジックの場合、こんな風に書くと
・重複した記述がいらない
・(慣れてれば)見やすい
ので、すっきり書けますよ、的な感じになります。

いわゆるフラグ的に on/off 出来るステータスがあるようなもの

この辺は、PHPの関数でも割とおなじみなように思うのですが。
ふと思い出しやすい所で、 file_put_contents( https://www.php.net/manual/ja/function.file-put-contents.php ) を例に。

第三引数のflagsが、まさにこのやり方をやっています。

<?php

var_dump(FILE_USE_INCLUDE_PATH);
var_dump(FILE_APPEND);
var_dump(LOCK_EX);

int(1)
int(8)
int(2)

4がいませんが、まぁ2進数にすると
0b0001
0b1000
0b0010
で、ちょうどそれぞれ「1つだけbitが立っている」値になっています。

なので、例えば「FILE_APPENDかつLOCK_EXにしたい」場合は、論理和(or)を使って「 FILE_APPEND | LOCK_EX 」とやると、$flgには10(0b1010)が渡るので。
あとは、プログラムで

if ($flg & 0b1000) {
    // FILE_APPEND用の処理
}
if ($flg & 0b0010) {
    // LOCK_EX用の処理
}

って書き方をするから便利なんですね。

……と、そういえば error_reporting( https://www.php.net/manual/ja/function.error-reporting.php )もこれをがっつり使ってましたねぇ。
こっちは https://www.php.net/manual/ja/errorfunc.constants.php で(とりあえず現在のバージョンの時の)「各定数の数値」まで書いてあるので、見てみるとよいか、と。
値を2進数にしてみるとわかるのですが、いずれも「1箇所だけ1、残りは0になってる2進数」になっているかと思います。
だから、error_reportingの値は、主にor(|)をつかって「複数指定」が出来るんですね*2

この辺、多分今でも(そして多分、今後も)なにげに出てくると思われるので、知っていると便利かなぁ、と。

*1:一応、PHPerだからねぇおいちゃんw

*2: 「bit反転(~)+and(&)による一部bit(主にNotice)寝かせ」は、邪悪な行為なのでおいちゃんのコズムではリアリティにより否定されます

PHP初心者本 覚え書き

ちょっとまぁやりたい事、があって。
まずは「PHPの初心者本」のうち、現在入手可能と思われるやつ(で、版を重ねてるものはそれの最新)を、ざっくりと列挙。

PHPの絵本 第2版 https://www.amazon.co.jp/dp/4798151645
よくわかるPHPの教科書 https://www.amazon.co.jp/dp/4839964688
Webサイト制作者のための PHP入門講座 https://www.amazon.co.jp/dp/4774145254
いきなりはじめるPHP https://www.amazon.co.jp/dp/4897978858
気づけばプロ並みPHP 改訂版 https://www.amazon.co.jp/dp/4865940650
最初のキホンがやさしくわかる 絵で学ぶPHP入門 https://www.amazon.co.jp/dp/4048868691
改訂新版 ゼロからわかるPHP超入門 https://www.amazon.co.jp/dp/4774178918
詳細! PHP 7+MySQL 入門ノート https://www.amazon.co.jp/dp/4800711304
スラスラわかるPHP https://www.amazon.co.jp/dp/4798125806
独習PHP 第3版 https://www.amazon.co.jp/dp/479813547X
PHP7+MariaDBMySQLマスターブック https://www.amazon.co.jp/dp/4839962340
いちばんやさしい PHP の教本 第2版 https://www.amazon.co.jp/dp/4295001244
スラスラ読める PHPふりがなプログラミング https://www.amazon.co.jp/dp/4295007676
誰もがあきらめずにすむPHP超入門 https://www.amazon.co.jp/dp/4797398973
確かな力が身につくPHP「超」入門 https://www.amazon.co.jp/dp/4797388722
30時間アカデミックPHP入門 https://www.amazon.co.jp/dp/4407340355
イラストでよくわかるPHP はじめてのWebプログラミング入門 https://www.amazon.co.jp/dp/4844332953
PHPしっかり入門教室 使える力が身につく、仕組みからわかる。 https://www.amazon.co.jp/dp/4798153370
最初に「読む」PHP https://www.amazon.co.jp/dp/4883379019
PHPサーバーサイドプログラミングパーフェクトマスター https://www.amazon.co.jp/dp/4798044865
みるみるPHPがわかる本 https://www.amazon.co.jp/dp/4798026026
PHPプログラミングの教科書 https://www.amazon.co.jp/dp/B00GJGOF4G

初めてのPHP https://www.amazon.co.jp/dp/4873117933

以下覚え書き
・とりあえず「物理でも電子でもいいけど、購入可能なもの」除く古本
・「プログラム自体の初心者」を仮想ターゲットとする、ので、「変数は?」「環境構築」とかその辺が必須
・HTMLは理解している前提
・「PHP5 なのか 7なのか」は、初心者本的にはさほど重要事項ではない気がする(不要ではないけど、後回しでも大体足りると思われ)
・関数までは必須。classは、欲しいけど、ページ数的に「十全にやる」のはまぁ難しいと思われるので一端気にしない
・セキュリティ的にアウトなのは過敏に反応したい
・記述の嘘、誤解を招きやすいもの、コードサンプルの不備にも過敏に反応したい
・あとは、先に「評価軸」を作成してから、一気にチェック、かなぁ

購入は年内。
実施は年明け……には頑張れるといいなぁ。

0000-00-00 と PHP:実装側の考察

さて。
とりあえず「0000-00-00を食い止める」を至上命題にする場合、必要なのが
・空文字でINSERTしない
ここ。
これを主眼にして、少し実装側の考察をしてみませう。

一つ、ガードとして
sql_modeに STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_DATE を入れて置く
という手があるのですが。データは入らないのですが「SQLでエラーになる」ので、やり方を間違えると若干困るので、もうちょっと頑張ってみましょう。

https://gallu.hatenadiary.jp/entry/2019/11/04/160833 でも書きましたが、Laravel には ConvertEmptyStringsToNull というMiddleware があります。
今回の件で考えると一件便利そう、ではあるのですが、LaravelのConvertEmptyStringsToNullは「全項目無差別」なので。

んと……端的には
・文字列のカラムがあって
・空文字でもいい(入力必須ではない)
ようなケースで、ConvertEmptyStringsToNullがonになっていると
・未入力
・空文字……が、ConvertEmptyStringsToNullによってNULLに変換される
・NULLをinsertしようとしてエラー(NOT NULL制約って、普通、入ってるよね?)
って感じになるので、無差別はちょっと嬉しくないの感じでございます。

となると、とりあえずやりたいのが
・「カラムが日付系の型のデータだけ」空文字ならNULLに変換したい
ってのが、とりあえず今回としては「よい落としどころ」になるのではないかなぁ、と思っています。

後は、カラムによっては「文字型であろうが"NULL許可で、空文字が入るくらいならNULLがいい"」ってケースが、おいちゃんは全く思いつかないし思い浮かばないのですが、もしかするとそういうケースが「ないとも限らない」のであろうなぁ、と、思われるので(可能性としては)。

大雑把には
・「対象になるカラム名の配列」を用意して
・そのカラムについては「空文字ならNULLに置換する」
ような処理を、良い感じの箇所に挟み込んでおくと、比較的「案配がよい」のではないかなぁ、と思われます。

「とりあえず」SlimLittleToolsでの実装予定

んで。最近、Slimが大変にお好みなので、つい SlimLittleTools / なんてぇものを作って、使っておりますが。
そこでは
・filterの追加: ConvertEmptyStringsToNull と同じようなもの、を、カラム単位で出来るように
・MackModelDetail の修正( make_model_detail.php の実態 ):「日付型のカラムの配列」を作成できるようにする
・Modelの修正: 「日付型のカラムの配列」については「空文字ならNULL」に変換するようにする:その変換が on/off 出来るようなスイッチ用のメソッドを追加(デフォルトは、一応 false(変換しない)にしておく)
なんて実装を考えてます。

あと、 https://gallu.hatenadiary.jp/entry/2019/11/04/160833 で書いた
> NULLをVALUESに与えると、NULLになる(DEFAULTの値にはならない)
この部分が微妙に気になって引っかかっているので。加えて
・Modelの修正: insertとupdateのタイミングで「値がNULLなら、keyごと削除」の処理を追加:その削除が on/off 出来るようなスイッチ用のメソッドを追加(デフォルトは、一応 true(削除する)にしておく)
を入れておこうかなぁ、と思ってます。

この辺はまだ、少し考察や検討の余地などもありそうなので。
ご意見などいただけると、反映できる、かもしれないので、よかったら是非。

0000-00-00 と PHP

MySQLの、(少なくとも一部では)悪名高き 0000-00-00 について、は、そこそこブログがあるのですが。
「それを、PHP(plain)とか(PHPの)フレームワークとかでどうやってるんだろ?」というのが、ざっくりググった限りだと案外と無かったので。
調査かてがて、備忘録として。

0000-00-00 について

まずは 0000-00-00 について。
いやまぁググればあちこちで出てくる「MySQLに固有の、奇異なデータ」なのですが。
端的には
・入れ方によって、日付型(DATE、DATETIME、TIMESTAMP)に入る事があるデータ
MySQL以外で 0000-00-00 が入るRDBはない(はず)
・NOT NULL制約が入っているカラムで「0000-00-00」があると、WHERE句の IS NULL、IS NOT NULLの双方で 0000-00-00が引っかかる
てなもの、になります。

http://nippondanji.blogspot.com/2018/05/mysqlzero-date.html
https://soudai.hatenablog.com/entry/2018/05/12/191050
http://sakaik.hateblo.jp/entry/20151227/mysql_date_null

など、言及されている所は少なからずあるか、と思います。
詳しくは、上述のBlogなど見て頂くほうが早いかなぁ、と思います。

少し細かくまとめると。

・カラムにNOT NULL制約がある時
 → 空文字('')をVALUESに与えると、 0000-00-00 になる: modeにSTRICT_TRANS_TABLESがある時はエラーが出る(Incorrect date value: '' for column)
 → NULLをVALUESに与えると、エラーになる(当たり前だ)
・カラムにNOT NULL制約がない時
 → 空文字('')をVALUESに与えると、 0000-00-00 になる: modeにSTRICT_TRANS_TABLESがある時はエラーが出る(Incorrect date value: '' for column)
 → NULLをVALUESに与えると、NULLになる(当たり前だ)

となります。
また
・カラムにNOT NULL制約があり、かつ、DEFAULTが定義されている時
 → 空文字('')をVALUESに与えると、 0000-00-00 になる: modeにSTRICT_TRANS_TABLESがある時はエラーが出る(Incorrect date value: '' for column)
 → NULLをVALUESに与えると、エラーになる(当たり前だ)
 → INSERTの対象カラムからそもそも削除すると、DEFAULTで定義された値になる(当たり前だ)
・カラムにNOT NULL制約がなく、かつ、DEFAULTが定義されている時
 → 空文字('')をVALUESに与えると、 0000-00-00 になる: modeにSTRICT_TRANS_TABLESがある時はエラーが出る(Incorrect date value: '' for column)
 → NULLをVALUESに与えると、NULLになる(DEFAULTの値にはならない)
 → INSERTの対象カラムからそもそも削除すると、DEFAULTで定義された値になる(当たり前だ)

となります。
ここ、後述でちょっと重要になるので、ポイントとして。

フレームワーク無し」で実装すると?

まぁ昨今だととかですかねぇ。古来だとか。
日付を入力してもらう時に、まぁ最終的には文字列で受け取るわけ、なのですが。
これが「必須入力」なら「日付のフォーマットに沿ってるかどうかを判断」とかでいけばよくて……個人的にはstrtotime()使うのが、楽で好みかなぁ。

$t = strtotime($_POST['日付'] ?? '');
if (false === $t) {
    // エラー処理
    return;
}
// else
$date = date('Y-m-d H:i:s', $t);

とかやるとざっくりとフォーマットが整うので、よく使います。
DateTimeとかDateTimeImmutableとかのクラスを使う場合(このチョイスならDateTimeImmutableのほうが好み)、エラー時は「例外を投げてくる」ので、書き方にちょっとだけ注意を払いましょう。

ただ、必須じゃない場合「空文字を許容」する必要がある、と思われるのですが。

$date = $_POST['日付'] ?? '';
if ('' !== $date) {
    $t = strtotime();
    if (false === $t) {
        // エラー処理
        return;
    }
    // else
    $date = date('Y-m-d H:i:s', $t);
}

上述で「パラメタの値を取得して」「SQL作成してINSERTとかUPDATEとか」やると、結局日付系のカラムで「VALUEが空文字なINSERT/UPDATE」が走るので、0000-00-00問題が、回避できないような気がするのですだよ……。

なお

$date = $_POST['日付'] ?? null;

ってやっても変わらない。だって「formには"日付"のnameアトリビュートが存在しているから、空文字が返ってくる事が十分に想起される」から。

PHPフレームワークはどうなってるんだろ?

と言うわけで様々なフレームワークを……試す根性がありませんでしたすみません orz
ので、現状多分「一番メジャーであろう」Laravelで実験。
とりあえずバージョンは…… 6.4.1 。あら。5.8系かと思ったんだけど、バージョン指定しないとこんな先のバージョンになるのか。
まぁ「新しいほうがより機能は洗練されてる」だろうから、あんまり気にしない(雑

すげぇ単純に
・テーブル作って
・form入りのテンプレート作って
・ざっくりとinsert
こんな感じ。

マニュアルのPageにも
https://readouble.com/laravel/5.8/ja/eloquent.html

        $flight = new Flight;
        $flight->name = $request->name;
        $flight->save();

ってあるので、おんなじようにやってみる……エラー。
どうもConvertEmptyStringsToNullというのがお邪魔しくさりやがってくださっているらしく
・ConvertEmptyStringsToNull Middleware によって「空文字ならnullに置換」される
・対象カラムがNOT NULL制約
・なので「NOT NULL制約の所にNULLぶち込んだから嫌がられる」
という単純構造。

一端 app/Http/Kernel.php に修正を入れて、ConvertEmptyStringsToNull をoff(ついでにTrimStringsもoffしたほうがいいんじゃねぇか疑惑があるんだが一端放置)。
「未入力ならそのまま空文字になる」ように修正して再度実行。

……予想通り 0000-00-00 が入りやがりました orz
この辺、Laravelでも特に「なんか対応」はしていないんだなぁ、と、実感。

今までよく引っかかってこなかったなぁ……と思うんだけど、まぁぶち合ったんだからどうにか考えませんとなぁ。

https://gallu.hatenadiary.jp/entry/2019/11/04/160910 で、実装について少し考察をしてみます。

おまけ:実験結果

mysql> show create table date_tests;
+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| date_tests | CREATE TABLE `date_tests` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`d_test` date NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci |
+------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select id, d_test from date_tests;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
+----+------------+
2 rows in set (0.01 sec)

mysql> insert into date_tests(d_test) value('');
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> insert into date_tests(d_test) value(null);
ERROR 1048 (23000): Column 'd_test' cannot be null
mysql> select id, d_test from date_tests;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
+----+------------+
3 rows in set (0.00 sec)

mysql> ALTER TABLE date_tests MODIFY COLUMN `d_test` date ;
Query OK, 0 rows affected (0.15 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> insert into date_tests(d_test) value('');
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> insert into date_tests(d_test) value(null);
Query OK, 1 row affected (0.01 sec)

mysql> select id, d_test from date_tests;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 5 | NULL |
+----+------------+
5 rows in set (0.00 sec)

mysql> SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into date_tests(d_test) value('');
ERROR 1292 (22007): Incorrect date value: '' for column 'd_test' at row 1
mysql> insert into date_tests(d_test) value(null);
Query OK, 1 row affected (0.00 sec)

mysql> select id, d_test from date_tests;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 5 | NULL |
| 6 | 0000-00-00 |
| 7 | NULL |
+----+------------+
7 rows in set (0.00 sec)



mysql> select id, d_test from date_tests where d_test is null;
+----+--------+
| id | d_test |
+----+--------+
| 5 | NULL |
| 7 | NULL |
+----+--------+
2 rows in set (0.00 sec)

mysql> select id, d_test from date_tests where d_test is not null;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
+----+------------+
5 rows in set (0.00 sec)

mysql> ALTER TABLE date_tests MODIFY COLUMN `d_test` date NOT NULL;
Query OK, 5 rows affected (0.09 sec)
Records: 5 Duplicates: 0 Warnings: 0

mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
+----+------------+
5 rows in set (0.00 sec)

mysql> select id, d_test from date_tests where d_test is null;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
+----+------------+
4 rows in set (0.00 sec)

mysql> select id, d_test from date_tests where d_test is not null;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
+----+------------+
5 rows in set (0.00 sec)

mysql> ALTER TABLE date_tests MODIFY COLUMN `d_test` date DEFAULT '1970-1-1';
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> insert into date_tests(d_test) value('');
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> insert into date_tests(d_test) value(null);
Query OK, 1 row affected (0.00 sec)

mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
| 8 | 0000-00-00 |
| 9 | NULL |
+----+------------+
7 rows in set (0.00 sec)

mysql> ALTER TABLE date_tests MODIFY COLUMN `d_test` date NOT NULL DEFAULT '1970-1-1';
Query OK, 7 rows affected, 1 warning (0.02 sec)
Records: 7 Duplicates: 0 Warnings: 1

mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
| 8 | 0000-00-00 |
| 9 | 0000-00-00 |
+----+------------+
7 rows in set (0.01 sec)

mysql> insert into date_tests(d_test) value('');
Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> insert into date_tests(d_test) value(null);
ERROR 1048 (23000): Column 'd_test' cannot be null
mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
| 8 | 0000-00-00 |
| 9 | 0000-00-00 |
| 10 | 0000-00-00 |
+----+------------+
8 rows in set (0.00 sec)

mysql> insert into date_tests(id) value(null);
Query OK, 1 row affected (0.01 sec)

mysql> select id, d_test from date_tests ;
+----+------------+
| id | d_test |
+----+------------+
| 1 | 0000-00-00 |
| 2 | 2019-11-01 |
| 3 | 0000-00-00 |
| 4 | 0000-00-00 |
| 6 | 0000-00-00 |
| 8 | 0000-00-00 |
| 9 | 0000-00-00 |
| 10 | 0000-00-00 |
| 11 | 1970-01-01 |
+----+------------+
9 rows in set (0.01 sec)

インフラの(雑)管理方法

https://twitter.com/ndxbn/status/1189735988907536384

マシンの /usr/local 以下に make && make install で直接インストールしてあるミドルウェアのバージョン更新って、どうしてます?
普通に新しいバージョンの tar ball を持ってきて、 make && make install で上書きしておわり?

https://twitter.com/ndxbn/status/1189745023736090625

ミドルウェアの更新するハメになったのだけれど、そういえばどうしてるんだろうって気になったので聞いたーー
使わなくなった lib とか includes が残留していくけど、キニシナイ感じなのねー?
更新してダメだったとき用で直ぐにバージョン戻せる必要とかもあるから /opt とか使ってくれーなどと思う

という質問を貰ったので。
ツイッターで書くには微妙に長くなりそう」なのと、ちょうどこの辺はどこかで書いておきたいなぁ、と思っていたので、おいちゃん流の雑管理のやり方を。

大雑把には
・バージョンをさほど気にしていなくてコンパイルオプションも気にしていないものは、パッケージ管理(yum)でインストール
・バージョンを気にしたりコンパイルオプションを気にしたりするものは、ソースコードでインストール
てな感じかな。

もうちょっと台数が増えたりしたら、Fabricつかったり(AWSなら)AMIつかったりBlue-Green Deploymentやったり、とか色々と妄想はしているのですが(笑
現状、そこまでの台数でもないので、とりあえずは手動でえっちらおっちらと。

昔は割と「がっちがち」だったのですが、時間とともに段々「緩く」なってきました(笑
っていう流れとともに。

昔々は「全部、ソースコードinstall」でやってました。
教えてくれた人の流儀なんだと思うんですが、 /usr/local/srcではなくて、/usr/srcにコードを置く事が多かったですねぇ。

install先ですが。
単純なライブラリとかはそのまま make installだったのですが、例えばapacheとかは
・ベースは /opt
apacheなら、例えば /opt/apache/apache-2.4.41 などの「バージョンが入っているディレクトリ」にinstall
シンボリックリンクで、最新のディレクトリを /opt/apache/apache_current
に入れてます(ました)。

これの最大の利点は、まぁ上述にもあるとおり「バージョンがすぐに戻せる」事。
apache止めて
シンボリックリンクを「一つ前の古いディレクトリ」に指し直して
apache動かす
で戻せるので「楽」だってんで、昔、教えてくれた人から教わったので、割とそれをずっと使ってました。

古いバージョンは……一応建前としては「使わなくなったものは消す」のですが、ディスクが圧迫していなければ、あんまり消さなかったなぁ。
10世代超えると邪魔くさいんで、少し消してましたが。

そこから少し時は流れて。

いやまぁ例えばぶっちゃけgccとかlibtoolとかcmakeとか「そこまでバージョンをタイトに追ってない」かつ「コンパイルオプションを基本あんまり使わない」もの、ってぇのがありまして。
この辺はまぁ、yumなりrpmなりdnfなりapt-getなりaptitudeなりを使って「楽をしてもいいんじゃないかなぁ」ってのが、おいちゃんの最近の見解。
なので「どこまでをパッケージ管理で入れるか」には多少の議論はあろうかなぁ、と思うのですが、「どこか、までは」パッケージ管理を使って、ある程度楽をしています(笑

で、例えば典型的にはPHPあたりは、おいちゃんは「ソースコードでinstall」しています。
PHPの場合は
・最新のバージョンを追いたい
コンパイルオプションを割と色々と付け足したり変えたりする事が多い
から。
似たような理由で、apache(+apr-util)、nginxあたりはソースコードinstallが多いです。
MySQLも多かった……のですが、最近、お仕事だとRDSとか使う事が多いので、installする事が減ったなぁ。まぁ「なんかで使う」なら、ソースコードで入れますが。

この辺は
・/usr/local/src なり /usr/src なりに、tar ball持ってきて
・configure / make / sudo make install する
ってやりかた(MySQLはcmakeになったよねぇ、とかいう細かい話しはおいといて)。

configureのオプションを残しておきたいので、おいちゃんは
php7_ccc
apche_ccc
nginx_ccc
mysql_ccc
みたいな形で、ファイルで残しておくようにします。そうするとlostしない(笑

で、万が一戻す時は
・戻したいバージョンのディレクトリで sudo make install
する感じかな。単純。

configとかは、メンテナンスバージョンくらいでは大してかわらないので。とはいえまぁこれも教えられた流儀があるので
・cp -p で「日付付き」でバックアップファイルを作っておく
・編集したらdiffコマンドで「意図しない箇所を変更していないか」を確認
ってやるので。もし「configを変えた」ら、それも戻せばOK。

……でまぁ、こんなやり方なので、最近「インストールディレクトリもバージョン番号付き」にする、のは、やらなくなっちゃいましたねぇ。

んで。
> 使わなくなった lib とか includes が残留していくけど、キニシナイ感じなのねー?
このやり方をすると、includesは「/usr/local/src 側」に残るはずなので、あんまり気にしない。
もし「別のプロダクトのincludeを参照する」ような場合は、例えば
ln -s /usr/local/php-7.2.24 /usr/local/php-7.current
とかってやって「シンボリックリンクを -I オプションに指定する」とかやると、普通に楽。

libについては「そこまで散らからないだろう」と思っているので、「キニシナイ」感じかなぁ。
昔ならともかく、今「サーバのファイルの最後の1つまで全部把握」は若干しんどいので(笑

最近は、こんな感じかなぁ。
これでとりあえず「差し障りない」くらいの管理は出来ているので。「もっとすっげぇ楽」とか「差し障る」とか出たら方針を変えそうですが、しばらくはこんな感じでやってます。

一端、メモ

時々、使うし使いたくなる台詞なんだけど「どこにあったっけ?」と「正確な文章を覚えていない」がちょいちょいあるので、めも。

P165

努力と技術で無理を通すのは我らの誇りだが
本当に無理なことは無理なので無理だ