20210121のMySQLに関する記事は4件です。

Formオブジェクトを用いて作成したデータを、編集・更新する方法

要点

  • 初心者がアプリを作るときの参考に
  • formオブジェクトを用いた編集・更新機能は複雑なので詳しく知りたいorおさらいしたい
  • 下記のエラーを解消する方法
param is missing or the value is empty: 'Formオブジェクト名'

はじめに

メルカリのようなフリマアプリを作成中で、Formオブジェクトを用いて商品にタグ付けして編集・更新(edit・update)する機能を実装まで行いました
各モデルとコントローラーは以下のようになります

  • Item/商品
  • Tag/タグ
  • TagItemRelation/商品とタグの中間テーブル
  • TagsItem/ ItemとTagを同時に保存するためのFormオブジェクト
/app/model/item.rb
class Item < ApplicationRecord
  has_many :tag_item_relations, foreign_key: :item_id, dependent: :destroy
  has_many :tags, through: :tag_item_relations
end
/app/model/tag.rb
class Tag < ApplicationRecord
  has_many :tag_item_relations
  has_many :items, through: :tag_item_relations
  validates :tag_name, uniqueness: true
end
/app/model/tag_item_relation.rb
class TagItemRelation < ApplicationRecord
  belongs_to :item
  belongs_to :tag
end
/app/form/tags_item.rb
class TagsItem
  include ActiveModel::Model
  attr_accessor :item_name,
                :tag_name

  with_options presence: true do
    validates :item_name
  end

  def save
    item = Item.create(item_name: item_name)
    tag = Tag.where(tag_name: tag_name).first_or_initialize
    tag.save
    TagItemRelation.create(item_id: item.id, tag_id: tag.id)
  end
end

コントローラー

/app/controller/items_controller.rb
class ItemsController < ApplicationController
   def index
     @items = Item.all.order('created_at ASC')
   end

   def edit
    @item = current_user.items.find(params[:id])
    @tegs_item = TagsItem.new(item: @item)
   end

   def update
    @item = current_user.items.find(params[:id])
    @tags_item = TagsItem.new(update_items_params, item: @item)
     if @tags_item.valid?
       @tags_item.save
       redirect_to root_path
     else
       render :edit
     end
   end

  private

  def update_items_params
    params.require(:item).permit(
      :item_name,
      :tag_name
    )
  end
end

しかし実際に商品を編集・更新をしてみると、、、

param is missing or the value is empty: 'tags_item'

のエラーが出てしまい、商品の更新ができませんでした

調べたこと

エラー内容を読んでみると生成したFormオブジェクトの'tag_item_relation'が空になっているとのこと
そして、binding.pryなどを使って調べてみると、そもそもこの記述では'tags_item'編集・更新では機能していないことが分かります
さらに原因を調べてみると新規投稿(new・create)と編集更新(edit・update)の機能を仕分けしていなかったことだと分かりました

Formオブジェクトで編集更新

今回のエラーの原因はFormオブジェクトに記載したsaveが新規投稿と編集更新で仕分けされていなかったことだと分かりました

/app/form/tags_item.rb
class TagsItem
  include ActiveModel::Model
  attr_accessor :item_name,
                :tag_name

  with_options presence: true do
    validates :item_name
  end

  def save
    item = Item.create(item_name: item_name)  #ここがcreateアクションのみ
    tag = Tag.where(tag_name: tag_name).first_or_initialize
    tag.save
    TagItemRelation.create(item_id: item.id, tag_id: tag.id)
  end
end

この記述を

/app/form/tags_item.rb
class TagsItem
  include ActiveModel::Model
  attr_accessor :item_name,
                :tag_name

  with_options presence: true do
    validates :item_name
  end

  # itemがすでに保存されているものか、新規のものかで、PUTとPATCHを分ける
  delegate :persisted?, to: :item


  # initializeでFormオブジェクトの値を初期化し、更新の際はdefault_attributesを呼び出す設定
  def initialize(attributes = nil, item: Item.new)
    @item = item
    attributes ||= default_attributes
    super(attributes)
  end

  def save
    return if invalid?

    ActiveRecord::Base.transaction do
      # mapメソッドを使いsplit_tag_namesをtagの情報に変換
      tags = split_tag_names.map { |tag_name| Tag.find_or_create_by!(tag_name: tag_name) }
      item.update!(item_name: item_name, tags: tags)
    end
  rescue ActiveRecord::RecordInvalid
    false
  end

  #  formを飛ばす場所を(#createか#updateか)を判別して、切り替えている
  def to_model
    item
  end

  private

  attr_reader :item

  def default_attributes
    {
      item_name: item.item_name,
      tag_name: item.tags.pluck(:tag_name).join(',')
    }
  end

  def split_tag_names
    tag_name.split(',')
  end
end

に変更することで無事にタグ付け機能の編集更新を行うことができました

参考にしたサイト

Railsのデザインパターン: Formオブジェクト

formオブジェクトパターンで、タグ付け機能を実装

関連記事

Formオブジェクトを用いて作成したデータを、特定のデータのみ削除する方法←(前回記事)

railsでタグ機能を実装する

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

DockerのMySQLコンテナに外部からアクセスしたい

docker ps
docker exec -it 7c10 /bin/bash

root@7c10ef625739:/#

な表示になるので、

mysql -u root -p

でログインする

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

Dockerを用いてRuby on Railsの環境構築をする方法( Docker初学者向け )

Dockerを初めて勉強するとき、多くの方が「参考書」や「入門Dockerなどのサイト」で基礎的なところから学習すると思います。
しかし、Dockerとは何なのか、何のイメージも沸いていない状態で、様々な単語を羅列されても、さっぱりわからないという方も多いと思います。( 少なくとも私はそうでした。)
なので極論、基礎すっ飛ばしてまず実際にDockerで仮想環境を構築しながら学んでいこうという話です。
そして何となくでいいのでイメージを掴んでから、基礎を学べば良いと思います。
Dockerを初めて学習する方の中で、Ruby on Railsで開発したアプリケーションをポートフォリオとしている方も多いのではないでしょうか。
なので今回は、Dockerを用いてRuby on Railsの環境構築をする方法 をご紹介いたします。

ですが、超基本的なことだけ先に説明させてください。
( とにかく早く環境構築した人は 前提 まで飛んでください )

Dockerとは

1_y6CvfE6WUgoIdT8Mp0Ev_g.png
Dockerは 仮想環境を構築するためのツール です。

- Dockerエンジンとは -

Dockerエンジンとは Dockerイメージの作成 や コンテナの起動 などを行う、いわばDockerの要です。

- コンテナとは -

コンテナとはDockerエンジン上で構築される仮想環境そのものです。
コンテナは、CentOSUbuntsなどの OS、NginxMySQLなどの ミドルウェア、RailsWordPressなどアプリケーションまで、様々な環境を構築することができます。

- Dockerイメージとは -

Dockerイメージは、コンテナを作成するための レシピ のようなものです。
CentOS、MySQL、Ruby などなど、他にも数えきれないほどのDockerイメージがあります。

- Dockerfileとは -

Dockerfileは、オリジナルのDockerイメージを作成するための設計書のようなものです。
既存のDockerイメージをベースに、パッケージをインストールしたり、ファイルを書き換えたりした、新しいイメージを作成することができます。

- Docker composeとは -

docker-compose-button.jpg
Docker compose とは、複数のコンテナの構築、実行する手順を自動化するツールです。
その手順などを記したファイルがdocker-compose.ymlで、これのおかげで少ないコマンドの実行で複数のコンテナを起動することができます。

環境

  • MacOS
  • Docker 20.10.2
  • docker-compose 1.27.4
  • Ruby 2.5.7
  • Rails 5.2.4
  • MySQL 5.7

前提

  • DockerおよびDocker composeのインストールを済ませてください
  • これ以降の説明のmyappの部分はご自身のアプリケーション名に変えてください
  • とにかく早く環境構築したい方は、説明を飛ばし、コピペして コマンドを実行してください。

アプリケーションの作業ディレクトリを作成

mkdirで作業ディレクトリを作成します。

$ mkdir myapp

必要なファイルを用意

ファイル構成は以下のようになります。

myapp
  |-- Dockerfile
  |-- docker-compose.yml
  |-- Gemfile
  |-- Gemfile.lock

それでは、先ほど作成したディレクトリの中に以下の各ファイルを作成していきます。

① Gemfile

Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 5.2.4', '>= 5.2.4.2'

③で作成するDockerfilebundle installする項目があるのですが、そのときにこのGemfileを使います。

② Gemfile.lock

Gemfile.lock

Gemfile.lockの中身は空でOKです。
Gemfile.lockGemfileとセットで必要なので、用意します。

③ Dockerfile

Dockerfile
FROM ruby:2.5.7
# ベースにするイメージを指定

RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs default-mysql-client vim
# RailsのインストールやMySQLへの接続に必要なパッケージをインストール

RUN mkdir /myapp
# コンテナ内にmyappディレクトリを作成

WORKDIR /myapp
# 作成したmyappディレクトリを作業用ディレクトリとして設定

COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
# ローカルの Gemfile と Gemfile.lock をコンテナ内のmyapp配下にコピー

RUN bundle install
# コンテナ内にコピーした Gemfile の bundle install

COPY . /myapp
# ローカルのmyapp配下のファイルをコンテナ内のmyapp配下にコピー

Dockerfileは自分オリジナルの イメージ を作成するための設計書のようなものです。
今回はruby:2.5.7という元々誰かが作ったイメージをベースに、Railsのインストールに必要なパッケージをインストールしたり、myappをコピーしたり、bundle installしたりと、様々なもの加えているというわけです。

④ docker-compose.yml

docker-compose.yml
version: '3'
# docker-composeの書式のバージョンを指定します。(原則、最新を指定する)

services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_USER: root
      MYSQL_ROOT_PASSWORD: pass
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql

  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp/
    ports:
      - 3000:3000
    depends_on:
      - db

volumes:
  mysql_data:

docker-compose.ymlは複数のコンテナを定義し、実行するためのファイルです。
ファイルに書かれている単語をひとつずつ解説してきます。

services:

servicesの中でコンテナを定義します。
今回はMySQLRailsをコンテナ化するので、名前をわかりやすくdbwebとします。
実際に作成されるコンテナ名は「アプリケーション名 + サービス名 + 連番」myapp_db_1myapp_web_1となります。

image:

利用するイメージを指定し、コンテナを構築します。

enviroment:

MySQLに関する環境変数を設定します。(今回はパスワードのみ)
パスワードはご自身で決めて構いません。

ports:

ポート番号に関するを設定します。
MySQL は 3306、 Rails は 3000 というポート番号がデフォルト値として設定されています。
Railsに関してはおなじみのlocalhost:3000の 3000 ですね。
Dockerを使わず、ローカルで環境構築した場合は直接このlocalhost:3000にアクセスすれば良かったわけですが、Dockerを使った場合、コンテナに外部からアクセスすることができないので、ちょっとした工夫が必要です。
そこで登場するのがこのports:です。
ports:- ローカル側のポート番号 : コンテナ側のポート番号で表します。
今回、ports: - 3000:3000としていますが、説明をわかりやすくするため、仮にもしports: - 9000:3000に設定するとします。
これはコンテナ側の3000ポート9000ポートでのアクセスを許可するという意味になります。
つまりこの場合、localhost:9000でアクセス可能になるということになります。

volumes:

ボリュームに関する設定をします。
ボリュームとはコンテナにおいて生成されるデータを永続的に保存するためのものです。
MySQLを例にとると、MySQLをコンテナ化し、そこに テーブル や カラム などのデータを保存したとします。
しかし、そのMySQLのコンテナを削除してしまうと、保存してあったテーブル、カラムなどのデータも一緒に削除されてしまいます。
それはまずいので、データだけローカルにも保存しておく、この仕組みをボリュームといいます。
このボリュームを利用すると、仮にコンテナを削除したとしても、新しくコンテナを立ち上げたときに、そのデータを再利用することができます。

ボリュームを保存する方法は大きく分けて2つあります。
1つ目は、保存したいディレクトリをマウントする方法、2つ目は、名前付きボリュームを利用する方法 です。

今回MySQLは2つ目の名前付きボリュームを利用する方法を使います。

この方法は、ローカルに名前付きのボリュームを作成し、そこにコンテナ側の指定したディレクトリを保存するというものです。
名前付きボリュームの場合、volumes:- ボリューム名 : コンテナ側の保存させたいディレクトリで表します。
今回、ボリューム名mysql_dataとし、コンテナ側の保存させたいディレクトリは MySQLのデータ保存場所である/var/lib/mysqlを指定します。
結果、volumes: - mysql_data:/var/lib/mysqlという書き方になります。
そして、version: や services: と同じ段落(トップレベル)の、一番下に書いているvolumes:にボリューム名を記載し、名前付きボリュームであることを明示します。

次にRailsは、1つ目の保存したいディレクトリをマウントする方法を使います。
この方法は、「ローカル側の指定したディレクトリ」と「コンテナ側の指定したディレクトリ」を同期させて、両者を常に同じ状態に保つというものです。
マウントする場合、volumes:- ローカル側の同期させたいディレクトリ : コンテナ側の同期させたいディレクトリで表します。
今回、volumes: - .:/myapp/としています。
つまり、.( docker-compose.ymlのあるディレクトリ、つまりローカル側のmyapp配下 ) と /myapp/( コンテナ側のmyapp配下 ) を同期するということです。
これにより、コンテナ起動後、ローカルのmyapp配下のファイルなどを編集すると、コンテナ側にも編集が反映されます。

build:

Dockerfileのあるディレクトリをして指定します。( docker-compose.ymlから見て同じディレクトリにDockerfileが存在するので.)
ここのステップで ③で説明したDockerfileを利用しオリジナルのイメージを作成し、コンテナを構築します。

command:

コンテナ起動時の実行されるコマンドを設定します。
今回設定するコマンドは、bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" です。
bash -c ""は コンテナの中に入り""内のコマンドを実行するという命令です。
rm -f tmp/pids/server.pidで rails server が起動していた場合、停止します。( 何らかの原因でrails server がすでに起動していた場合、新しいrails serverが起動できないため。)
bundle exec rails s -p 3000 -b '0.0.0.0'で rails server を起動します。
&&は複数のコマンドを実行するときに用います。

depends_on:

コンテナの作成順序の設定です。
depends_on: - dbは、MySQLのコンテナが起動してからRailsのコンテナを起動するという意味です。

rails new を実行

$ docker-compose run web rails new . --force --database=mysql --skip-bundle

アプリケーションの作業ディレクリに移動しdocker-compose runコマンドでrails newを実行します。

--force : 同じファイルがある場合、上書きする
--database=mysql : データベースにMySQLを指定する
--skip-bundle : bundle install をスキップする。( bundle installはあとで行います。)

このコマンドを実行すると、Dockerfileを元に、イメージとコンテナを作成し、そのコンテナの中でrails newを実行します。
よって、ローカルにも見慣れたファイルが作成されたと思います。

docker-compose build を実行

$ docker-compose build

rails newGemfileが更新されたので、bundle installするため、docker-compose buildを実行します。
先ほどのdocker-compose runで作成されたイメージはrails newされる前のGemfilebundle installしたイメージなので、更新されたGemfilebundle installしたイメージを再度作成する必要があります。

database.yml を編集

/config/database.yml
# MySQL. Versions 5.1.10 and up are supported.
#
# Install the MySQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
# 以下2行を編集
  password: pass
  host: db

development:
  <<: *default
  database: myapp_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: myapp_test

# As with config/secrets.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password as a unix environment variable when you boot
# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full rundown on how to provide these environment variables in a
# production deployment.
#
# On Heroku and other platform providers, you may have a full connection URL
# available as an environment variable. For example:
#
#   DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# You can use this database configuration with:
#
#   production:
#     url: <%= ENV['DATABASE_URL'] %>
#
production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>

password:host:を編集します。
password: には docker-compose.ymlMYSQL_ROOT_PASSWORD:に書いたパスワードを記載します。
host: には docker-compose.ymlで命名したMySQLのコンテナ名dbを記載します。

docker-compose up -d でコンテナを起動

$ docker-compose up -d

このコマンドでコンテナを起動します。
-dをつけることでバックグラウンドで起動します。

これでコンテナが起動できました。
以下のコマンドでちゃんと起動しているか確認しましょう。

$ docker-compose ps

   Name                  Command               State                 Ports              
----------------------------------------------------------------------------------------
myapp_db_1    docker-entrypoint.sh mysqld      Up      0.0.0.0:3306->3306/tcp, 33060/tcp
myapp_web_1   bash -c rm -f tmp/pids/ser ...   Up      0.0.0.0:3000->3000/tcp 

このように、それぞれのコンテナが UP になっていれば起動成功です。

データベースを作成

以下のコマンドでデータベースを作成します。

$ docker-compose exec web rails db:create

localhost:3000にアクセス

localhost:3000にアクセスし、以下のページが表示されれば DockerによるRailsの環境構築 は完了です。
スクリーンショット 2021-01-20 19.39.11.png

環境構築後の開発の仕方

これまで通り、ファイルの編集などは基本ローカル環境で行います
しかしrails grails db:migrateなどの railsコマンド はコンテナ内で実行します。
なので、コンテナに入る必要があります。
以下のコマンドでRailsのコンテナに入ります。

$ docker-compose exec web bash

するとプロンプト( ターミナルの左側の部分 )が以下のように変わると思います。( @の後ろはコンテナのID )

root@97b8e3430f3f:/myapp#

これでコンテナの中に入れました。ここで railsコマンド を実行してください。
これまでのローカル環境での開発で、ターミナルで実行していたコマンドは全てこのコンテナ内で行います。( gitコマンドはローカル環境で行います )

コンテナを抜けるには、以下のコマンドを実行してください。( もしくはcontrol + d )

$ exit

- ログの表示 -

これまではrails sでサーバーを起動し、ログが表示されていたと思います。
コンテナ内において、ログを表示させる方法は以下の通りです。

コンテナ内に入り、以下のコマンドを実行します。

$ tail -f log/development.log

これでログが表示されます。

ターミナルでタブを3つ開き、「ローカルの操作をするタブ」「コンテナ内でrailsコマンドなどを実行するタブ」「ログを表示するタブ」に分けると効率的な開発ができるかもしれません。

- デバッグに関して -

開発中にエラーが発生し、controllerを編集し、デバッグしたとします。
この場合、一度サーバーを再起動させると思います。

Dockerの場合、Railsのコンテナ( myapp_web_1 )ごと以下のコマンドで再起動します。

$ docker-compose restart web

しかし、デバッグするたびにいちいちコンテナを再起動するのは非効率です。
なので、サーバー起動中もコードの更新をチェックしてくれるように設定します。

config/environments/development.rbの一番下に記載されている以下を

config.file_watcher = ActiveSupport::EventedFileUpdateChecker

次のように変更します。

config.file_watcher = ActiveSupport::FileUpdateChecker

これで再起動を行わずにデバッグが可能になります。

その他の基本的なDockerのコマンド

・ コンテナの停止、削除

$ docker-compose down

・ コンテナの停止、削除および紐づいている名前付きボリュームの削除

$ docker-compose down -v

※ 永続化しているデータが消えるの要注意 !

・ docker-composeのログを出力

$ docker-compose logs

コンテナがうまく起動しなかったときなどにログを見ると、原因がわかったりします。

・ イメージの一覧

$ docker images

・ イメージの削除

$ docker rmi イメージID

・ 名前付きボリュームの一覧

$ docker volume ls

・ 名前付きボリュームの削除

$ docker volume rm ボリュームの名前

まとめ

最初の内は理解できないことも多いと思います。
ひとつずつ着実に理解していくのではなく、全体的に漠然と理解していくと良いと思います。

この記事を読んでわからないことがあれば、コメントしていただければお答えいたします !

最後まで読んでいただきありがとうございました。

参考

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

MySQL8 の TIMESTAMP, DATETIME におけるExplicit Default な式定義について

MySQL8.0.13 からDEFAULT値に簡単な式を書けるようになりました。超便利です。

timestampでデフォルト値を書いたときにALTER TABLE ADD COLUMNCREATE INDEXなどテーブル加工をするとエラーになることがあります。
挙動が罠ではあるが、バグではないです。

https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html#data-type-defaults-implicit

The exception is that, for TIMESTAMP and DATETIME columns, you can specify the CURRENT_TIMESTAMP function as the default, without enclosing parentheses. See Section 11.2.5, “Automatic Initialization and Updating for TIMESTAMP and DATETIME”.

リファレンスマニュアルを見ても、TIMESTAMP と DATETIME だけは例外と書いてあります。
この2つだけは規定のデフォルト値に従わなければいけません、
詳細は、 https://dev.mysql.com/doc/refman/8.0/en/timestamp-initialization.html
基本は、ここに記述されてるもののみデフォルトにできます。

TIMESTAMPやDATETIMEはデフォルトを指定しないときのImplicit Default の挙動が他の型と異なるため、NOT NULLのときにトラブルになることがあります。
INSERT や UPDATEなどデータを入れるときはExplicit Defaultの式がおかしくてもエラーにならずに良い感じに補正されて動きますが、ALTERなどのときはNOT NULL型ではエラーになります。

mysql> create table tt1 (t timestamp not null default (CURRENT_TIMESTAMP + INTERVAL 1 YEAR), i int);
mysql> create table tt2 (t timestamp not null default CURRENT_TIMESTAMP, i int);
mysql> create table tt3 (t timestamp null default (CURRENT_TIMESTAMP + INTERVAL 1 YEAR), i int);
mysql> create table tt4 (t timestamp null default CURRENT_TIMESTAMP, i int);
mysql> desc tt1;
+-------+-----------+------+-----+---------------------------+-------------------+
| Field | Type      | Null | Key | Default                   | Extra             |
+-------+-----------+------+-----+---------------------------+-------------------+
| t     | timestamp | NO   |     | (now() + interval 1 year) | DEFAULT_GENERATED |
| i     | int       | YES  |     | NULL                      |                   |
+-------+-----------+------+-----+---------------------------+-------------------+
2 rows in set (0.00 sec)

mysql> desc tt2;
+-------+-----------+------+-----+-------------------+-------------------+
| Field | Type      | Null | Key | Default           | Extra             |
+-------+-----------+------+-----+-------------------+-------------------+
| t     | timestamp | NO   |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
| i     | int       | YES  |     | NULL              |                   |
+-------+-----------+------+-----+-------------------+-------------------+
2 rows in set (0.00 sec)

mysql> desc tt3;
+-------+-----------+------+-----+---------------------------+-------------------+
| Field | Type      | Null | Key | Default                   | Extra             |
+-------+-----------+------+-----+---------------------------+-------------------+
| t     | timestamp | YES  |     | (now() + interval 1 year) | DEFAULT_GENERATED |
| i     | int       | YES  |     | NULL                      |                   |
+-------+-----------+------+-----+---------------------------+-------------------+
2 rows in set (0.00 sec)

mysql> desc tt4;
+-------+-----------+------+-----+-------------------+-------------------+
| Field | Type      | Null | Key | Default           | Extra             |
+-------+-----------+------+-----+-------------------+-------------------+
| t     | timestamp | YES  |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED |
| i     | int       | YES  |     | NULL              |                   |
+-------+-----------+------+-----+-------------------+-------------------+
2 rows in set (0.00 sec)
mysql> insert tt1 set i=0;
mysql> insert tt2 set i=0;
mysql> insert tt3 set i=0;
mysql> insert tt4 set i=0;

普通に入る。

mysql> alter table tt1 add j int;
ERROR 1067 (42000): Invalid default value for 't'

mysql> alter table tt2 add j int;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table tt3 add j int;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table tt4 add j int;
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

tt1 だけALTERがエラーになります。
NULLを許可してる場合はエラーになりませんが、たまたま動いてると考えたほうが良さそうです。

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