give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

僕たちは本当のSQLite3を何も知らない(柔軟なデータ型と外部キー制約の罠について)

「えっ、SQLite3ってこんな仕様なの!?」と最近ビックリしたことを紹介します。

たとえばこんな2つのテーブルがあったとします。

CREATE TABLE blogs (
    id int primary key,
    title varchar(32)
);

CREATE TABLE comments (
    id int primary key,
    content varchar(32),
    blog_id int,
    foreign key (blog_id) references blogs(id)
);

ポイントはcommentsテーブルのblog_idにはblogs(id)への外部キー制約が貼ってあることです。 もちろん、blog_idblogs(id)も、どちらもint型です。

で、以下のようなSQLを発行します(blog_idの値に注目)。

-- blogsにデータを追加
INSERT INTO blogs (id, title) VALUES (10, 'Hello');

--- commentsにデータを追加(blog_idの値に注目)
INSERT INTO comments (id, content, blog_id) VALUES (20, 'Good!', 'XYZ');

さて、この実行結果はどうなるでしょうか? 僕は当然commentsテーブルへのINSERTは失敗するものと思っていました。

ところがどっこい、INSERTできてしまいました……。(blog_idに"XYZ"が入ってしまっている)

sqlite> .headers on
sqlite> 
sqlite> SELECT * FROM blogs;
id|title
10|Hello
sqlite> 
sqlite> SELECT * FROM comments;
id|content|blog_id
20|Good!|XYZ

なんでなん!?

ここが変だよSQLite3

SQLite3には実はこんな罠があります。

データ型が柔軟すぎる

SQLite3は"Flexible Typing"という方針で実装されており、INT型のカラムには以下のデータ型を保存することができます。

  • INTEGER
  • REAL
  • TEXT
  • BLOB

参考: The Advantages Of Flexible Typing

えっ、こんなのデータ型の意味ないやん……。

ちなみに上記の参考リンクには"Flexible Typing"のメリットやSQLite3がなぜ"Flexible Typing"を採用しているのか、といった説明がつらつらと書いてあります。 興味がある人は読んでみてください。(Google自動翻訳のリンクを載せておきます)

柔軟な入力の利点(自動翻訳)

外部キー制約がデフォルトで無効になっている

仮にデータ型が柔軟だったとしても、外部キー制約がちゃんと機能していれば「ダメダメ、そんなデタラメなblog_idは保存できないよ!」とデータベースが怒ってくれそうです。が、SQLite3は怒ってくれません。

それもそのはず、なんとSQLite3はデフォルトで外部キー制約が無効になっているのです。(マジかよ)

Foreign key constraints are disabled by default (for backwards compatibility)

SQLite Foreign Key Support

外部キー制約を有効にしたい場合は、DB接続後に PRAGMA foreign_keys = true を実行する必要があります。こうすればデタラメなblog_idを保存できなくなります。

sqlite> PRAGMA foreign_keys = true;
sqlite> INSERT INTO comments (id, content, blog_id) VALUES (21, 'Good!', 'XYZ');
Runtime error: FOREIGN KEY constraint failed (19)

ちなみに外部キー制約が無効になっているのは後方互換性のため(for backwards compatibility)で、将来的にはデフォルトで有効になる可能性もあるそうです。

Note, however, that future releases of SQLite might change so that foreign key constraints enabled by default.

SQLite Foreign Key Support

余談:SQLite1やSQLite2って存在するの?

「普段は"SQLite3"っていう名前ばっかりよく聞くけど、"SQLite1"や"SQLite2"って聞いたことがないなあ」と思って調べてみたところ、いちおう1や2も存在しているようです。

  • 2000-08-17:1.0 リリース
  • 2001-09-28:2.0.0 リリース
  • 2004-06-18:3.0.0 リリース
  • 2023-05-16:3.42.0 リリース(2023年5月現在の最新版)

参考:History Of SQLite Releases

でもかれこれ19年ぐらいバージョン3なんですね。 そりゃ"SQLite3"しか聞いたことがないはずだわ〜。

まとめ

SQLite3はRuby on RailsのデフォルトのRDBMSなので、なんだかんだで10年ぐらい使っていることになります。 でも、こんな仕様になっているとは全然知りませんでした! 今まで使ってきたRDBMS(PostgreSQLとか、MS SQL Serverとか)とは大きく仕様が違うのでビックリです。

データ型の強制や外部キー制約はデータの整合性を保つための重要な機能なので、やっぱりSQLite3は本番環境で使うRDBMSじゃないな、と改めて思いました(もともと使ったことないけど)。

とはいえ、どんなRDBMSでも「えっ、そんな仕様なの!?」って驚く部分は何かしらあるので、普段あまり使わないRDBMSを使うときは方言(?)に十分気を付ける必要がありますね。

SQLアンチパターン

SQLアンチパターン

Amazon

【動画付き】RailsGilrsのドキュメント更新一緒にやりませんか? → やってみました! #railsgirls #railsgirlsjp

はじめに

id:maimux2x さんが、ブログで「RailsGilrsのドキュメント更新一緒にやりませんか?」と呼びかけていました。

maimux2x.hatenablog.com

ブログを読むととても丁寧に手順が解説されていて、「これなら誰でも"初めてのOSS活動"ができるのでは?」と思いました。

僕もやってみた!

というわけで、僕も実際にやってみました。
今回僕が訳したのは「Create your first voting app with Sinatra」というページで、以下のプルリクエストで翻訳しています。

github.com

翻訳後のページはこんなふうになってます。(僕のローカル環境での実行結果です)

翻訳にかけた時間は約1時間した(スピード重視で翻訳)。

手順を動画で説明してみました

id:maimux2x さんのブログだけでも十分わかりやすいのですが、動画もあった方がさらにわかりやすいのでは?と思い、実際に僕が作業する様子を動画にしてみました。
以下の動画ではgit cloneして、翻訳して、プルリクエストを作成するまでの手順を紹介しています。


www.youtube.com

「コントリビュートの手順がわからない」「なんとなくわかるけど自信がない」という人は、ぜひ上の動画を参考にしてみてください!

Q. 私も協力したい!でも、どこを翻訳すればいいの?

railsgirls.jpのSlackによると、

未翻訳のガイドは
【10. あなたのアプリをインターネットに公開しよう】の「Heroku」のページ以降です。
https://railsgirls.jp/

とのことです。

つまり、以下の画像のあたりですね。

なお、railsgirls.jpのSlackですが、過去の参加者やコーチが中心に参加しているものの、「RailsGirlsに何かしら貢献したい気持ちがある人なら以下のページから自由に参加OK」とのことでした😉

https://rails-girls-slack.herokuapp.com/

2023-5-17 訂正
すいません、上記のリンクは英語版の本家RailsGirlsのSlack参加リンクでした🙏
日本版RailsGirlsのSlackへの参加方法については、わかり次第また追記します!
 ↓
さしあたって、まいむさん(@maimux2x)にTwitter DMを送ると良いようです。

※もしも直近でslackへのご参加を希望される方は自分にTwitter経由でDMをいただけると幸いです。招待リンクをお送りします!

RailsGilrsのドキュメント更新一緒にやりませんか? - 出る杭は打たれない

まとめ

というわけで、今回のエントリでは、RailsGilrsのドキュメント更新を実際にやってみましたよ、という話を書いてみました。
翻訳作業そのものはちょっと大変かもしれませんが、難しいコードを書いたりする必要はないので、やる気と時間さえあれば誰でもできると思います。

ぜひ、みなさんもチャレンジしてみてください!

【初心者必見】これまでに書いた自動テストやRSpecに関する記事のまとめ

これはなに?

自動テストの初心者がテストコードを書くときに意識したことが方が良いことについて、僕が過去に書いた記事をまとめたものです。

RSpecでRailsのテストを書くケースがメインですが、自動テスト全般に役立つ知識も結構多いはずです。

<もくじ>

どんなテストを書けばいいのかわからない

何をテストしたらいいの?どういうテストを書けばいいの?という基本事項をまとめています。
qiita.com

バリデーションのテストって書くのは簡単ですが、本当に意味ある?っていう話を書いてます
qiita.com

なぜあなたはテストを書くのですか?と聞かれたときに、ちゃんと答えられるようにしましょう。

こちらはid:t-wadaさんの書かれた記事ですが、privateメソッドをテストするかどうかはFAQのひとつなのでよく参考にさせてもらってます。
t-wada.hatenablog.jp

可読性の高いテストを書きたい

共通したコードは全部beforeブロックに突っ込んで共通化、はNGです。
qiita.com

テストコードはあえてDRYを捨てて、APIドキュメントのように読めるコードを書きましょう。

不具合をちゃんと検知できるテストを書く

テストコードにアプリ側とそのまま同じコードを書いたりしちゃダメです!!
qiita.com

テストは全部パスしたけど、実はバグが隠れてました、なんてことがないようにしましょう。
qiita.com

「〜ではないこと」だけを検証して満足すると痛い目を見ます。
qiita.com

一覧ページのデータはちゃんとORDER BYを付けてユニークな並び順を保証しましょう。
qiita.com

RSpecの基本的な使い方を知りたい

実践的でわかりやすい、RSpecの定番入門記事。全4回。
qiita.com
qiita.com
qiita.com
qiita.com

RSpecの公式ドキュメントは最近URLが変わったのでご注意あれ。
qiita.com

Railsのテストを書くならこれ!僕が翻訳した電子書籍です。
leanpub.com

RSpecをもっと使いこなしたい

itとかexampleとかあるけど、どう使い分けたらいいの?というFAQに対する回答です。
qiita.com

RSpecは多機能ですが、全部モリモリに使おうとすると全然読めないテストコードが生まれまっせ。
qiita.com

僕は「subjectは滅多に使わない教」の信者です(人によって宗派がわかれるやつ)。
blog.jnito.com

こちらは弊社ソニックガーデンのメンバーが書いた記事です。sleepメソッド、乱用してませんか?
qiita.com

MinitestとRSpec、どっちがいいの?

MinitestとRSpecの比較記事ですが、いずれもちょっと情報が古いので現在(2023年)は少し状況が変わってるかも?(あまり変わってないかも?)
blog.jnito.com
blog.jnito.com

MinitestをRSpecに書き直す

「プロを目指す人のためのRuby入門(第1版)」のテストコードをMinitestからRSpecに書き換えてみました。全3回。
qiita.com
qiita.com
qiita.com

ちなみに現在は改訂2版が発売中です。上の記事の大半は改訂2版でも役に立つはずです。

勉強会でみんなの悩みを聞いてみた

勉強会でテストコードに関するいろんなお悩みを聞いてみました。誰もが一度は疑問に思うテーマが満載です。
blog.jnito.com
blog.jnito.com

まとめ

とりあえず、ぱっと思い出した記事をひととおり挙げてみました。
また何か新しい記事を書いたら追記しますね。