| ... | ... |
@@ -10,6 +10,9 @@ my $builder = Module::Build->new( |
| 10 | 10 |
build_requires => {
|
| 11 | 11 |
'Test::More' => 0, |
| 12 | 12 |
}, |
| 13 |
+ requires => {
|
|
| 14 |
+ 'Object::Simple' => 0.0602, |
|
| 15 |
+ }, |
|
| 13 | 16 |
add_to_cleanup => [ 'DBI-Custom-*' ], |
| 14 | 17 |
create_makefile_pl => 'traditional', |
| 15 | 18 |
); |
| ... | ... |
@@ -5,64 +5,62 @@ our $VERSION = '0.0101'; |
| 5 | 5 |
|
| 6 | 6 |
use Carp 'croak'; |
| 7 | 7 |
use DBI; |
| 8 |
+use DBI::Custom::SQL::Template; |
|
| 9 |
+ |
|
| 10 |
+### Class-Object Accessors |
|
| 11 |
+sub connect_info : ClassObjectAttr { type => 'hash', auto_build => sub {
|
|
| 12 |
+ shift->Object::Simple::initialize_class_object_attr( |
|
| 13 |
+ default => sub { {} }, clone => sub {
|
|
| 14 |
+ my $value = shift; |
|
| 15 |
+ my $new_value = \%{$value || {}};
|
|
| 16 |
+ $new_value->{options} = $value->{options} if $value->{options};
|
|
| 17 |
+ return $new_value; |
|
| 18 |
+ } |
|
| 19 |
+ ) |
|
| 20 |
+}} |
|
| 8 | 21 |
|
| 9 |
-# Model |
|
| 10 |
-sub prototype : ClassAttr { auto_build => sub {
|
|
| 11 |
- my $class = shift; |
|
| 12 |
- my $super = do {
|
|
| 13 |
- no strict 'refs'; |
|
| 14 |
- ${"${class}::ISA"}[0];
|
|
| 15 |
- }; |
|
| 16 |
- my $prototype = eval{$super->can('prototype')}
|
|
| 17 |
- ? $super->prototype->clone |
|
| 18 |
- : $class->Object::Simple::new; |
|
| 19 |
- |
|
| 20 |
- $class->prototype(bless $prototype, $class); |
|
| 22 |
+sub bind_filter : ClassObjectAttr { auto_build => sub {
|
|
| 23 |
+ shift->Object::Simple::initialize_class_object_attr(clone => 'scalar') |
|
| 24 |
+}} |
|
| 25 |
+sub fetch_filter : ClassObjectAttr { auto_build => sub {
|
|
| 26 |
+ shift->Object::Simple::initialize_class_object_attr(clone => 'scalar') |
|
| 21 | 27 |
}} |
| 22 | 28 |
|
| 23 |
-# New |
|
| 24 |
-sub new {
|
|
| 25 |
- my $invocant = shift; |
|
| 26 |
- my $class = ref $invocant || $invocant; |
|
| 27 |
- my $prototype = $class->prototype; |
|
| 28 |
- my $self = $class->Object::Simple::new(%{$prototype->clone}, @_);
|
|
| 29 |
- return bless $self, $class; |
|
| 30 |
-} |
|
| 29 |
+sub filters : ClassObjectAttr { type => 'hash', deref => 1, auto_build => sub {
|
|
| 30 |
+ shift->Object::Simple::initialize_class_object_attr(clone => 'hash') |
|
| 31 |
+}} |
|
| 31 | 32 |
|
| 32 |
-# Clone |
|
| 33 |
-sub clone {
|
|
| 34 |
- my $self = shift; |
|
| 35 |
- my $new = $self->Object::Simple::new; |
|
| 36 |
- |
|
| 37 |
- # Scalar copy |
|
| 38 |
- foreach my $attr (qw/bind_filter fetch_filter result_class/) {
|
|
| 39 |
- $new->$attr($self->$attr); |
|
| 40 |
- } |
|
| 41 |
- |
|
| 42 |
- # Hash ref copy |
|
| 43 |
- foreach my $attr (qw/connect_info filters valid_connect_info/) {
|
|
| 44 |
- $new->$attr(\%{$self->$attr || {}});
|
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- # Other |
|
| 48 |
- $new->connect_info->{options} = \%{$self->connect_info->{options}};
|
|
| 49 |
- $new->sql_template($self->sql_template->clone); |
|
| 50 |
-} |
|
| 33 |
+sub result_class : ClassObjectAttr { auto_build => sub {
|
|
| 34 |
+ shift->Object::Simple::initialize_class_object_attr(clone => 'scalar') |
|
| 35 |
+}} |
|
| 51 | 36 |
|
| 52 |
-# Attribute |
|
| 53 |
-sub connect_info : Attr { type => 'hash', default => sub { {} } }
|
|
| 54 |
-sub bind_filter : Attr {}
|
|
| 55 |
-sub fetch_filter : Attr {}
|
|
| 37 |
+sub sql_template : ClassObjectAttr { auto_build => sub {
|
|
| 38 |
+ shift->Object::Simple::initialize_class_object_attr( |
|
| 39 |
+ clone => sub {my $value = shift; $value ? $value->clone : undef},
|
|
| 40 |
+ default => sub { DBI::Custom::SQL::Template->new }
|
|
| 41 |
+ ) |
|
| 42 |
+}} |
|
| 56 | 43 |
|
| 57 |
-sub filters : Attr { type => 'hash', deref => 1, default => sub { {} } }
|
|
| 58 |
-sub add_filter { shift->filters(@_) }
|
|
| 44 |
+sub valid_connect_info : ClassObjectAttr { type => 'hash', deref => 1, auto_build => sub {
|
|
| 45 |
+ shift->Object::Simple::initialize_class_object_attr( |
|
| 46 |
+ default => sub { return {map {$_ => 1} qw/data_source user password options/} },
|
|
| 47 |
+ clone => 'hash' |
|
| 48 |
+ ) |
|
| 49 |
+}} |
|
| 59 | 50 |
|
| 60 |
-sub result_class : Attr { default => 'DBI::Custom::Result' }
|
|
| 51 |
+### Object Accessor |
|
| 61 | 52 |
sub dbh : Attr {}
|
| 62 |
-sub sql_template : Attr { default => sub { DBI::Custom::SQL::Template->new } }
|
|
| 63 |
-sub valid_connect_info : Attr { type => 'hash', deref => 1, default => sub {
|
|
| 64 |
- return {map {$_ => 1} qw/data_source user password options/}
|
|
| 65 |
-}} |
|
| 53 |
+ |
|
| 54 |
+ |
|
| 55 |
+### Methods |
|
| 56 |
+# Add filter |
|
| 57 |
+sub add_filter {
|
|
| 58 |
+ my $invocant = shift; |
|
| 59 |
+ |
|
| 60 |
+ my %old_filters = $invocant->filters; |
|
| 61 |
+ my %new_filters = ref $_[0] eq 'HASH' ? %{$_[0]} : @_;
|
|
| 62 |
+ $invocant->filters(%old_filters, %new_filters); |
|
| 63 |
+} |
|
| 66 | 64 |
|
| 67 | 65 |
# Auto commit |
| 68 | 66 |
sub auto_commit {
|
| ... | ... |
@@ -104,6 +102,7 @@ sub connect {
|
| 104 | 102 |
return $self; |
| 105 | 103 |
} |
| 106 | 104 |
|
| 105 |
+# DESTROY |
|
| 107 | 106 |
sub DESTROY {
|
| 108 | 107 |
my $self = shift; |
| 109 | 108 |
$self->disconnect if $self->connected; |
| ... | ... |
@@ -223,404 +222,6 @@ sub query_raw_sql {
|
| 223 | 222 |
|
| 224 | 223 |
Object::Simple->build_class; |
| 225 | 224 |
|
| 226 |
- |
|
| 227 |
-package DBI::Custom::Result; |
|
| 228 |
-use Object::Simple; |
|
| 229 |
- |
|
| 230 |
-# Attributes |
|
| 231 |
-sub sth : Attr {}
|
|
| 232 |
-sub fetch_filter : Attr {}
|
|
| 233 |
- |
|
| 234 |
- |
|
| 235 |
-# Fetch (array) |
|
| 236 |
-sub fetch {
|
|
| 237 |
- my ($self, $type) = @_; |
|
| 238 |
- my $sth = $self->sth; |
|
| 239 |
- my $fetch_filter = $self->fetch_filter; |
|
| 240 |
- |
|
| 241 |
- # Fetch |
|
| 242 |
- my $row = $sth->fetchrow_arrayref; |
|
| 243 |
- |
|
| 244 |
- # Cannot fetch |
|
| 245 |
- return unless $row; |
|
| 246 |
- |
|
| 247 |
- # Filter |
|
| 248 |
- if ($fetch_filter) {
|
|
| 249 |
- my $keys = $sth->{NAME_lc};
|
|
| 250 |
- my $types = $sth->{TYPE};
|
|
| 251 |
- for (my $i = 0; $i < @$keys; $i++) {
|
|
| 252 |
- $row->[$i]= $fetch_filter->($keys->[$i], $row->[$i], $types->[$i], |
|
| 253 |
- $sth, $i); |
|
| 254 |
- } |
|
| 255 |
- } |
|
| 256 |
- return wantarray ? @$row : $row; |
|
| 257 |
-} |
|
| 258 |
- |
|
| 259 |
-# Fetch (hash) |
|
| 260 |
-sub fetch_hash {
|
|
| 261 |
- my $self = shift; |
|
| 262 |
- my $sth = $self->sth; |
|
| 263 |
- my $fetch_filter = $self->fetch_filter; |
|
| 264 |
- |
|
| 265 |
- # Fetch |
|
| 266 |
- my $row = $sth->fetchrow_arrayref; |
|
| 267 |
- |
|
| 268 |
- # Cannot fetch |
|
| 269 |
- return unless $row; |
|
| 270 |
- |
|
| 271 |
- # Keys |
|
| 272 |
- my $keys = $sth->{NAME_lc};
|
|
| 273 |
- |
|
| 274 |
- # Filter |
|
| 275 |
- my $row_hash = {};
|
|
| 276 |
- if ($fetch_filter) {
|
|
| 277 |
- my $types = $sth->{TYPE};
|
|
| 278 |
- for (my $i = 0; $i < @$keys; $i++) {
|
|
| 279 |
- $row_hash->{$keys->[$i]} = $fetch_filter->($keys->[$i], $row->[$i],
|
|
| 280 |
- $types->[$i], $sth, $i); |
|
| 281 |
- } |
|
| 282 |
- } |
|
| 283 |
- |
|
| 284 |
- # No filter |
|
| 285 |
- else {
|
|
| 286 |
- for (my $i = 0; $i < @$keys; $i++) {
|
|
| 287 |
- $row_hash->{$keys->[$i]} = $row->[$i];
|
|
| 288 |
- } |
|
| 289 |
- } |
|
| 290 |
- return wantarray ? %$row_hash : $row_hash; |
|
| 291 |
-} |
|
| 292 |
- |
|
| 293 |
-# Fetch all (array) |
|
| 294 |
-sub fetch_all {
|
|
| 295 |
- my $self = shift; |
|
| 296 |
- |
|
| 297 |
- my $rows = []; |
|
| 298 |
- while(my @row = $self->fetch) {
|
|
| 299 |
- push @$rows, [@row]; |
|
| 300 |
- } |
|
| 301 |
- return wantarray ? @$rows : $rows; |
|
| 302 |
-} |
|
| 303 |
- |
|
| 304 |
-# Fetch all (hash) |
|
| 305 |
-sub fetch_all_hash {
|
|
| 306 |
- my $self = shift; |
|
| 307 |
- |
|
| 308 |
- my $rows = []; |
|
| 309 |
- while(my %row = $self->fetch_hash) {
|
|
| 310 |
- push @$rows, {%row};
|
|
| 311 |
- } |
|
| 312 |
- return wantarray ? @$rows : $rows; |
|
| 313 |
-} |
|
| 314 |
- |
|
| 315 |
-# Finish |
|
| 316 |
-sub finish { shift->sth->finish }
|
|
| 317 |
- |
|
| 318 |
-# Error |
|
| 319 |
-sub error {
|
|
| 320 |
- my $self = shift; |
|
| 321 |
- my $sth = $self->sth; |
|
| 322 |
- return wantarray ? ($sth->errstr, $sth->err, $sth->state) : $sth->errstr; |
|
| 323 |
-} |
|
| 324 |
- |
|
| 325 |
-Object::Simple->build_class; |
|
| 326 |
- |
|
| 327 |
- |
|
| 328 |
-package DBI::Custom::SQL::Template; |
|
| 329 |
-use Object::Simple; |
|
| 330 |
-use Carp 'croak'; |
|
| 331 |
- |
|
| 332 |
-# Clone |
|
| 333 |
-sub clone {
|
|
| 334 |
- my $self = shift; |
|
| 335 |
- my $new = $self->Object::Simple::new; |
|
| 336 |
- |
|
| 337 |
- # Scalar copy |
|
| 338 |
- foreach my $attr (qw/tag_start tag_end bind_filter upper_case tag_syntax template/) {
|
|
| 339 |
- $new->$attr($self->$attr); |
|
| 340 |
- } |
|
| 341 |
- |
|
| 342 |
- # Hash ref copy |
|
| 343 |
- foreach my $attr (qw/tag_processors/) {
|
|
| 344 |
- $new->$attr(\%{$self->$attr || {}});
|
|
| 345 |
- } |
|
| 346 |
- |
|
| 347 |
- # Other |
|
| 348 |
- $new->tree([]); |
|
| 349 |
- |
|
| 350 |
- return $new; |
|
| 351 |
-} |
|
| 352 |
- |
|
| 353 |
- |
|
| 354 |
-### Attributes; |
|
| 355 |
-sub tag_start : Attr { default => '{' }
|
|
| 356 |
-sub tag_end : Attr { default => '}' }
|
|
| 357 |
-sub template : Attr {};
|
|
| 358 |
-sub tree : Attr { default => sub { [] } }
|
|
| 359 |
-sub bind_filter : Attr {}
|
|
| 360 |
-sub upper_case : Attr {default => 0}
|
|
| 361 |
- |
|
| 362 |
-sub tag_syntax : Attr { default => <<'EOS' };
|
|
| 363 |
-{? name} ?
|
|
| 364 |
-{= name} name = ?
|
|
| 365 |
-{<> name} name <> ?
|
|
| 366 |
- |
|
| 367 |
-{< name} name < ?
|
|
| 368 |
-{> name} name > ?
|
|
| 369 |
-{>= name} name >= ?
|
|
| 370 |
-{<= name} name <= ?
|
|
| 371 |
- |
|
| 372 |
-{like name} name like ?
|
|
| 373 |
-{in name} name in [?, ?, ..]
|
|
| 374 |
- |
|
| 375 |
-{insert_values} (key1, key2, key3) values (?, ?, ?)
|
|
| 376 |
-{update_values} set key1 = ?, key2 = ?, key3 = ?
|
|
| 377 |
-EOS |
|
| 378 |
- |
|
| 379 |
-sub tag_processors : Attr {type => 'hash', deref => 1, auto_build => sub {
|
|
| 380 |
- shift->tag_processors( |
|
| 381 |
- '?' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 382 |
- '=' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 383 |
- '<>' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 384 |
- '>' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 385 |
- '<' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 386 |
- '>=' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 387 |
- '<=' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 388 |
- 'like' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 389 |
- 'in' => \&DBI::Custom::SQL::Template::TagProcessor::expand_place_holder, |
|
| 390 |
- 'insert_values' => \&DBI::Custom::SQL::Template::TagProcessor::expand_insert_values, |
|
| 391 |
- 'update_set' => \&DBI::Custom::SQL::Template::TagProcessor::expand_update_set |
|
| 392 |
- ); |
|
| 393 |
-}} |
|
| 394 |
- |
|
| 395 |
-sub add_tag_processor {
|
|
| 396 |
- my $class = shift; |
|
| 397 |
- my $tag_processors = ref $_[0] eq 'HASH' ? $_[0] : {@_};
|
|
| 398 |
- $class->tag_processor(%{$class->tag_processor}, %{$tag_processors});
|
|
| 399 |
-} |
|
| 400 |
- |
|
| 401 |
-sub create_sql {
|
|
| 402 |
- my ($self, $template, $values, $filter) = @_; |
|
| 403 |
- |
|
| 404 |
- $filter ||= $self->bind_filter; |
|
| 405 |
- |
|
| 406 |
- $self->parse($template); |
|
| 407 |
- |
|
| 408 |
- my ($sql, @bind) = $self->build_sql({bind_filter => $filter, values => $values});
|
|
| 409 |
- |
|
| 410 |
- return ($sql, @bind); |
|
| 411 |
-} |
|
| 412 |
- |
|
| 413 |
-sub parse {
|
|
| 414 |
- my ($self, $template) = @_; |
|
| 415 |
- $self->template($template); |
|
| 416 |
- |
|
| 417 |
- # Clean start; |
|
| 418 |
- $self->tree([]); |
|
| 419 |
- |
|
| 420 |
- # Tags |
|
| 421 |
- my $tag_start = quotemeta $self->tag_start; |
|
| 422 |
- my $tag_end = quotemeta $self->tag_end; |
|
| 423 |
- |
|
| 424 |
- # Tokenize |
|
| 425 |
- my $state = 'text'; |
|
| 426 |
- |
|
| 427 |
- # Save original template |
|
| 428 |
- my $original_template = $template; |
|
| 429 |
- |
|
| 430 |
- # Text |
|
| 431 |
- while ($template =~ s/([^$tag_start]*?)$tag_start([^$tag_end].*?)$tag_end//sm) {
|
|
| 432 |
- my $text = $1; |
|
| 433 |
- my $tag = $2; |
|
| 434 |
- |
|
| 435 |
- push @{$self->tree}, {type => 'text', args => [$text]} if $text;
|
|
| 436 |
- |
|
| 437 |
- if ($tag) {
|
|
| 438 |
- |
|
| 439 |
- my ($tag_name, @args) = split /\s+/, $tag; |
|
| 440 |
- |
|
| 441 |
- $tag ||= ''; |
|
| 442 |
- unless ($self->tag_processors->{$tag_name}) {
|
|
| 443 |
- my $tag_syntax = $self->tag_syntax; |
|
| 444 |
- croak("Tag '{$tag}' in SQL template is not exist.\n\n" .
|
|
| 445 |
- "SQL template tag syntax\n" . |
|
| 446 |
- "$tag_syntax\n\n" . |
|
| 447 |
- "Your SQL template is \n$original_template\n\n"); |
|
| 448 |
- } |
|
| 449 |
- |
|
| 450 |
- push @{$self->tree}, {type => 'tag', tag_name => $tag_name, args => [@args]};
|
|
| 451 |
- } |
|
| 452 |
- } |
|
| 453 |
- |
|
| 454 |
- push @{$self->tree}, {type => 'text', args => [$template]} if $template;
|
|
| 455 |
-} |
|
| 456 |
- |
|
| 457 |
-sub build_sql {
|
|
| 458 |
- my ($self, $args) = @_; |
|
| 459 |
- |
|
| 460 |
- my $tree = $args->{tree} || $self->tree;
|
|
| 461 |
- my $bind_filter = $args->{bind_filter} || $self->bind_filter;
|
|
| 462 |
- my $values = $args->{values} || {};
|
|
| 463 |
- |
|
| 464 |
- my @bind_values_all; |
|
| 465 |
- my $sql = ''; |
|
| 466 |
- foreach my $node (@$tree) {
|
|
| 467 |
- my $type = $node->{type};
|
|
| 468 |
- my $tag_name = $node->{tag_name};
|
|
| 469 |
- my $args = $node->{args};
|
|
| 470 |
- |
|
| 471 |
- if ($type eq 'text') {
|
|
| 472 |
- # Join text |
|
| 473 |
- $sql .= $args->[0]; |
|
| 474 |
- } |
|
| 475 |
- elsif ($type eq 'tag') {
|
|
| 476 |
- my $tag_processor = $self->tag_processors->{$tag_name};
|
|
| 477 |
- |
|
| 478 |
- croak("Tag processor '$type' must be code reference")
|
|
| 479 |
- unless ref $tag_processor eq 'CODE'; |
|
| 480 |
- |
|
| 481 |
- my ($expand, @bind_values) |
|
| 482 |
- = $tag_processor->($tag_name, $args, $values, |
|
| 483 |
- $bind_filter, $self); |
|
| 484 |
- |
|
| 485 |
- $DB::single = 1; |
|
| 486 |
- unless ($self->_placeholder_count($expand) == @bind_values) {
|
|
| 487 |
- require Data::Dumper; |
|
| 488 |
- |
|
| 489 |
- my $bind_values_dump |
|
| 490 |
- = Data::Dumper->Dump([\@bind_values], ['*bind_values']); |
|
| 491 |
- |
|
| 492 |
- croak("Place holder count must be same as bind value count\n" .
|
|
| 493 |
- "Tag : $tag_name\n" . |
|
| 494 |
- "Expand : $expand\n" . |
|
| 495 |
- "Bind values: $bind_values_dump\n"); |
|
| 496 |
- } |
|
| 497 |
- push @bind_values_all, @bind_values; |
|
| 498 |
- $sql .= $expand; |
|
| 499 |
- } |
|
| 500 |
- } |
|
| 501 |
- $sql .= ';' unless $sql =~ /;$/; |
|
| 502 |
- return ($sql, @bind_values_all); |
|
| 503 |
-} |
|
| 504 |
- |
|
| 505 |
-sub _placeholder_count {
|
|
| 506 |
- my ($self, $expand) = @_; |
|
| 507 |
- $expand ||= ''; |
|
| 508 |
- |
|
| 509 |
- my $count = 0; |
|
| 510 |
- my $pos = -1; |
|
| 511 |
- while (($pos = index($expand, '?', $pos + 1)) != -1) {
|
|
| 512 |
- $count++; |
|
| 513 |
- } |
|
| 514 |
- return $count; |
|
| 515 |
-} |
|
| 516 |
- |
|
| 517 |
-Object::Simple->build_class; |
|
| 518 |
- |
|
| 519 |
- |
|
| 520 |
-package DBI::Custom::SQL::Template::TagProcessor; |
|
| 521 |
-use strict; |
|
| 522 |
-use warnings; |
|
| 523 |
- |
|
| 524 |
-sub expand_place_holder {
|
|
| 525 |
- my ($tag_name, $args, $values, $bind_filter, $sql_tmpl_obj) = @_; |
|
| 526 |
- |
|
| 527 |
- my $key = $args->[0]; |
|
| 528 |
- |
|
| 529 |
- my @bind_values; |
|
| 530 |
- # Filter Value |
|
| 531 |
- if ($tag_name eq 'in') {
|
|
| 532 |
- $values->{$key} = [$values->{$key}] unless ref $values->{$key} eq 'ARRAY';
|
|
| 533 |
- if ($bind_filter) {
|
|
| 534 |
- for (my $i = 0; $i < @$values; $i++) {
|
|
| 535 |
- push @bind_values, scalar $bind_filter->($key, $values->{$key}->[$i]);
|
|
| 536 |
- } |
|
| 537 |
- } |
|
| 538 |
- else {
|
|
| 539 |
- for (my $i = 0; $i < @$values; $i++) {
|
|
| 540 |
- push @bind_values, $values->{$key}->[$i];
|
|
| 541 |
- } |
|
| 542 |
- } |
|
| 543 |
- } |
|
| 544 |
- else {
|
|
| 545 |
- if ($bind_filter) {
|
|
| 546 |
- push @bind_values, scalar $bind_filter->($key, $values->{$key});
|
|
| 547 |
- } |
|
| 548 |
- else {
|
|
| 549 |
- push @bind_values, $values->{$key};
|
|
| 550 |
- } |
|
| 551 |
- } |
|
| 552 |
- |
|
| 553 |
- $tag_name = uc $tag_name if $sql_tmpl_obj->upper_case; |
|
| 554 |
- |
|
| 555 |
- my $expand; |
|
| 556 |
- if ($tag_name eq '?') {
|
|
| 557 |
- $expand = '?'; |
|
| 558 |
- } |
|
| 559 |
- elsif ($tag_name eq 'in') {
|
|
| 560 |
- $expand = '(';
|
|
| 561 |
- for (my $i = 0; $i < @$values; $i++) {
|
|
| 562 |
- $expand .= '?, '; |
|
| 563 |
- } |
|
| 564 |
- $expand =~ s/, $'//; |
|
| 565 |
- $expand .= ')'; |
|
| 566 |
- } |
|
| 567 |
- else {
|
|
| 568 |
- $expand = "$key $tag_name ?"; |
|
| 569 |
- } |
|
| 570 |
- |
|
| 571 |
- return ($expand, @bind_values); |
|
| 572 |
-} |
|
| 573 |
- |
|
| 574 |
-sub expand_insert_values {
|
|
| 575 |
- my ($tag_name, $args, $values, $bind_filter, $sql_tmpl_obj) = @_; |
|
| 576 |
- |
|
| 577 |
- my $insert_keys = '(';
|
|
| 578 |
- my $place_holders = '(';
|
|
| 579 |
- |
|
| 580 |
- $values = $args->[0] ? $values->{$args->[0]} : $values->{insert_values};
|
|
| 581 |
- |
|
| 582 |
- my @bind_values; |
|
| 583 |
- foreach my $key (sort keys %$values) {
|
|
| 584 |
- $bind_filter ? push @bind_values, scalar $bind_filter->($key, $values->{$key})
|
|
| 585 |
- : push @bind_values, $values->{$key};
|
|
| 586 |
- |
|
| 587 |
- $insert_keys .= "$key, "; |
|
| 588 |
- $place_holders .= "?, "; |
|
| 589 |
- } |
|
| 590 |
- |
|
| 591 |
- $insert_keys =~ s/, $//; |
|
| 592 |
- $insert_keys .= ')'; |
|
| 593 |
- |
|
| 594 |
- $place_holders =~ s/, $//; |
|
| 595 |
- $place_holders .= ')'; |
|
| 596 |
- |
|
| 597 |
- my $expand = $sql_tmpl_obj->upper_case ? "$insert_keys VALUES $place_holders" |
|
| 598 |
- : "$insert_keys values $place_holders"; |
|
| 599 |
- |
|
| 600 |
- return ($expand, @bind_values); |
|
| 601 |
-} |
|
| 602 |
- |
|
| 603 |
-sub expand_update_set {
|
|
| 604 |
- my ($tag_name, $args, $values, $bind_filter, $sql_tmpl_obj) = @_; |
|
| 605 |
- |
|
| 606 |
- my $expand = $sql_tmpl_obj->upper_case ? 'SET ' : 'set '; |
|
| 607 |
- $values = $args->[0] ? $values->{$args->[0]} : $values->{update_set};
|
|
| 608 |
- |
|
| 609 |
- my @bind_values; |
|
| 610 |
- foreach my $key (sort keys %$values) {
|
|
| 611 |
- $bind_filter ? push @bind_values, scalar $bind_filter->($key, $values->{$key})
|
|
| 612 |
- : push @bind_values, $values->{$key};
|
|
| 613 |
- |
|
| 614 |
- $expand .= "$key = ?, "; |
|
| 615 |
- } |
|
| 616 |
- $expand =~ s/, $//; |
|
| 617 |
- return ($expand, @bind_values); |
|
| 618 |
-} |
|
| 619 |
- |
|
| 620 |
- |
|
| 621 |
-package DBI::Custom; |
|
| 622 |
-1; |
|
| 623 |
- |
|
| 624 | 225 |
=head1 NAME |
| 625 | 226 |
|
| 626 | 227 |
DBI::Custom - Customizable simple DBI |
| ... | ... |
@@ -4,6 +4,7 @@ use warnings; |
| 4 | 4 |
|
| 5 | 5 |
use DBI::Custom; |
| 6 | 6 |
use Scalar::Util qw/blessed/; |
| 7 |
+use DBI::Custom::SQL::Template; |
|
| 7 | 8 |
|
| 8 | 9 |
my $sql_tmpl1 = DBI::Custom::SQL::Template->new->upper_case(0); |
| 9 | 10 |
my $sql_tmpl2 = DBI::Custom::SQL::Template->new->upper_case(1); |
| ... | ... |
@@ -22,14 +23,13 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 22 | 23 |
}, |
| 23 | 24 |
bind_filter => 'f', |
| 24 | 25 |
fetch_filter => 'g', |
| 25 |
- dbh => 'e', |
|
| 26 | 26 |
result_class => 'g', |
| 27 | 27 |
sql_template => $sql_tmpl1, |
| 28 | 28 |
valid_connect_info => {i => 1}
|
| 29 | 29 |
); |
| 30 | 30 |
is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c',
|
| 31 | 31 |
options => {d => 1, e => 2}}, filters => {f => 3}, bind_filter => 'f',
|
| 32 |
- fetch_filter => 'g', dbh => 'e', result_class => 'g', |
|
| 32 |
+ fetch_filter => 'g', result_class => 'g', |
|
| 33 | 33 |
sql_template => $sql_tmpl1, valid_connect_info => {i => 1}}, 'new');
|
| 34 | 34 |
|
| 35 | 35 |
isa_ok($dbi, 'DBI::Custom'); |
| ... | ... |
@@ -40,9 +40,9 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 40 | 40 |
package DBI::Custom::T1; |
| 41 | 41 |
use base 'DBI::Custom'; |
| 42 | 42 |
|
| 43 |
- my $prototype = __PACKAGE__->prototype; |
|
| 43 |
+ my $class = __PACKAGE__; |
|
| 44 | 44 |
|
| 45 |
- $prototype |
|
| 45 |
+ $class |
|
| 46 | 46 |
->connect_info( |
| 47 | 47 |
user => 'a', |
| 48 | 48 |
password => 'b', |
| ... | ... |
@@ -54,7 +54,6 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 54 | 54 |
) |
| 55 | 55 |
->bind_filter('f')
|
| 56 | 56 |
->fetch_filter('g')
|
| 57 |
- ->dbh('e')
|
|
| 58 | 57 |
->result_class('DBI::Custom::Result')
|
| 59 | 58 |
->sql_template($sql_tmpl1) |
| 60 | 59 |
->valid_connect_info({p => 1})
|
| ... | ... |
@@ -90,11 +89,13 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 90 | 89 |
{
|
| 91 | 90 |
my $dbi = DBI::Custom::T1->new; |
| 92 | 91 |
|
| 93 |
- my $sql_tmpl = delete $dbi->{sql_template};
|
|
| 94 |
- is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}},
|
|
| 95 |
- filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', result_class => 'DBI::Custom::Result',
|
|
| 96 |
- valid_connect_info => {p => 1}}, 'new custom class');
|
|
| 97 |
- |
|
| 92 |
+ is_deeply($dbi->connect_info, {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}});
|
|
| 93 |
+ is_deeply({$dbi->filters}, {f => 3});
|
|
| 94 |
+ is($dbi->bind_filter, 'f'); |
|
| 95 |
+ is($dbi->fetch_filter, 'g'); |
|
| 96 |
+ is($dbi->result_class, 'DBI::Custom::Result'); |
|
| 97 |
+ is_deeply({$dbi->valid_connect_info},{p => 1});
|
|
| 98 |
+ is($dbi->sql_template->upper_case, 0); |
|
| 98 | 99 |
isa_ok($dbi, 'DBI::Custom::T1'); |
| 99 | 100 |
|
| 100 | 101 |
} |
| ... | ... |
@@ -107,11 +108,13 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 107 | 108 |
{
|
| 108 | 109 |
my $dbi = DBI::Custom::T1_2->new; |
| 109 | 110 |
|
| 110 |
- my $sql_tmpl = delete $dbi->{sql_template};
|
|
| 111 |
- is($sql_tmpl->upper_case, 0); |
|
| 112 |
- is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}},
|
|
| 113 |
- filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', result_class => 'DBI::Custom::Result',
|
|
| 114 |
- valid_connect_info => {p => 1}}, 'new custom class inherit');
|
|
| 111 |
+ is_deeply($dbi->connect_info, {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}});
|
|
| 112 |
+ is_deeply(scalar $dbi->filters, {f => 3});
|
|
| 113 |
+ is($dbi->bind_filter, 'f'); |
|
| 114 |
+ is($dbi->fetch_filter, 'g'); |
|
| 115 |
+ is($dbi->result_class, 'DBI::Custom::Result'); |
|
| 116 |
+ is_deeply({$dbi->valid_connect_info}, {p => 1});
|
|
| 117 |
+ is($dbi->sql_template->upper_case, 0); |
|
| 115 | 118 |
|
| 116 | 119 |
isa_ok($dbi, 'DBI::Custom::T1_2'); |
| 117 | 120 |
} |
| ... | ... |
@@ -120,9 +123,9 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 120 | 123 |
package DBI::Custom::T1_3; |
| 121 | 124 |
use base 'DBI::Custom::T1'; |
| 122 | 125 |
|
| 123 |
- my $prototype = __PACKAGE__->prototype; |
|
| 126 |
+ my $class = __PACKAGE__; |
|
| 124 | 127 |
|
| 125 |
- $prototype |
|
| 128 |
+ $class |
|
| 126 | 129 |
->connect_info( |
| 127 | 130 |
user => 'ao', |
| 128 | 131 |
password => 'bo', |
| ... | ... |
@@ -134,7 +137,6 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 134 | 137 |
) |
| 135 | 138 |
->bind_filter('fo')
|
| 136 | 139 |
->fetch_filter('go')
|
| 137 |
- ->dbh('eo')
|
|
| 138 | 140 |
->result_class('ho')
|
| 139 | 141 |
->sql_template($sql_tmpl2) |
| 140 | 142 |
->valid_connect_info({p => 3})
|
| ... | ... |
@@ -144,11 +146,13 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 144 | 146 |
{
|
| 145 | 147 |
my $dbi = DBI::Custom::T1_3->new; |
| 146 | 148 |
|
| 147 |
- my $sql_tmpl = delete $dbi->{sql_template};
|
|
| 148 |
- is($sql_tmpl->upper_case, 1); |
|
| 149 |
- is_deeply($dbi,{connect_info => {user => 'ao', password => 'bo', data_source => 'co', options => {do => 10, eo => 20}},
|
|
| 150 |
- filters => {fo => 30}, bind_filter => 'fo', fetch_filter => 'go', result_class => 'ho',
|
|
| 151 |
- valid_connect_info => {p => 3}}, 'new custom class');
|
|
| 149 |
+ is_deeply($dbi->connect_info, {user => 'ao', password => 'bo', data_source => 'co', options => {do => 10, eo => 20}});
|
|
| 150 |
+ is_deeply(scalar $dbi->filters, {fo => 30});
|
|
| 151 |
+ is($dbi->bind_filter, 'fo'); |
|
| 152 |
+ is($dbi->fetch_filter, 'go'); |
|
| 153 |
+ is($dbi->result_class, 'ho'); |
|
| 154 |
+ is_deeply(scalar $dbi->valid_connect_info, {p => 3});
|
|
| 155 |
+ is($dbi->sql_template->upper_case, 1); |
|
| 152 | 156 |
|
| 153 | 157 |
isa_ok($dbi, 'DBI::Custom::T1_3'); |
| 154 | 158 |
} |
| ... | ... |
@@ -166,7 +170,6 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 166 | 170 |
}, |
| 167 | 171 |
bind_filter => 'f', |
| 168 | 172 |
fetch_filter => 'g', |
| 169 |
- dbh => 'e', |
|
| 170 | 173 |
result_class => 'h', |
| 171 | 174 |
sql_template => $sql_tmpl3, |
| 172 | 175 |
valid_connect_info => {p => 4}
|
| ... | ... |
@@ -175,7 +178,7 @@ my $sql_tmpl3 = DBI::Custom::SQL::Template->new->upper_case(2); |
| 175 | 178 |
my $sql_tmpl = delete $dbi->{sql_template};
|
| 176 | 179 |
is($sql_tmpl->upper_case, 2); |
| 177 | 180 |
is_deeply($dbi,{connect_info => {user => 'a', password => 'b', data_source => 'c', options => {d => 1, e => 2}},
|
| 178 |
- filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', dbh => 'e', result_class => 'h',
|
|
| 181 |
+ filters => {f => 3}, bind_filter => 'f', fetch_filter => 'g', result_class => 'h',
|
|
| 179 | 182 |
valid_connect_info => {p => 4}}, 'new');
|
| 180 | 183 |
|
| 181 | 184 |
isa_ok($dbi, 'DBI::Custom'); |