データが存在するテーブルに not null 制約の付いたカラムが追加できる
not null に関しておかしな話を聞いたので調べてみた。
おかしな話の内容
「データが存在するテーブルにnot null制約の付いたカラムが追加できる」
…うそん!!!
前置き
発言者はmysqlの事を言っていると思う。
なぜなら、
発言者はmysqlを使っている製品に「今」関わって(昔は知らない)いて、
私の経験として、「データが存在するテーブルにnot null制約の付いたカラムが追加できる」
なんて無茶はoracle、postgresでは許された事がない。
あ、忘れてた、SQLServerも許してくれなかった。
調査
他の RDBMS の挙動は分かっているので、mysql に対してのみ確認します。
データが存在するテーブルへのnot null制約の付いたカラム追加
テーブルを作成
CREATE TABLE test_null( null_char_column varchar(10), null_int_column int, notnull_char_column varchar(10) NOT NULL, notnull_int_column int NOT NULL );
定義を確認
desc test_null; +---------------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------------+-------------+------+-----+---------+-------+ | null_char_column | varchar(10) | YES | | NULL | | | null_int_column | int(11) | YES | | NULL | | | notnull_char_column | varchar(10) | NO | | NULL | | | notnull_int_column | int(11) | NO | | NULL | | +---------------------+-------------+------+-----+---------+-------+
notnull_char_column、notnull_int_column の2カラム。
not null 指定したから Null のところが NO となっているのは正しく、
Default が NULL なのもOK。
データを INSERT する時に指定してなかったらNULLなのでエラー、というのは
至極全うな動きだ。
データ追加
INSERT INTO test_null (null_char_column, null_int_column, notnull_char_column, notnull_int_column) VALUES('a', 1, 'b', 2);
データを確認
select * from test_null; +------------------+-----------------+---------------------+--------------------+ | null_char_column | null_int_column | notnull_char_column | notnull_int_column | +------------------+-----------------+---------------------+--------------------+ | a | 1 | b | 2 | +------------------+-----------------+---------------------+--------------------+
not null 制約の付いたカラムを追加する。
ALTER TABLE test_null ADD notnull_char_column_add varchar(10) not null; ALTER TABLE test_null ADD notnull_char_int_add int not null;
がーん、、、エラーにならなかった・・・
定義を確認
desc test_null; +-------------------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------------------+-------------+------+-----+---------+-------+ | null_char_column | varchar(10) | YES | | NULL | | | null_int_column | int(11) | YES | | NULL | | | notnull_char_column | varchar(10) | NO | | NULL | | | notnull_int_column | int(11) | NO | | NULL | | | notnull_char_column_add | varchar(10) | NO | | NULL | | | notnull_char_int_add | int(11) | NO | | NULL | | +-------------------------+-------------+------+-----+---------+-------+
CREATE TABLE の時に作成した他の not null 制約付きカラムと同じ定義で追加されてる。
データを確認
select * from test_null; +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ | null_char_column | null_int_column | notnull_char_column | notnull_int_column | notnull_char_column_add | notnull_int_add | +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ | a | 1 | b | 2 | | 0 | +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+
追加した int 型カラムのレコードに 0 が勝手に入ってる・・・。
varchar 型カラムの方は多分''かな?
確認してみる。
select * from test_null where notnull_char_column_add is null; Empty set (0.00 sec) select * from test_null where notnull_char_column_add is not null; +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ | null_char_column | null_int_column | notnull_char_column | notnull_int_column | notnull_char_column_add | notnull_int_add | +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ | a | 1 | b | 2 | | 0 | +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ 1 row in set (0.00 sec) select * from test_null where notnull_char_column_add = ''; +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ | null_char_column | null_int_column | notnull_char_column | notnull_int_column | notnull_char_column_add | notnull_int_add | +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ | a | 1 | b | 2 | | 0 | +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ 1 row in set (0.00 sec)
という事でやっぱり varchar 型の(勝手な)初期化に使われる値は''でした。
INSERT で not null のカラムにデータを指定しないとどうなるのか確認してみる
答えは予想がつきますが、予想外の仕様を↑で見たばかりなので念のため。
データ追加
INSERT INTO test_null (null_char_column, null_int_column) VALUES('c', 3);
普通に通りました。
違和感を感じるけど驚かなくなってきた。
データを確認
select * from test_null; +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ | null_char_column | null_int_column | notnull_char_column | notnull_int_column | notnull_char_column_add | notnull_int_add | +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+ | a | 1 | b | 2 | | 0 | | c | 3 | | 0 | | 0 | +------------------+-----------------+---------------------+--------------------+-------------------------+-----------------+
カラム追加の時と同じですね。
まとめ
not null 制約の付いたカラムを、データが存在するテーブルに対して追加する
mysql ○
※その場合の初期値は int なら 0、char なら ''
oracle ×
カラム追加のDDL文に NOT NULL を付ける時は同時に DEFAULT も付ける。
ex.)
ALTER TABLE tab_name ADD col_name NOT NULL DEFAULT 0;
postgres ×
oracleと違って NOT NULL と DEFAULT を同時に付ける事ができないので、
DEFAULT だけ付けてカラムを追加した後に、ALTER COLUMN で NOT NULL を追加します。
ex.)
ALTER TABLE tab_name ALTER COLUMN col_name DEFAULT 0;
ALTER TABLE tab_name ALTER COLUMN col_name SET NOT NULL;
のこり
not null カラムにmysqlが勝手に値を入れる、って事に強烈な根拠が欲しいんだけど、
もしかして default を指定するよりも早いのかな?
また今度検証してみよう。
East Asian Ambiguous Width Character
テキストエディタなどで → など一部の記号が入っている時に表示崩れなどが発生する問題。
問題
unicodeでambiguous width に分類される文字があると、以下の問題を起こす。
- 表示の乱れ
- カーソルの表示と内部バッファの位置のズレ
つまりまともに編集できなくなる。
(大体においては行単位でバッファの管理をしているので、行単位でおかしくなる)
原因
ambiguous width は、文字コードとして文字幅の規定がされていないのでアプリ側で表示する際の文字幅を確認して扱わないといけない。
問題が出るのは以下の2パターン。
- ambiguous width の文字幅を設定ファイルなどで指定すれば扱える(指定しないとダメ)
- そもそも対応が考えられていない
対応
screen
East Asian Ambiguous 対応としてパッチで提供されているので再インストールが必要。
https://savannah.gnu.org/bugs/?16666#postcomment
# すでにインストールされている場合はアンインストール yum remove screen # screen ソースをダウンロード wget http://ftp.gnu.org/pub/gnu/screen/screen-4.0.3.tar.gz tar zxvf screen-4.0.3.tar.gz cd screen-4.0.3 # パッチのダウンロード wget screen-4.0.2-patch-cjkwidth-cvs-2006052001 # その他のパッチもついでにダウンロード wget ftp://www.dekaino.net/pub/screen/screen-4.0.2-deadlock-patch wget ftp://www.dekaino.net/pub/screen/screen-4.0.2-hankanacopy-patch # パッチを当てる patch < screen-4.0.2-deadlock-patch patch < screen-4.0.2-hankanacopy-patch patch < patch-cjkwidth-cvs-2006052001 # インストール ./configure make && make install
screenの East Asian Ambiguous 対応パッチを読んでみる
utf8の文字単位に文字幅を確認する関数内で、East Asian Ambiguous かどうかを確認する為の文字コードテーブルが追加されてる。
int utf8_isdouble(c) int c; { // 〜 中略 〜 /* sorted list of non-overlapping intervals of East Asian Ambiguous * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ static const struct interval ambiguous[] = { { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, // 〜 中略 〜 { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } };