| ... | ... |
@@ -12,12 +12,34 @@ L<DBI>のラッパクラスになっています。 |
| 12 | 12 |
|
| 13 | 13 |
L<DBIx::Custom>はO/Rマッパーではありません。O/Rマッパーは |
| 14 | 14 |
便利ですが、O/Rマッパのたくさんの文法を覚える必要があります。 |
| 15 |
-また、O/Rマッパによって生成されたSQLは非効率なことがあり、 |
|
| 16 |
-生のSQLを発行しなければならないこともあります。 |
|
| 15 |
+また、O/Rマッパによって生成されたSQLは非効率なことがありますし、 |
|
| 16 |
+複雑なSQLを生成することができないので、 |
|
| 17 |
+生のSQLを発行しなければならない場合がたくさんあります。 |
|
| 17 | 18 |
|
| 18 |
-L<DBIx::Custom>はO/RマッパとL<DBI>の中間に位置するモジュールです。 |
|
| 19 |
-L<DBIx::Custom>は柔軟なハッシュパラメータバインディングとフィルタリング |
|
| 20 |
-のシステムを提供します。またSQLを簡単に実行するための、 |
|
| 19 |
+L<DBIx::Custom>はO/Rマッパとは対照的な設計が行われています。 |
|
| 20 |
+L<DBIx::Custom>の主な目的は、SQLを尊重しつつ、DBIだけでは |
|
| 21 |
+とてもめんどうな作業を簡単にすることです。もしSQLについて |
|
| 22 |
+多くの知識を持っているならば、L<DBIx::Custom>でそのまま |
|
| 23 |
+生かすことができます。 |
|
| 24 |
+ |
|
| 25 |
+L<DBIx::Custom>の仕組みを簡単に説明しておきましょう。 |
|
| 26 |
+L<DBIx::Custom>では、タグと呼ばれるものを |
|
| 27 |
+SQLの中に埋め込むことができます。 |
|
| 28 |
+ |
|
| 29 |
+ select * from book where {= title} and {=author};
|
|
| 30 |
+ |
|
| 31 |
+{}で囲まれた部分がタグです。このSQLは実際に実行されるときには
|
|
| 32 |
+次のようにプレースホルダに展開されます。 |
|
| 33 |
+ |
|
| 34 |
+ select * from book where title = ? and title = ?; |
|
| 35 |
+ |
|
| 36 |
+これらの展開にはどのような意味があるのでしょうかと質問 |
|
| 37 |
+されることかと思います。 |
|
| 38 |
+ |
|
| 39 |
+ |
|
| 40 |
+ |
|
| 41 |
+ |
|
| 42 |
+またSQLを簡単に実行するための、 |
|
| 21 | 43 |
C<insert()>, C<update()>, C<delete()>,C<select()>などの |
| 22 | 44 |
シュガーメソッドも提供します。 |
| 23 | 45 |
|
| ... | ... |
@@ -216,424 +238,4 @@ C<delete()>、C<select>メソッドで使用することが |
| 216 | 238 |
$dbi->insert(table => 'book', |
| 217 | 239 |
param => {title => 'Perl', author => 'Ken'});
|
| 218 | 240 |
filter => {title => 'encode_utf8',
|
| 219 |
- author => 'encode_utf8'}); |
|
| 220 |
- |
|
| 221 |
-C<filter>オプションは、C<insert()>、C<update()>、C<update_all()> |
|
| 222 |
-C<delete()>、C<select>メソッドで使用することが |
|
| 223 |
-できます。 |
|
| 224 |
- |
|
| 225 |
-C<select()>メソッドのC<where>オプションではハッシュの代わりに |
|
| 226 |
-タグを利用することもできます。これによって柔軟な |
|
| 227 |
-条件を指定することができます。 |
|
| 228 |
- |
|
| 229 |
- # Select, more flexible where |
|
| 230 |
- my $result = $dbi->select( |
|
| 231 |
- table => 'book', |
|
| 232 |
- where => ['{= author} and {like title}',
|
|
| 233 |
- {author => 'Ken', title => '%Perl%'}]
|
|
| 234 |
- ); |
|
| 235 |
- |
|
| 236 |
-タグについては以降で解説します。 |
|
| 237 |
- |
|
| 238 |
-=head2 3. 行のフェッチ |
|
| 239 |
- |
|
| 240 |
-C<select()>メソッドの戻り値であるL<DBIx::Custom::Result> |
|
| 241 |
-には行をフェッチするためのさまざまなメソッドが |
|
| 242 |
-用意されています。 |
|
| 243 |
-(このセクションの解説では「配列」は「配列のリファレンス」を |
|
| 244 |
-「ハッシュ」は「ハッシュのリファレンス」を意味しますので |
|
| 245 |
-注意してください。) |
|
| 246 |
- |
|
| 247 |
-=head3 C<fetch> |
|
| 248 |
- |
|
| 249 |
-一行フェッチして配列に格納します。 |
|
| 250 |
- |
|
| 251 |
- while (my $row = $result->fetch) {
|
|
| 252 |
- my $author = $row->[0]; |
|
| 253 |
- my $title = $row->[1]; |
|
| 254 |
- } |
|
| 255 |
- |
|
| 256 |
-=head3 C<fetch_first> |
|
| 257 |
- |
|
| 258 |
-一行だけフェッチして配列に格納します。 |
|
| 259 |
- |
|
| 260 |
- my $row = $result->fetch_first; |
|
| 261 |
- |
|
| 262 |
-フェッチが終わった後は、ステートメントハンドルからC<finish()> |
|
| 263 |
-メソッドが呼び出されてそれ以上フェッチできなくなります。 |
|
| 264 |
- |
|
| 265 |
-=head3 C<fetch_multi> |
|
| 266 |
- |
|
| 267 |
-複数行をフェッチして配列の配列に格納します。 |
|
| 268 |
- |
|
| 269 |
- while (my $rows = $result->fetch_multi(5)) {
|
|
| 270 |
- my $first_author = $rows->[0][0]; |
|
| 271 |
- my $first_title = $rows->[0][1]; |
|
| 272 |
- my $second_author = $rows->[1][0]; |
|
| 273 |
- my $second_value = $rows->[1][1]; |
|
| 274 |
- } |
|
| 275 |
- |
|
| 276 |
-=head3 C<fetch_all> |
|
| 277 |
- |
|
| 278 |
-すべての行をフェッチして配列の配列に格納します。 |
|
| 279 |
- |
|
| 280 |
- my $rows = $result->fetch_all; |
|
| 281 |
- |
|
| 282 |
-=head3 C<fetch_hash> |
|
| 283 |
- |
|
| 284 |
-一行フェッチしてハッシュに格納します。 |
|
| 285 |
- |
|
| 286 |
- while (my $row = $result->fetch_hash) {
|
|
| 287 |
- my $title = $row->{title};
|
|
| 288 |
- my $author = $row->{author};
|
|
| 289 |
- } |
|
| 290 |
- |
|
| 291 |
-=head3 C<fetch_hash_first> |
|
| 292 |
- |
|
| 293 |
-一行だけフェッチしてハッシュに格納します。 |
|
| 294 |
- |
|
| 295 |
- my $row = $result->fetch_hash_first; |
|
| 296 |
- |
|
| 297 |
-フェッチが終わった後は、ステートメントハンドルからC<finish()> |
|
| 298 |
-メソッドが呼び出されてそれ以上フェッチできなくなります。 |
|
| 299 |
- |
|
| 300 |
-=head3 C<fetch_hash_multi> |
|
| 301 |
- |
|
| 302 |
-複数行をフェッチしてハッシュの配列に格納します。 |
|
| 303 |
- |
|
| 304 |
- while (my $rows = $result->fetch_hash_multi(5)) {
|
|
| 305 |
- my $first_title = $rows->[0]{title};
|
|
| 306 |
- my $first_author = $rows->[0]{author};
|
|
| 307 |
- my $second_title = $rows->[1]{title};
|
|
| 308 |
- my $second_author = $rows->[1]{author};
|
|
| 309 |
- } |
|
| 310 |
- |
|
| 311 |
-=head3 C<fetch_all> |
|
| 312 |
- |
|
| 313 |
-すべての行をフェッチしてハッシュの配列に格納します。 |
|
| 314 |
- |
|
| 315 |
- my $rows = $result->fetch_hash_all; |
|
| 316 |
- |
|
| 317 |
-L<DBI>のステートメントハンドルに直接アクセスしたい場合は |
|
| 318 |
-<sth>を使用します。 |
|
| 319 |
- |
|
| 320 |
- my $sth = $result->sth; |
|
| 321 |
- |
|
| 322 |
-=head2 4. ハッシュパラメタバインド |
|
| 323 |
- |
|
| 324 |
-L<DBIx::Custom>はハッシュパラメタバインドを提供します。 |
|
| 325 |
- |
|
| 326 |
-まず最初にL<DBI>による通常のパラメタバインドをご覧ください。 |
|
| 327 |
- |
|
| 328 |
- use DBI; |
|
| 329 |
- my $dbh = DBI->connect(...); |
|
| 330 |
- my $sth = $dbh->prepare( |
|
| 331 |
- "select * from book where author = ? and title like ?;" |
|
| 332 |
- ); |
|
| 333 |
- $sth->execute('Ken', '%Perl%');
|
|
| 334 |
- |
|
| 335 |
-これはデータベースシステムがSQLをキャッシュすることができ、 |
|
| 336 |
-パラメータは自動的にクォートされるので、 |
|
| 337 |
-パフォーマンス面でも、セキュリティ面でも |
|
| 338 |
-とても良い方法です。 |
|
| 339 |
- |
|
| 340 |
- |
|
| 341 |
-L<DBIx::Custom>はこれを改善して、ハッシュで |
|
| 342 |
-パラメタを指定できるようにしました。 |
|
| 343 |
- |
|
| 344 |
- my $result = $dbi->execute( |
|
| 345 |
- "select * from book where {= author} and {like title};"
|
|
| 346 |
- param => {author => 'Ken', title => '%Perl%'}
|
|
| 347 |
- ); |
|
| 348 |
- |
|
| 349 |
-C<{= author}>とC<{like title}>はタグと呼ばれます。
|
|
| 350 |
-タグは内部ではプレースホルダを含む文字列に置き換えられます。 |
|
| 351 |
- |
|
| 352 |
- select * from book where {= author} and {like title}
|
|
| 353 |
- |
|
| 354 |
-という文は以下のSQLに置き換えられます。 |
|
| 355 |
- |
|
| 356 |
- select * from book where author = ? and title like ?; |
|
| 357 |
- |
|
| 358 |
-このようにタグを使ってSQL文を表現するのがL<DBIx::Custom>の |
|
| 359 |
-特徴です。以下のタグが利用可能です。 |
|
| 360 |
- |
|
| 361 |
- [TAG] [REPLACED] |
|
| 362 |
- {? NAME} -> ?
|
|
| 363 |
- {= NAME} -> NAME = ?
|
|
| 364 |
- {<> NAME} -> NAME <> ?
|
|
| 365 |
- |
|
| 366 |
- {< NAME} -> NAME < ?
|
|
| 367 |
- {> NAME} -> NAME > ?
|
|
| 368 |
- {>= NAME} -> NAME >= ?
|
|
| 369 |
- {<= NAME} -> NAME <= ?
|
|
| 370 |
- |
|
| 371 |
- {like NAME} -> NAME like ?
|
|
| 372 |
- {in NAME COUNT} -> NAME in [?, ?, ..]
|
|
| 373 |
- |
|
| 374 |
- {insert_param NAME1 NAME2} -> (NAME1, NAME2) values (?, ?)
|
|
| 375 |
- {update_param NAME1 NAME2} -> set NAME1 = ?, NAME2 = ?
|
|
| 376 |
- |
|
| 377 |
-これらの変換はL<DBIx::Custom::QueryBuilder>によって行われます。 |
|
| 378 |
- |
|
| 379 |
-C<{>とC<}>は予約語です。これらの文字を使いたい場合は
|
|
| 380 |
-「\」を使ってエスケープする必要があります。 |
|
| 381 |
-'\'はPerlのエスケープ文字なので、 |
|
| 382 |
-エスケープするためには'\\'と書く必要があることに注意 |
|
| 383 |
-してください。 |
|
| 384 |
- |
|
| 385 |
- 'select * from book \\{ something statement \\}'
|
|
| 386 |
- |
|
| 387 |
-=head2 5. フィルタリング |
|
| 388 |
- |
|
| 389 |
-=head3 パラメタバインド時のフィルタリング |
|
| 390 |
- |
|
| 391 |
-データベースに登録するデータをフィルタリングしたい場合 |
|
| 392 |
-があります。たとえば、内部文字列で文字列を保持している場合は |
|
| 393 |
-データベースにデータを登録する前に、バイト文字列に変換する |
|
| 394 |
-必要があります。L<DBIx::Custom>のフィルタリングシステムは |
|
| 395 |
-あるデータを他のデータに変換するのを手助けしてくれます。 |
|
| 396 |
- |
|
| 397 |
-フィルタリングを利用するにはまず、 |
|
| 398 |
-C<register_filter()>メソッドを使用して |
|
| 399 |
-フィルタを登録しておく必要があります。 |
|
| 400 |
- |
|
| 401 |
- $dbi->register_filter( |
|
| 402 |
- to_upper_case => sub {
|
|
| 403 |
- my $value = shift; |
|
| 404 |
- return uc $value; |
|
| 405 |
- } |
|
| 406 |
- ); |
|
| 407 |
- |
|
| 408 |
-デフォルトのフィルタとしてC<encode_utf8>とC<decode_utf8> |
|
| 409 |
-が登録されています。 |
|
| 410 |
- |
|
| 411 |
-登録されているフィルタはC<execute()>メソッドのC<filter>オプション |
|
| 412 |
-で指定することができます。 |
|
| 413 |
- |
|
| 414 |
- my $result = $dbi->execute( |
|
| 415 |
- "select * from book where {= author} and {like title};"
|
|
| 416 |
- param => {author => 'Ken', title => '%Perl%'},
|
|
| 417 |
- filter => {author => 'to_upper_case, title => 'encode_utf8'}
|
|
| 418 |
- ); |
|
| 419 |
- |
|
| 420 |
-この例ではC<author>の値はバインドされるときに大文字に変換され、 |
|
| 421 |
-C<title>の値はバイト文字列に変換されます。 |
|
| 422 |
- |
|
| 423 |
-C<filter>オプションは |
|
| 424 |
-C<insert()>、C<update()>、 C<update_all()>, |
|
| 425 |
-C<delete()>、C<select()> |
|
| 426 |
-メソッドにおいても使用することができます。 |
|
| 427 |
- |
|
| 428 |
- # insert() with filter option |
|
| 429 |
- $dbi->insert(table => 'book', |
|
| 430 |
- param => {title => 'Perl', author => 'Ken'},
|
|
| 431 |
- filter => {title => 'encode_utf8'});
|
|
| 432 |
- |
|
| 433 |
- # select() with filter option |
|
| 434 |
- my $result = $dbi->select( |
|
| 435 |
- table => 'book', |
|
| 436 |
- column => [qw/author title/], |
|
| 437 |
- where => {author => 'Ken'},
|
|
| 438 |
- append => 'order by id limit 1', |
|
| 439 |
- filter => {title => 'encode_utf8'}
|
|
| 440 |
- ); |
|
| 441 |
- |
|
| 442 |
-B<フィルタのサンプル> |
|
| 443 |
- |
|
| 444 |
-MySQL |
|
| 445 |
- |
|
| 446 |
- # Time::Piece object to DATETIME format |
|
| 447 |
- tp_to_datetime => sub {
|
|
| 448 |
- return shift->strftime('%Y-%m-%d %H:%M:%S');
|
|
| 449 |
- } |
|
| 450 |
- |
|
| 451 |
- # Time::Piece object to DATE format |
|
| 452 |
- tp_to_date => sub {
|
|
| 453 |
- return shift->strftime('%Y-%m-%d');
|
|
| 454 |
- } |
|
| 455 |
- |
|
| 456 |
- # DATETIME to Time::Piece object |
|
| 457 |
- datetime_to_tp => sub {
|
|
| 458 |
- return Time::Piece->strptime(shift, '%Y-%m-%d %H:%M:%S'); |
|
| 459 |
- } |
|
| 460 |
- |
|
| 461 |
- # DATE to Time::Piece object |
|
| 462 |
- date_to_tp => sub {
|
|
| 463 |
- return Time::Piece->strptime(shift, '%Y-%m-%d'); |
|
| 464 |
- } |
|
| 465 |
- |
|
| 466 |
-SQLite |
|
| 467 |
- |
|
| 468 |
- # Time::Piece object to DATETIME format |
|
| 469 |
- tp_to_datetime => sub {
|
|
| 470 |
- return shift->strftime('%Y-%m-%d %H:%M:%S');
|
|
| 471 |
- } |
|
| 472 |
- |
|
| 473 |
- # Time::Piece object to DATE format |
|
| 474 |
- tp_to_date => sub {
|
|
| 475 |
- return shift->strftime('%Y-%m-%d');
|
|
| 476 |
- } |
|
| 477 |
- |
|
| 478 |
- # DATETIME to Time::Piece object |
|
| 479 |
- datetime_to_tp => sub {
|
|
| 480 |
- return Time::Piece->strptime(shift, $FORMATS->{db_datetime});
|
|
| 481 |
- } |
|
| 482 |
- |
|
| 483 |
- # DATE to Time::Piece object |
|
| 484 |
- date_to_tp => sub {
|
|
| 485 |
- return Time::Piece->strptime(shift, $FORMATS->{db_date});
|
|
| 486 |
- } |
|
| 487 |
- |
|
| 488 |
-=head3 行のフェッチ時のフィルタリング |
|
| 489 |
- |
|
| 490 |
-行をフェッチするときのフィルタも設定することができます。 |
|
| 491 |
-これはL<DBIx::Custom::Result>クラスのC<filter>メソッドを使って |
|
| 492 |
-行います。 |
|
| 493 |
- |
|
| 494 |
- my $result = $dbi->select(table => 'book'); |
|
| 495 |
- $result->filter({title => 'decode_utf8', author => 'to_upper_case'});
|
|
| 496 |
- |
|
| 497 |
-フェッチのためのフィルタにおいて、 |
|
| 498 |
-たとえ、列名が大文字を含む場合であっても |
|
| 499 |
-列名は小文字であることに注意してください。 |
|
| 500 |
-これはデータベースシステムに依存させないための要件です。 |
|
| 501 |
- |
|
| 502 |
-=head2 6. パフォーマンスの改善 |
|
| 503 |
- |
|
| 504 |
-=head3 シュガーメソッドを使わない |
|
| 505 |
- |
|
| 506 |
-もしC<insert()>メソッドを使用してインサートを実行した場合、 |
|
| 507 |
-必要なパフォーマンスを得られない場合があるかもしれません。 |
|
| 508 |
-C<insert()>メソッドは、SQL文とステートメントハンドルを |
|
| 509 |
-毎回作成するためすこし遅いです。 |
|
| 510 |
- |
|
| 511 |
-そのような場合は、C<create_query()>メソッドによって |
|
| 512 |
-クエリを用意しておくことができます。 |
|
| 513 |
- |
|
| 514 |
- my $query = $dbi->create_query( |
|
| 515 |
- "insert into book {insert_param title author};"
|
|
| 516 |
- ); |
|
| 517 |
- |
|
| 518 |
-戻り値はL<DBIx::Custom::Query>オブジェクトです。 |
|
| 519 |
-このオブジェクトはSQL文とパラメータバインド時の列名を |
|
| 520 |
-保持しています。またステートメントハンドルも保持しています。 |
|
| 521 |
- |
|
| 522 |
- {
|
|
| 523 |
- sql => 'insert into book (title, author) values (?, ?);', |
|
| 524 |
- columns => ['title', 'author'], |
|
| 525 |
- sth => $sth |
|
| 526 |
- } |
|
| 527 |
- |
|
| 528 |
-クエリオブジェクトを使って繰り返し実行するには次のようにします。 |
|
| 529 |
- |
|
| 530 |
- my $inputs = [ |
|
| 531 |
- {title => 'Perl', author => 'Ken'},
|
|
| 532 |
- {title => 'Good days', author => 'Mike'}
|
|
| 533 |
- ]; |
|
| 534 |
- |
|
| 535 |
- foreach my $input (@$inputs) {
|
|
| 536 |
- $dbi->execute($query, $input); |
|
| 537 |
- } |
|
| 538 |
- |
|
| 539 |
-C<execute>メソッドの第一引数にクエリオブジェトを渡すことができます。 |
|
| 540 |
-これはC<insert()>メソッドよりも高速です。 |
|
| 541 |
- |
|
| 542 |
-=head2 7. その他の機能 |
|
| 543 |
- |
|
| 544 |
-=head3 トランザクション |
|
| 545 |
- |
|
| 546 |
-トランザクションを便利に利用するために、 |
|
| 547 |
-C<begin_work()>、C<commit()>、C<rollback()> |
|
| 548 |
-という三つのメソッドが容易されています。 |
|
| 549 |
-これはL<DBI>の同名のメソッドと同じ機能を持ちます。 |
|
| 550 |
- |
|
| 551 |
- $dbi->begin_work; |
|
| 552 |
- |
|
| 553 |
- eval {
|
|
| 554 |
- $dbi->update(...); |
|
| 555 |
- $dbi->update(...); |
|
| 556 |
- }; |
|
| 557 |
- |
|
| 558 |
- if ($@) {
|
|
| 559 |
- $dbi->rollback; |
|
| 560 |
- } |
|
| 561 |
- else {
|
|
| 562 |
- $dbi->commit; |
|
| 563 |
- } |
|
| 564 |
- |
|
| 565 |
-=head3 selectメソッドの結果クラスの変更 |
|
| 566 |
- |
|
| 567 |
-必要ならばC<select()>メソッドの結果クラスを変更することができます。 |
|
| 568 |
- |
|
| 569 |
- package Your::Result; |
|
| 570 |
- use base 'DBIx::Custom::Result'; |
|
| 571 |
- |
|
| 572 |
- sub some_method { ... }
|
|
| 573 |
- |
|
| 574 |
- 1; |
|
| 575 |
- |
|
| 576 |
- package main; |
|
| 577 |
- |
|
| 578 |
- use Your::Result; |
|
| 579 |
- |
|
| 580 |
- my $dbi = DBIx::Custom->connect(...); |
|
| 581 |
- $dbi->result_class('Your::Result');
|
|
| 582 |
- |
|
| 583 |
-=head3 L<DBIx::Custom::QueryBuilder>の機能の拡張 |
|
| 584 |
- |
|
| 585 |
-新しいタグが欲しい場合はL<DBIx::Custom::QueryBuilder>の機能を拡張 |
|
| 586 |
-することができます。 |
|
| 587 |
- |
|
| 588 |
- my $dbi = DBIx::Custom->connect(...); |
|
| 589 |
- $dbi->query_builder->register_tag_processor( |
|
| 590 |
- name => sub {
|
|
| 591 |
- ... |
|
| 592 |
- } |
|
| 593 |
- ); |
|
| 594 |
- |
|
| 595 |
-=head3 ヘルパーメソッドの登録 |
|
| 596 |
- |
|
| 597 |
-ヘルパーメソッドを登録することができます。 |
|
| 598 |
- |
|
| 599 |
- $dbi->helper( |
|
| 600 |
- update_or_insert => sub {
|
|
| 601 |
- my $self = shift; |
|
| 602 |
- # do something |
|
| 603 |
- }, |
|
| 604 |
- find_or_create => sub {
|
|
| 605 |
- my $self = shift; |
|
| 606 |
- # do something |
|
| 607 |
- } |
|
| 608 |
- ); |
|
| 609 |
- |
|
| 610 |
-<helper()>メソッドで登録したメソッドは |
|
| 611 |
-L<DBIx::Custom>オブジェクトから直接呼び出すことができます。 |
|
| 612 |
- |
|
| 613 |
- $dbi->update_or_insert; |
|
| 614 |
- $dbi->find_or_create; |
|
| 615 |
- |
|
| 616 |
-=head3 ユーティリティメソッド(実験的) |
|
| 617 |
- |
|
| 618 |
-C<expand>メソッドを使用すると次のようなハッシュに含まれる |
|
| 619 |
-テーブル名と列名を結合することができます。 |
|
| 620 |
- |
|
| 621 |
- my %expanded = $dbi->expand(\%source); |
|
| 622 |
- |
|
| 623 |
-以下のハッシュ |
|
| 624 |
- |
|
| 625 |
- {book => {title => 'Perl', author => 'Ken'}}
|
|
| 626 |
- |
|
| 627 |
-は次のように展開されます。 |
|
| 628 |
- |
|
| 629 |
- ('book.title' => 'Perl', 'book.author' => 'Ken')
|
|
| 630 |
- |
|
| 631 |
-これはテーブル名を含むselect文で利用すると便利です。 |
|
| 632 |
- |
|
| 633 |
- my $param = {title => 'Perl', author => '%Ken%'};
|
|
| 634 |
- $dbi->execute( |
|
| 635 |
- 'select * from book where {= book.title} && {like book.author};',
|
|
| 636 |
- param => {$dbi->expand({book => $param})}
|
|
| 637 |
- ); |
|
| 638 |
- |
|
| 639 |
-=cut |
|
| 241 |
+ a |