... | ... |
@@ -5,6 +5,8 @@ |
5 | 5 |
renamed tag_processors to tags. tag_prosessors is available, but deprecated. |
6 | 6 |
improved error message |
7 | 7 |
build all clause if param is undefined. |
8 |
+ each_column callback receive self as first argument. |
|
9 |
+ removed experimental txn_scope |
|
8 | 10 |
0.1636 |
9 | 11 |
added tests and cleanup |
10 | 12 |
0.1635 |
... | ... |
@@ -437,7 +437,7 @@ sub each_column { |
437 | 437 |
my $sth_columns = $self->dbh->column_info(undef, undef, $table, '%'); |
438 | 438 |
while (my $column_info = $sth_columns->fetchrow_hashref) { |
439 | 439 |
my $column = $column_info->{COLUMN_NAME}; |
440 |
- $cb->($table, $column, $column_info); |
|
440 |
+ $self->$cb($table, $column, $column_info); |
|
441 | 441 |
} |
442 | 442 |
} |
443 | 443 |
} |
... | ... |
@@ -590,17 +590,6 @@ sub table { |
590 | 590 |
return $self->{_tables}{$name}; |
591 | 591 |
} |
592 | 592 |
|
593 |
-sub txn_scope { |
|
594 |
- my $self = shift; |
|
595 |
- |
|
596 |
- require DBIx::TransactionManager; |
|
597 |
- |
|
598 |
- $self->{_transaction_manager} |
|
599 |
- ||= DBIx::TransactionManager->new($self->dbh); |
|
600 |
- |
|
601 |
- return $self->{_transaction_manager}->txn_scope; |
|
602 |
-} |
|
603 |
- |
|
604 | 593 |
our %VALID_UPDATE_ARGS |
605 | 594 |
= map { $_ => 1 } qw/table param |
606 | 595 |
where append filter allow_update_all query/; |
... | ... |
@@ -1255,21 +1244,6 @@ default to 0. This is experimental. |
1255 | 1244 |
This is overwrites C<default_bind_filter>. |
1256 | 1245 |
Return value of C<update()> is the count of affected rows. |
1257 | 1246 |
|
1258 |
-=head2 C<(experimental) txn_scope> |
|
1259 |
- |
|
1260 |
- { |
|
1261 |
- my $txn = $dbi->txn_scope; |
|
1262 |
- $dbi->insert(table => 'book', param => {title => 'Perl'}); |
|
1263 |
- $dbi->insert(table => 'book', param => {title => 'Good days'}); |
|
1264 |
- $txn->commit; |
|
1265 |
- } |
|
1266 |
- |
|
1267 |
-Create transaction scope. If you escape scope(that is { .. }) and commited, |
|
1268 |
-Rollback is automatically done. |
|
1269 |
- |
|
1270 |
-Note that this is feature of L<DBIx::TransactionManager> |
|
1271 |
-L<DBIx::TransactionManager> is required. |
|
1272 |
- |
|
1273 | 1247 |
=head2 C<(experimental) table> |
1274 | 1248 |
|
1275 | 1249 |
$dbi->table('book', |
... | ... |
@@ -462,12 +462,9 @@ You can change Result class if you need. |
462 | 462 |
my $dbi = DBIx::Custom->connect(...); |
463 | 463 |
$dbi->result_class('Your::Result'); |
464 | 464 |
|
465 |
-=head3 Register tag processor |
|
465 |
+=head3 Register tag |
|
466 | 466 |
|
467 |
-You can custamize query builder object |
|
468 |
- |
|
469 |
- my $dbi = DBIx::Custom->connect(...); |
|
470 |
- $dbi->register_tag_processor( |
|
467 |
+ $dbi->register_tag( |
|
471 | 468 |
name => sub { |
472 | 469 |
... |
473 | 470 |
} |
... | ... |
@@ -350,6 +350,40 @@ SQL文の末尾に文字列を追加したい場合は<append>を使用します |
350 | 350 |
またC<append>は、C<select>だけでなくC<insert()>、C<update()>、C<update_all()> |
351 | 351 |
C<delete()>、C<delete_all()>、C<select()>で使用することもできます。 |
352 | 352 |
|
353 |
+=head4 execute |
|
354 |
+ |
|
355 |
+任意のSQLを実行するにはexecuteメソッドを使用します。 |
|
356 |
+ |
|
357 |
+ $dbi->execute("select * from book;"); |
|
358 |
+ |
|
359 |
+C<execute()>はL<DBIx::Custom>の根幹のメソッドでありタグを展開します。 |
|
360 |
+ |
|
361 |
+ $dbi->execute( |
|
362 |
+ "select * from book {= title} and {= author};" |
|
363 |
+ param => {title => 'Perl', author => 'Ken'} |
|
364 |
+ ); |
|
365 |
+ |
|
366 |
+上記のタグを含んだSQLは次のように展開されます。 |
|
367 |
+ |
|
368 |
+ select * from book title = ? and author = ?; |
|
369 |
+ |
|
370 |
+SQLが実行されるときにプレースホルダ(?)に対応する位置にtitleとauthor |
|
371 |
+の値がが自動的に埋め込まれます。 |
|
372 |
+ |
|
373 |
+タグについてはL<5. タグ/"5. タグ">で詳しく解説しますが、 |
|
374 |
+ひとつの注意点があります。 |
|
375 |
+タグを展開するためにC<{>とC<}>は予約語になっています。 |
|
376 |
+もし利用したい場合は直前に\をおいてエスケープを行う必要があります。 |
|
377 |
+ |
|
378 |
+ $dbi->execute("... \\{ ... \\} ..."); |
|
379 |
+ |
|
380 |
+\自体がPerlのエスケープ文字ですので、二つ必要になるという点に注意してください。 |
|
381 |
+ |
|
382 |
+またexecuteのキュートな機能として、SQLの最後にセミコロンをおかなくても |
|
383 |
+かまいません。 |
|
384 |
+ |
|
385 |
+ $dbi->execute('select * from book'); |
|
386 |
+ |
|
353 | 387 |
=head2 3. 行のフェッチ |
354 | 388 |
|
355 | 389 |
C<select()>メソッドの戻り値はL<DBIx::Custom::Result>オブジェクトです。 |
... | ... |
@@ -551,19 +585,109 @@ C<delete_all()>、C<select()>で有効になります。 |
551 | 585 |
このような自動的に実行されるフィルタを登録できることがL<DBIx::Custom>の |
552 | 586 |
特徴のひとつです。 |
553 | 587 |
|
554 |
-=head3 個別にフィルタを適用する |
|
588 |
+=head3 個別のフィルタの適用 |
|
555 | 589 |
|
556 | 590 |
C<apply_filter()>を使って最初にすべてのテーブルの列について |
557 |
-フィルタを定義しておくこともできますが、 |
|
558 |
-さらに個別にフィルタを適用することもできます。 |
|
591 |
+フィルタを定義することもできますが、 |
|
592 |
+個別にフィルタを適用することもできます。 |
|
559 | 593 |
個別のフィルタはC<apply_filter()>で適用したフィルタを上書きます。 |
594 |
+個別のフィルタはSQLのasを使って、列の別名を作成する必要がある場合に活躍します。 |
|
595 |
+ |
|
596 |
+データベースに送信する場合に、個別のフィルタを適用するには、各メソッドの |
|
597 |
+C<filter>オプションを使用します。個別のフィルタは、C<insert()>、C<update()>、 |
|
598 |
+C<update_all()>、C<delete()>、C<delete_all()>、C<select()>、C<execute()> |
|
599 |
+で使用することができます。 |
|
600 |
+ |
|
601 |
+C<insert()>の例を示します。 |
|
560 | 602 |
|
561 |
-行をフェッチするときに個別にフィルタを適用するには、 |
|
603 |
+ $dbi->insert( |
|
604 |
+ table => 'book', |
|
605 |
+ param => {issue_date => $tp, first_issue_date => $tp}, |
|
606 |
+ filter => {issue_date => 'tp_to_date', first_issue_date => 'tp_to_date'} |
|
607 |
+ ); |
|
608 |
+ |
|
609 |
+C<execute()>の例を示します。 |
|
610 |
+ |
|
611 |
+my $sql = <<"EOS"; |
|
612 |
+select YEAR(issue_date) as issue_year |
|
613 |
+from book |
|
614 |
+where YEAR(issue_date) = {? issue_year} |
|
615 |
+EOS |
|
616 |
+ |
|
617 |
+ my $result = $dbi->execute( |
|
618 |
+ $sql, |
|
619 |
+ param => {issue_year => '2010'}, |
|
620 |
+ filter => {issue_year => 'tp_to_year'} |
|
621 |
+ ); |
|
622 |
+ |
|
623 |
+これはC<filter>を使う良くある例です。issue_dateの変換についてはC<apply_filter()> |
|
624 |
+で登録してあるのですが、新しく作成した列であるissue_yearについては、 |
|
625 |
+何の変換も登録されていません。ですので、個別にフィルタを設定しています。 |
|
626 |
+ |
|
627 |
+また反対に行をフェッチするときにも個別のフィルタを適用することができます。 |
|
628 |
+フィルタを適用するには、 |
|
562 | 629 |
C<DBIx::Custom::Result>クラスのC<filter>メソッドを使用します。 |
563 | 630 |
|
564 |
- $result->filter(issue_date => 'date_to_tp', first_issue_date => 'date_to_tp'); |
|
631 |
+ $result->filter(issue_year => 'year_to_tp'); |
|
565 | 632 |
|
566 |
-これは、C<apply_filter()>のフィルタルールのinで定義されたフィルタを上書きます。 |
|
633 |
+頻繁に利用するのであれば、個別に登録するよりもC<apply_filter()>で登録 |
|
634 |
+しておいたほうが便利でしょう。C<apply_filter()>は存在しない列に対しても |
|
635 |
+フィルタを適用できるからです。 |
|
636 |
+ |
|
637 |
+ $dbi->apply_filter('book', |
|
638 |
+ 'issue_year' => {out => 'tp_to_year', in => 'year_to_tp'} |
|
639 |
+ ); |
|
640 |
+ |
|
641 |
+C<DBIx::Custom::Result>ではさらに最後にもう一度、フィルタを追加で |
|
642 |
+登録することができます。たとえばHTMLに出力したい場合に、Time::Piece |
|
643 |
+オブジェクトから読みやすい記述に変換することができます。 |
|
644 |
+最後のフィルタを登録するには、C<end_filter()>を使用します。 |
|
645 |
+ |
|
646 |
+ $result->end_filter(issue_date => sub { |
|
647 |
+ my $tp = shift; |
|
648 |
+ |
|
649 |
+ return '' unless $tp; |
|
650 |
+ return $tp->strftime('%Y/%m/%d %h:%m:%s (%a)'); |
|
651 |
+ }); |
|
652 |
+ |
|
653 |
+日付を見やすい形にフォーマットすることができます。 |
|
654 |
+ |
|
655 |
+フィルタはフェッチを行う前に登録しておく必要があることに |
|
656 |
+注意してください。 |
|
657 |
+ |
|
658 |
+ $result->filter(...); |
|
659 |
+ $result->end_filter(...); |
|
660 |
+ my $row = $result->fetch_hash_first; |
|
661 |
+ |
|
662 |
+=head3 列の情報を元にフィルタを適用する |
|
663 |
+ |
|
664 |
+日付型の列は手動で設定しなくても、自動的に設定できると便利です。 |
|
665 |
+このためにデータベースのテーブルの列のすべての情報を |
|
666 |
+順番に処理するためのC<each_column()>があります。 |
|
667 |
+ |
|
668 |
+ $dbi->each_column( |
|
669 |
+ sub { |
|
670 |
+ my ($self, $table, $column, $info) = @_; |
|
671 |
+ |
|
672 |
+ my $type = $info->{TYPE_NAME}; |
|
673 |
+ |
|
674 |
+ my $filter = $type eq 'DATE' ? {out => 'tp_to_date', in => 'date_to_tp'} |
|
675 |
+ : $type eq 'DATETIME' ? {out => 'tp_to_datetime', in => 'datetime_to_tp'} |
|
676 |
+ : undef; |
|
677 |
+ |
|
678 |
+ $self->apply_filter($table, $column, $filter) |
|
679 |
+ if $filter; |
|
680 |
+ } |
|
681 |
+ ); |
|
682 |
+ |
|
683 |
+each_columnはコールバックを受け取ります。コールバックの引数は |
|
684 |
+順番にL<DBIx::Custom>オブジェクト、テーブル名、列名、列の情報です。 |
|
685 |
+列の型名の情報をもとに自動的に、フィルタを適用しています。 |
|
686 |
+ |
|
687 |
+ひとつの注意点としてコールバックの中から、コールバックの外側 |
|
688 |
+の変数を参照しないように注意してください。each_columnは |
|
689 |
+高々1回だけ実行されるだけなので、ほとんどの場合問題ありませんが、 |
|
690 |
+循環参照によるメモリリークが発生してしまう可能性を持っているからです。 |
|
567 | 691 |
|
568 | 692 |
=head2 5. タグ |
569 | 693 |
|
... | ... |
@@ -630,7 +754,21 @@ C<{>とC<}>は予約語です。これらの文字を使いたい場合は |
630 | 754 |
|
631 | 755 |
'select * from book \\{ something statement \\}' |
632 | 756 |
|
633 |
-=head2 6. パフォーマンスの改善 |
|
757 |
+=head3 L<DBIx::Custom::QueryBuilder>の機能の拡張 |
|
758 |
+ |
|
759 |
+新しいタグが欲しい場合はL<DBIx::Custom::QueryBuilder>の機能を拡張 |
|
760 |
+することができます。 |
|
761 |
+ |
|
762 |
+ my $dbi = DBIx::Custom->connect(...); |
|
763 |
+ $dbi->register_tag( |
|
764 |
+ name => sub { |
|
765 |
+ ... |
|
766 |
+ } |
|
767 |
+ ); |
|
768 |
+ |
|
769 |
+=head2 6. Where句の動的な生成 |
|
770 |
+ |
|
771 |
+=head2 7. パフォーマンスの改善 |
|
634 | 772 |
|
635 | 773 |
=head3 シュガーメソッドを使わない |
636 | 774 |
|
... | ... |
@@ -670,7 +808,7 @@ C<insert()>メソッドは、SQL文とステートメントハンドルを |
670 | 808 |
C<execute>メソッドの第一引数にクエリオブジェトを渡すことができます。 |
671 | 809 |
これはC<insert()>メソッドよりも高速です。 |
672 | 810 |
|
673 |
-=head2 7. その他の機能 |
|
811 |
+=head2 8. その他の機能 |
|
674 | 812 |
|
675 | 813 |
=head3 トランザクション |
676 | 814 |
|
... | ... |
@@ -711,18 +849,6 @@ fc |
711 | 849 |
my $dbi = DBIx::Custom->connect(...); |
712 | 850 |
$dbi->result_class('Your::Result'); |
713 | 851 |
|
714 |
-=head3 L<DBIx::Custom::QueryBuilder>の機能の拡張 |
|
715 |
- |
|
716 |
-新しいタグが欲しい場合はL<DBIx::Custom::QueryBuilder>の機能を拡張 |
|
717 |
-することができます。 |
|
718 |
- |
|
719 |
- my $dbi = DBIx::Custom->connect(...); |
|
720 |
- $dbi->query_builder->register_tag_processor( |
|
721 |
- name => sub { |
|
722 |
- ... |
|
723 |
- } |
|
724 |
- ); |
|
725 |
- |
|
726 | 852 |
=head3 ヘルパーメソッドの登録 |
727 | 853 |
|
728 | 854 |
ヘルパーメソッドを登録することができます。 |
... | ... |
@@ -650,7 +650,7 @@ $dbi->execute($CREATE_TABLE->{3}); |
650 | 650 |
|
651 | 651 |
$infos = []; |
652 | 652 |
$dbi->each_column(sub { |
653 |
- my ($table, $column, $cinfo) = @_; |
|
653 |
+ my ($self, $table, $column, $cinfo) = @_; |
|
654 | 654 |
|
655 | 655 |
if ($table =~ /^table/) { |
656 | 656 |
my $info = [$table, $column, $cinfo->{COLUMN_NAME}]; |
... | ... |
@@ -1,52 +0,0 @@ |
1 |
-use Test::More; |
|
2 |
- |
|
3 |
-eval {require DBIx::TransactionManager; 1} |
|
4 |
- or plan skip_all => 'required DBIx::TransactionManager'; |
|
5 |
- |
|
6 |
-plan 'no_plan'; |
|
7 |
- |
|
8 |
-use DBIx::Custom; |
|
9 |
- |
|
10 |
-# Function for test name |
|
11 |
-sub test { "# $_[0]\n" } |
|
12 |
- |
|
13 |
-# Constant varialbes for test |
|
14 |
-my $CREATE_TABLE = { |
|
15 |
- 0 => 'create table table1 (key1 char(255), key2 char(255));', |
|
16 |
-}; |
|
17 |
- |
|
18 |
-my $NEW_ARGS = { |
|
19 |
- 0 => {data_source => 'dbi:SQLite:dbname=:memory:'} |
|
20 |
-}; |
|
21 |
- |
|
22 |
-# Variables |
|
23 |
-my $dbi; |
|
24 |
-my $result; |
|
25 |
-my $txn; |
|
26 |
- |
|
27 |
-test 'transaction'; |
|
28 |
-$dbi = DBIx::Custom->connect($NEW_ARGS->{0}); |
|
29 |
-$dbi->execute($CREATE_TABLE->{0}); |
|
30 |
-{ |
|
31 |
- my $txn = $dbi->txn_scope; |
|
32 |
- $dbi->insert(table => 'table1', param => {key1 => 1, key2 => 2}); |
|
33 |
- $dbi->insert(table => 'table1', param => {key1 => 2, key2 => 3}); |
|
34 |
- $txn->commit; |
|
35 |
-} |
|
36 |
-$result = $dbi->select(table => 'table1'); |
|
37 |
-is_deeply(scalar $result->fetch_hash_all, [{key1 => 1, key2 => 2}, {key1 => 2, key2 => 3}], |
|
38 |
- "commit"); |
|
39 |
- |
|
40 |
-$dbi = DBIx::Custom->connect($NEW_ARGS->{0}); |
|
41 |
-$dbi->execute($CREATE_TABLE->{0}); |
|
42 |
- |
|
43 |
-{ |
|
44 |
- local $SIG{__WARN__} = sub {}; |
|
45 |
- { |
|
46 |
- my $txn = $dbi->txn_scope; |
|
47 |
- $dbi->insert(table => 'table1', param => {key1 => 1, key2 => 2}); |
|
48 |
- } |
|
49 |
-} |
|
50 |
-$result = $dbi->select(table => 'table1'); |
|
51 |
-ok(! $result->fetch_first, "rollback"); |
|
52 |
- |