Featured image of post 第1回 - 最小のSELECTクエリを作ろう(Perl SQLクエリビルダー入門)

第1回 - 最小のSELECTクエリを作ろう(Perl SQLクエリビルダー入門)

PerlとMooでSQLクエリビルダーを作る連載第1回。シンプルな文字列結合で「SELECT * FROM users」を生成する方法を学びます。Builderパターン習得への第一歩。

@nqounetです。

今回から「PerlとMooで作るSQLクエリビルダー」シリーズを始めます。SQLクエリビルダーを自作しながら、GoFデザインパターンの1つ「Builderパターン」を学んでいきます。

「え、SQL文なんて文字列結合で作ればいいじゃん」と思ったあなた。その素朴なアプローチがどんな地獄への入り口なのか、これから一緒に体験していきましょう(そして華麗に脱出します)。

このシリーズでは、以下のことを学べます:

  • SQLクエリを段階的に構築する技法
  • Fluent Interface(メソッドチェーン)の実装
  • SQLインジェクション対策の基本(第3回でちょっと危険な実験もします)
  • Builderパターンの本質的な価値

まずは最もシンプルなSELECTクエリから始めましょう。

シリーズの前提

このシリーズでは以下の環境を想定しています:

  • Perl v5.36以上
  • Moo(オブジェクト指向フレームワーク)
  • SQLite(ローカルでの実験用)

「Mooによるオブジェクト指向Perl」入門連載を修了していることを前提としています。

文字列結合でSQLを組み立てる

最初のアプローチとして、シンプルな文字列結合でSQLクエリを組み立ててみます。

Query.pm(文字列結合版)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo

package Query;
use v5.36;
use Moo;

has table => (is => 'ro', required => 1);

sub to_sql ($self) {
    return "SELECT * FROM " . $self->table;
}

1;

このクラスはとてもシンプルです。table属性を受け取り、to_sqlメソッドでSQLクエリを生成します。

使ってみる

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo

use v5.36;
use lib 'lib';
use Query;

my $query = Query->new(table => 'users');
say $query->to_sql;
# 出力: SELECT * FROM users

たった数行で動くSQLクエリビルダーができました。

実行してみる

実際にSQLiteで動作を確認してみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo, DBI, DBD::SQLite

use v5.36;
use lib 'lib';
use Query;
use DBI;

# SQLiteデータベース接続
my $dbh = DBI->connect('dbi:SQLite:dbname=:memory:', '', '', {
    RaiseError => 1,
    PrintError => 0,
});

# テストテーブル作成
$dbh->do('CREATE TABLE users (id INTEGER, name TEXT)');
$dbh->do("INSERT INTO users VALUES (1, 'Alice')");
$dbh->do("INSERT INTO users VALUES (2, 'Bob')");

# クエリビルダーを使用
my $query = Query->new(table => 'users');
my $sql = $query->to_sql;
say "SQL: $sql";

# 実行
my $rows = $dbh->selectall_arrayref($sql, { Slice => {} });
for my $row ($rows->@*) {
    say "ID: $row->{id}, Name: $row->{name}";
}

実行結果:

1
2
3
SQL: SELECT * FROM users
ID: 1, Name: Alice
ID: 2, Name: Bob

ちゃんと動いていますね。

今回のまとめ

今回は最もシンプルなSQLクエリビルダーを作りました。

  • Queryクラスでテーブル名を受け取り、SELECT * FROM テーブル名を生成
  • 文字列結合による素朴な実装
  • SQLiteで実際に動作確認

しかし、このアプローチには問題があります。WHERE条件を追加したい場合はどうなるでしょうか?ORDER BYは?LIMITは?

次回は「WHERE条件を追加したい」という要望に応えようとして、パラメータ地獄に陥る様子を体験します。

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。