, and
451
C. The first two should be obvious; C is where you
452
set the new value you want the column to have. The C column should
453
be the lvalue of Jifty::DBI::Record::PrimaryKeys(). Finally ,
454
C is set when the Value is a SQL function. For example, you
455
might have C<< value => 'PASSWORD(string)' >>, by setting C to true,
456
that string will be inserted into the query directly rather then as a binding.
457
458
=cut
459
460
sub update_record_value {
461
56
56
1
85
my $self = shift;
462
56
292
my %args = (
463
table => undef,
464
column => undef,
465
is_sql_function => undef,
466
primary_keys => undef,
467
@_
468
);
469
470
56
50
86
return 1 unless grep {defined} values %{ $args{primary_keys} };
56
190
56
157
471
472
56
88
my @bind = ();
473
56
149
my $query = 'UPDATE ' . $args{'table'} . ' ';
474
56
125
$query .= 'SET ' . $args{'column'} . '=';
475
476
## Look and see if the column is being updated via a SQL function.
477
56
50
126
if ( $args{'is_sql_function'} ) {
478
0
0
$query .= $args{'value'} . ' ';
479
} else {
480
56
79
$query .= '? ';
481
56
93
push( @bind, $args{'value'} );
482
}
483
484
## Constructs the where clause.
485
56
84
my $where = 'WHERE ';
486
56
71
foreach my $key ( keys %{ $args{'primary_keys'} } ) {
56
138
487
56
95
$where .= $key . "=?" . " AND ";
488
56
143
push( @bind, $args{'primary_keys'}{$key} );
489
}
490
56
272
$where =~ s/AND\s$//;
491
492
56
108
my $query_str = $query . $where;
493
56
165
return ( $self->simple_query( $query_str, @bind ) );
494
}
495
496
=head2 update_table_value table COLUMN NEW_value RECORD_ID IS_SQL
497
498
Update column COLUMN of table table where the record id = RECORD_ID.
499
500
If IS_SQL is set, don't quote the NEW_VALUE.
501
502
=cut
503
504
sub update_table_value {
505
0
0
1
0
my $self = shift;
506
507
## This is just a wrapper to update_record_value().
508
0
0
my %args = ();
509
0
0
$args{'table'} = shift;
510
0
0
$args{'column'} = shift;
511
0
0
$args{'value'} = shift;
512
0
0
$args{'primary_keys'} = shift;
513
0
0
$args{'is_sql_function'} = shift;
514
515
0
0
return $self->update_record_value(%args);
516
}
517
518
=head2 simple_query QUERY_STRING, [ BIND_VALUE, ... ]
519
520
Execute the SQL string specified in QUERY_STRING
521
522
=cut
523
524
our $retry_simple_query = 1;
525
sub simple_query {
526
501
501
1
12401
my $self = shift;
527
501
688
my $query_string = shift;
528
501
535
my @bind_values;
529
501
100
1223
@bind_values = (@_) if (@_);
530
531
501
1105
my $sth = $self->dbh->prepare($query_string);
532
501
100
46839
unless ($sth) {
533
3
13
my $message = "$self couldn't prepare the query '$query_string': "
534
. $self->dbh->errstr;
535
3
50
11
if ($DEBUG) {
536
0
0
die "$message\n";
537
} else {
538
3
11
warn "$message\n";
539
3
26
my $ret = Class::ReturnValue->new();
540
3
22
$ret->as_error(
541
errno => '-1',
542
message => $message,
543
do_backtrace => undef
544
);
545
3
123
return ( $ret->return_value );
546
}
547
}
548
549
# Check @bind_values for HASH refs
550
498
1571
for ( my $bind_idx = 0; $bind_idx < scalar @bind_values; $bind_idx++ ) {
551
592
100
1286
if ( ref( $bind_values[$bind_idx] ) eq "HASH" ) {
552
13
19
my $bhash = $bind_values[$bind_idx];
553
13
29
$bind_values[$bind_idx] = $bhash->{'value'};
554
13
27
delete $bhash->{'value'};
555
13
106
$sth->bind_param( $bind_idx + 1, undef, $bhash );
556
}
557
558
# Some databases, such as Oracle fail to cope if it's a perl utf8
559
# string. they desperately want bytes.
560
592
1801
Encode::_utf8_off( $bind_values[$bind_idx] );
561
}
562
563
498
548
my $basetime;
564
498
100
1331
if ( $self->log_sql_statements ) {
565
6
15
$basetime = Time::HiRes::time();
566
}
567
498
483
my $executed;
568
569
498
515
local $@;
570
{
571
36
36
173
no warnings 'uninitialized'; # undef in bind_values makes DBI sad
36
44
36
90682
498
422
572
498
725
eval { $executed = $sth->execute(@bind_values) };
498
29913085
573
574
# try to ping and reconnect, if the DB connection failed
575
498
50
66
4109
if (($@ or not $executed) and !$self->dbh->ping) {
66
576
0
0
$self->dbh(undef); # don't try pinging again, just connect
577
0
0
$self->connect;
578
579
# Need to call ourselves, to create a new sth from the new dbh
580
0
0
0
if ($retry_simple_query) {
581
0
0
local $retry_simple_query = 0;
582
0
0
return $self->simple_query($query_string, @_);
583
}
584
}
585
}
586
498
100
2030
if ( $self->log_sql_statements ) {
587
6
30
$self->_log_sql_statement( $query_string,
588
Time::HiRes::time() - $basetime, @bind_values );
589
590
}
591
592
498
100
66
2212
if ( $@ or !$executed ) {
593
1
33
5
my $message = "$self couldn't execute the query '$query_string': "
594
. ($self->dbh->errstr || $@);
595
596
1
50
3
if ($DEBUG) {
597
0
0
die "$message\n";
598
} else {
599
600
# XXX: This warn doesn't show up because we mask logging in Jifty::Test::END.
601
# and it usually fails because the test server is still running.
602
1
4
warn "$message\n";
603
604
1
6
my $ret = Class::ReturnValue->new();
605
1
5
$ret->as_error(
606
errno => '-1',
607
message => $message,
608
do_backtrace => undef
609
);
610
1
11
return ( $ret->return_value );
611
}
612
613
}
614
497
2588
return ($sth);
615
616
}
617
618
=head2 fetch_result QUERY, [ BIND_VALUE, ... ]
619
620
Takes a SELECT query as a string, along with an array of BIND_VALUEs
621
If the select succeeds, returns the first row as an array.
622
Otherwise, returns a Class::ResturnValue object with the failure loaded
623
up.
624
625
=cut
626
627
sub fetch_result {
628
1
1
1
2
my $self = shift;
629
1
1
my $query = shift;
630
1
2
my @bind_values = @_;
631
1
3
my $sth = $self->simple_query( $query, @bind_values );
632
1
50
3
if ($sth) {
633
1
19
return ( $sth->fetchrow );
634
} else {
635
0
0
return ($sth);
636
}
637
}
638
639
=head2 blob_params COLUMN_NAME COLUMN_TYPE
640
641
Returns a hash ref for the bind_param call to identify BLOB types used
642
by the current database for a particular column type.
643
644
=cut
645
646
sub blob_params {
647
13
13
1
93
my $self = shift;
648
649
# Don't assign to key 'value' as it is defined later.
650
13
25
return ( {} );
651
}
652
653
=head2 database_version
654
655
Returns the database's version.
656
657
If argument C is true returns short variant, in other
658
case returns whatever database handle/driver returns. By default
659
returns short version, e.g. C<4.1.23> or C<8.0-rc4>.
660
661
Returns empty string on error or if database couldn't return version.
662
663
The base implementation uses a C
664
665
=cut
666
667
sub database_version {
668
0
0
1
0
my $self = shift;
669
0
0
my %args = ( short => 1, @_ );
670
671
0
0
0
unless ( defined $self->{'database_version'} ) {
672
673
# turn off error handling, store old values to restore later
674
0
0
my $re = $self->raise_error;
675
0
0
$self->raise_error(0);
676
0
0
my $pe = $self->print_error;
677
0
0
$self->print_error(0);
678
679
0
0
my $statement = "SELECT VERSION()";
680
0
0
my $sth = $self->simple_query($statement);
681
682
0
0
my $ver = '';
683
0
0
0
0
$ver = ( $sth->fetchrow_arrayref->[0] || '' ) if $sth;
684
0
0
$ver =~ /(\d+(?:\.\d+)*(?:-[a-z0-9]+)?)/i;
685
0
0
$self->{'database_version'} = $ver;
686
0
0
0
$self->{'database_version_short'} = $1 || $ver;
687
688
0
0
$self->raise_error($re);
689
0
0
$self->print_error($pe);
690
}
691
692
0
0
0
return $self->{'database_version_short'} if $args{'short'};
693
0
0
return $self->{'database_version'};
694
}
695
696
=head2 case_sensitive
697
698
Returns 1 if the current database's searches are case sensitive by default
699
Returns undef otherwise
700
701
=cut
702
703
sub case_sensitive {
704
0
0
1
0
my $self = shift;
705
0
0
return (1);
706
}
707
708
=head2 _make_clause_case_insensitive column operator VALUE
709
710
Takes a column, operator and value. performs the magic necessary to make
711
your database treat this clause as case insensitive.
712
713
Returns a column operator value triple.
714
715
=cut
716
717
sub _case_insensitivity_valid {
718
88
88
93
my $self = shift;
719
88
82
my $column = shift;
720
88
88
my $operator = shift;
721
88
79
my $value = shift;
722
723
88
100
1116
return $value ne ''
724
&& $value ne "''"
725
&& ( $operator =~ /^(?:(?:NOT )?LIKE|!?=|IN)$/i )
726
727
# don't downcase integer values
728
&& $value !~ /^['"]?\d+['"]?$/;
729
}
730
731
sub _make_clause_case_insensitive {
732
0
0
0
my $self = shift;
733
0
0
my $column = shift;
734
0
0
my $operator = shift;
735
0
0
my $value = shift;
736
737
0
0
0
if ( $self->_case_insensitivity_valid( $column, $operator, $value ) ) {
738
0
0
$column = "lower($column)";
739
0
0
0
if ( ref $value eq 'ARRAY' ) {
740
0
0
map { $_ = "lower($_)" } @{$value};
0
0
0
0
741
} else {
742
0
0
$value = "lower($value)";
743
}
744
}
745
0
0
return ( $column, $operator, $value );
746
}
747
748
=head2 quote_value VALUE
749
750
Calls the database's L method and returns the result.
751
Additionally, turns on perl's utf8 flag if the returned content is
752
UTF8.
753
754
=cut
755
756
sub quote_value {
757
260
260
1
1022
my $self = shift;
758
260
258
my ($value) = @_;
759
260
372
my $tmp = $self->dbh->quote($value);
760
761
# Accomodate DBI drivers that don't understand UTF8
762
260
50
1856
if ( $] >= 5.007 ) {
763
260
1001
require Encode;
764
260
50
561
if ( Encode::is_utf8($tmp) ) {
765
0
0
Encode::_utf8_on($tmp);
766
}
767
}
768
260
562
return $tmp;
769
}
770
771
=head2 begin_transaction
772
773
Tells Jifty::DBI to begin a new SQL transaction. This will
774
temporarily suspend Autocommit mode.
775
776
Emulates nested transactions, by keeping a transaction stack depth.
777
778
=cut
779
780
sub begin_transaction {
781
1
1
1
2
my $self = shift;
782
783
1
50
3
if ( $TRANSDEPTH > 0 ) {
784
# We're inside a transaction.
785
0
0
$TRANSDEPTH++;
786
0
0
return $TRANSDEPTH;
787
}
788
789
1
2
my $rv = $self->dbh->begin_work;
790
1
50
21
if ($rv) {
791
1
1
$TRANSDEPTH++;
792
}
793
1
2
return $rv;
794
}
795
796
=head2 commit
797
798
Tells Jifty::DBI to commit the current SQL transaction.
799
This will turn Autocommit mode back on.
800
801
=cut
802
803
sub commit {
804
1
1
1
1
my $self = shift;
805
1
50
3
unless ($TRANSDEPTH) {
806
0
0
Carp::confess(
807
"Attempted to commit a transaction with none in progress");
808
}
809
810
1
50
3
if ($TRANSDEPTH > 1) {
811
# We're inside a nested transaction.
812
0
0
$TRANSDEPTH--;
813
0
0
return $TRANSDEPTH;
814
}
815
816
1
2
my $rv = $self->dbh->commit;
817
1
50
11
if ($rv) {
818
1
2
$TRANSDEPTH--;
819
}
820
1
34
return $rv;
821
}
822
823
=head2 rollback [FORCE]
824
825
Tells Jifty::DBI to abort the current SQL transaction.
826
This will turn Autocommit mode back on.
827
828
If this method is passed a true argument, stack depth is blown away and the outermost transaction is rolled back
829
830
=cut
831
832
sub rollback {
833
0
0
1
0
my $self = shift;
834
0
0
my $force = shift;
835
836
0
0
my $dbh = $self->dbh;
837
0
0
0
unless ($dbh) {
838
0
0
$TRANSDEPTH = 0;
839
0
0
return;
840
}
841
842
#unless ($TRANSDEPTH) {Carp::confess("Attempted to rollback a transaction with none in progress")};
843
0
0
0
if ($force) {
844
0
0
$TRANSDEPTH = 0;
845
846
0
0
return ( $dbh->rollback );
847
}
848
849
0
0
0
if ($TRANSDEPTH == 0) {
850
# We're not actually in a transaction.
851
0
0
return 1;
852
}
853
854
0
0
0
if ($TRANSDEPTH > 1) {
855
# We're inside a nested transaction.
856
0
0
$TRANSDEPTH--;
857
0
0
return $TRANSDEPTH;
858
}
859
860
0
0
my $rv = $dbh->rollback;
861
0
0
0
if ($rv) {
862
0
0
$TRANSDEPTH--;
863
}
864
0
0
return $rv;
865
}
866
867
=head2 force_rollback
868
869
Force the handle to rollback. Whether or not we're deep in nested transactions
870
871
=cut
872
873
sub force_rollback {
874
0
0
1
0
my $self = shift;
875
0
0
$self->rollback(1);
876
}
877
878
=head2 transaction_depth
879
880
Return the current depth of the faked nested transaction stack.
881
882
=cut
883
884
sub transaction_depth {
885
0
0
1
0
my $self = shift;
886
0
0
return ($TRANSDEPTH);
887
}
888
889
=head2 apply_limits STATEMENTREF ROWS_PER_PAGE FIRST_ROW
890
891
takes an SQL SELECT statement and massages it to return ROWS_PER_PAGE starting with FIRST_ROW;
892
893
894
=cut
895
896
sub apply_limits {
897
246
246
1
189
my $self = shift;
898
246
164
my $statementref = shift;
899
246
181
my $per_page = shift;
900
246
163
my $first = shift;
901
902
246
206
my $limit_clause = '';
903
904
246
50
332
if ($per_page) {
905
0
0
$limit_clause = " LIMIT ";
906
0
0
0
if ($first) {
907
0
0
$limit_clause .= $first . ", ";
908
}
909
0
0
$limit_clause .= $per_page;
910
}
911
912
246
395
$$statementref .= $limit_clause;
913
914
}
915
916
=head2 join { Paramhash }
917
918
Takes a paramhash of everything Jifty::DBI::Collection's C method
919
takes, plus a parameter called C that contains a ref to a
920
L object'.
921
922
This performs the join.
923
924
=cut
925
926
sub join {
927
928
12
12
1
68
my $self = shift;
929
12
66
my %args = (
930
collection => undef,
931
type => 'normal',
932
alias1 => 'main',
933
column1 => undef,
934
table2 => undef,
935
alias2 => undef,
936
column2 => undef,
937
expression => undef,
938
operator => '=',
939
is_distinct => 0,
940
@_
941
);
942
943
12
12
my $alias;
944
945
# If we're handed in a table2 as a Collection object, make notes
946
# about if the result of the join is still distinct for the
947
# calling collection
948
12
50
66
76
if ( $args{'table2'}
949
&& UNIVERSAL::isa( $args{'table2'}, 'Jifty::DBI::Collection' ) )
950
{
951
0
0
0
my $c = ref $args{'table2'} ? $args{'table2'} : $args{'table2'}->new($args{collection}->_new_collection_args);
952
0
0
0
if ( $args{'operator'} eq '=' ) {
953
0
0
my $x = $c->new_item->column( $args{column2} );
954
0
0
0
0
if ( $x->type eq 'serial' || $x->distinct ) {
955
0
0
$args{'is_distinct'} = 1;
956
}
957
}
958
0
0
$args{'class2'} = ref $c;
959
0
0
$args{'table2'} = $c->table;
960
}
961
962
12
100
23
if ( $args{'alias2'} ) {
963
3
50
33
22
if ( $args{'collection'}{'joins'}{ $args{alias2} } and lc $args{'collection'}{'joins'}{ $args{alias2} }{type} eq "cross" ) {
964
3
6
my $join = $args{'collection'}{'joins'}{ $args{alias2} };
965
3
4
$args{'table2'} = $join->{table};
966
3
6
$alias = $join->{alias};
967
} else {
968
969
# if we can't do that, can we reverse the join and have it work?
970
0
0
@args{qw/alias1 alias2/} = @args{qw/alias2 alias1/};
971
0
0
@args{qw/column1 column2/} = @args{qw/column2 column1/};
972
973
0
0
0
0
if ( $args{'collection'}{'joins'}{ $args{alias2} } and lc $args{'collection'}{'joins'}{ $args{alias2} }{type} eq "cross" ) {
974
0
0
my $join = $args{'collection'}{'joins'}{ $args{alias2} };
975
0
0
$args{'table2'} = $join->{table};
976
0
0
$alias = $join->{alias};
977
} else {
978
979
# Swap back
980
0
0
@args{qw/alias1 alias2/} = @args{qw/alias2 alias1/};
981
0
0
@args{qw/column1 column2/} = @args{qw/column2 column1/};
982
983
0
0
return $args{'collection'}->limit(
984
entry_aggregator => 'AND',
985
@_,
986
quote_value => 0,
987
alias => $args{'alias1'},
988
column => $args{'column1'},
989
value => $args{'alias2'} . "." . $args{'column2'},
990
);
991
}
992
}
993
} else {
994
9
33
$alias = $args{'collection'}->_get_alias( $args{'table2'} );
995
}
996
997
12
100
53
my $meta = $args{'collection'}->{'joins'}{$alias} ||= {};
998
12
21
$meta->{alias} = $alias;
999
12
100
38
if ( $args{'type'} =~ /LEFT/i ) {
1000
8
19
$meta->{'alias_string'}
1001
= " LEFT JOIN " . $args{'table2'} . " $alias ";
1002
8
9
$meta->{'type'} = 'LEFT';
1003
1004
} else {
1005
4
13
$meta->{'alias_string'} = " JOIN " . $args{'table2'} . " $alias ";
1006
4
7
$meta->{'type'} = 'NORMAL';
1007
}
1008
12
15
$meta->{'depends_on'} = $args{'alias1'};
1009
12
13
$meta->{'is_distinct'} = $args{'is_distinct'};
1010
12
50
32
$meta->{'class'} = $args{'class2'} if $args{'class2'};
1011
12
50
21
$meta->{'entry_aggregator'} = $args{'entry_aggregator'}
1012
if $args{'entry_aggregator'};
1013
1014
12
33
45
my $criterion = $args{'expression'} || "$args{'alias1'}.$args{'column1'}";
1015
12
53
$meta->{'criteria'}{'base_criterion'} = [
1016
{ column => $criterion,
1017
operator => $args{'operator'},
1018
value => "$alias.$args{'column2'}",
1019
}
1020
];
1021
1022
12
48
return ($alias);
1023
}
1024
1025
# this code is all hacky and evil. but people desperately want _something_ and I'm
1026
# super tired. refactoring gratefully appreciated.
1027
1028
sub _build_joins {
1029
328
328
967
my $self = shift;
1030
328
248
my $collection = shift;
1031
1032
328
558
$self->_optimize_joins( collection => $collection );
1033
1034
21
37
my @cross = grep { lc $_->{type} eq "cross" }
328
501
1035
328
246
values %{ $collection->{'joins'} };
1036
2
6
my $join_clause = ( $collection->table . " main" )
1037
328
639
. CORE::join( " ", map { $_->{alias_string} } @cross );
1038
328
500
my %processed = map { $_->{alias} => 1 } @cross;
2
4
1039
328
327
$processed{'main'} = 1;
1040
1041
# get a @list of joins that have not been processed yet, but depend on processed join
1042
328
347
my $joins = $collection->{'joins'};
1043
328
100
719
while ( my @list = grep !$processed{$_}
1044
&& $processed{ $joins->{$_}{'depends_on'} }, keys %$joins )
1045
{
1046
19
22
foreach my $join (@list) {
1047
19
23
$processed{$join}++;
1048
1049
19
14
my $meta = $joins->{$join};
1050
19
50
75
my $aggregator = $meta->{'entry_aggregator'} || 'AND';
1051
1052
19
34
$join_clause .= $meta->{'alias_string'} . " ON ";
1053
76
100
137
my @tmp = map {
1054
19
40
ref($_)
1055
? $_->{'column'} . ' '
1056
. $_->{'operator'} . ' '
1057
. $_->{'value'}
1058
: $_
1059
}
1060
map {
1061
19
36
( '(', @$_, ')', $aggregator )
1062
19
15
} values %{ $meta->{'criteria'} };
1063
1064
# delete last aggregator
1065
19
26
pop @tmp;
1066
19
246
$join_clause .= CORE::join ' ', @tmp;
1067
}
1068
}
1069
1070
# here we could check if there is recursion in joins by checking that all joins
1071
# are processed
1072
328
50
544
if ( my @not_processed = grep !$processed{$_}, keys %$joins ) {
1073
0
0
die "Unsatisfied dependency chain in joins @not_processed";
1074
}
1075
328
787
return $join_clause;
1076
}
1077
1078
sub _optimize_joins {
1079
328
328
230
my $self = shift;
1080
328
637
my %args = ( collection => undef, @_ );
1081
328
408
my $joins = $args{'collection'}->{'joins'};
1082
1083
328
251
my %processed;
1084
21
76
$processed{$_}++
1085
328
724
foreach grep {lc $joins->{$_}{'type'} ne 'left'} keys %$joins;
1086
328
430
$processed{'main'}++;
1087
1088
328
262
my @ordered;
1089
1090
# get a @list of joins that have not been processed yet, but depend on processed join
1091
# if we are talking about forest then we'll get the second level of the forest,
1092
# but we should process nodes on this level at the end, so we build FILO ordered list.
1093
# finally we'll get ordered list with leafes in the beginning and top most nodes at
1094
# the end.
1095
328
100
874
while ( my @list = grep !$processed{$_}
1096
&& $processed{ $joins->{$_}{'depends_on'} }, keys %$joins )
1097
{
1098
14
19
unshift @ordered, @list;
1099
14
67
$processed{$_}++ foreach @list;
1100
}
1101
1102
328
612
foreach my $join (@ordered) {
1103
next
1104
14
100
31
if $self->may_be_null(
1105
collection => $args{'collection'},
1106
alias => $join
1107
);
1108
1109
3
17
$joins->{$join}{'alias_string'} =~ s/^\s*LEFT\s+/ /i;
1110
3
9
$joins->{$join}{'type'} = 'NORMAL';
1111
}
1112
1113
}
1114
1115
=head2 may_be_null
1116
1117
Takes a C and C in a hash and returns true if
1118
restrictions of the query allow NULLs in a table joined with the
1119
alias, otherwise returns false value which means that you can use
1120
normal join instead of left for the aliased table.
1121
1122
Works only for queries have been built with
1123
L and L
1124
methods, for other cases return true value to avoid fault
1125
optimizations.
1126
1127
=cut
1128
1129
sub may_be_null {
1130
14
14
1
14
my $self = shift;
1131
14
34
my %args = ( collection => undef, alias => undef, @_ );
1132
1133
# if we have at least one subclause that is not generic then we should get out
1134
# of here as we can't parse subclauses
1135
14
40
return 1
1136
if grep $_ ne 'generic_restrictions',
1137
14
50
10
keys %{ $args{'collection'}->{'subclauses'} };
1138
1139
# build full list of generic conditions
1140
14
14
my @conditions;
1141
14
11
foreach ( grep @$_, values %{ $args{'collection'}->{'restrictions'} } ) {
14
32
1142
10
50
16
push @conditions, 'AND' if @conditions;
1143
10
17
push @conditions, '(', @$_, ')';
1144
}
1145
1146
# find tables that depends on this alias and add their join conditions
1147
14
14
foreach my $join ( values %{ $args{'collection'}->{'joins'} } ) {
14
21
1148
1149
# left joins on the left side so later we'll get 1 AND x expression
1150
# which equal to x, so we just skip it
1151
16
100
35
next if $join->{'type'} eq 'LEFT';
1152
1
50
33
6
next unless $join->{'depends_on'} && ($join->{'depends_on'} eq $args{'alias'});
1153
1154
1
3
my @tmp = map { ( '(', @$_, ')', $join->{'entry_aggregator'} ) }
1
2
1155
1
2
values %{ $join->{'criteria'} };
1156
1
1
pop @tmp;
1157
1158
1
5
@conditions = ( '(', @conditions, ')', 'AND', '(', @tmp, ')' );
1159
1160
}
1161
14
100
43
return 1 unless @conditions;
1162
1163
# replace conditions with boolean result: 1 - allow nulls, 0 - doesn't
1164
10
13
foreach ( splice @conditions ) {
1165
46
100
33
138
unless ( ref $_ ) {
100
50
1166
33
35
push @conditions, $_;
1167
} elsif ( $_->{'column'} =~ /^\Q$args{'alias'}./ ) {
1168
1169
# only operator IS allows NULLs in the aliased table
1170
10
20
push @conditions, lc $_->{'operator'} eq 'is';
1171
} elsif ( $_->{'value'} && $_->{'value'} =~ /^\Q$args{'alias'}./ ) {
1172
1173
# right operand is our alias, such condition don't allow NULLs
1174
0
0
push @conditions, 0;
1175
} else {
1176
1177
# conditions on other aliases
1178
3
5
push @conditions, 1;
1179
}
1180
}
1181
1182
# returns index of closing paren by index of openning paren
1183
my $closing_paren = sub {
1184
0
0
0
my $i = shift;
1185
0
0
my $count = 0;
1186
0
0
for ( ; $i < @conditions; $i++ ) {
1187
0
0
0
0
if ( $conditions[$i] && $conditions[$i] eq '(' ) {
0
0
1188
0
0
$count++;
1189
} elsif ( $conditions[$i] && $conditions[$i] eq ')' ) {
1190
0
0
$count--;
1191
}
1192
0
0
0
return $i unless $count;
1193
}
1194
0
0
die "lost in parens";
1195
10
34
};
1196
1197
# solve boolean expression we have, an answer is our result
1198
10
12
my @tmp = ();
1199
10
21
while ( defined( my $e = shift @conditions ) ) {
1200
1201
#warn "@tmp >>>$e<<< @conditions";
1202
48
100
66
124
return $e if !@conditions && !@tmp;
1203
1204
38
100
62
unless ($e) {
100
100
50
1205
3
100
8
if ( $conditions[0] eq ')' ) {
1206
1
1
push @tmp, $e;
1207
1
2
next;
1208
}
1209
1210
2
3
my $aggreg = uc shift @conditions;
1211
2
50
4
if ( $aggreg eq 'OR' ) {
0
1212
1213
# 0 OR x == x
1214
2
4
next;
1215
} elsif ( $aggreg eq 'AND' ) {
1216
1217
# 0 AND x == 0
1218
0
0
my $close_p = $closing_paren->(0);
1219
0
0
splice @conditions, 0, $close_p + 1, (0);
1220
} else {
1221
0
0
die "lost @tmp >>>$e $aggreg<<< @conditions";
1222
}
1223
} elsif ( $e eq '1' ) {
1224
6
100
9
if ( $conditions[0] eq ')' ) {
1225
5
6
push @tmp, $e;
1226
5
9
next;
1227
}
1228
1229
1
3
my $aggreg = uc shift @conditions;
1230
1
50
4
if ( $aggreg eq 'OR' ) {
50
1231
1232
# 1 OR x == 1
1233
0
0
my $close_p = $closing_paren->(0);
1234
0
0
splice @conditions, 0, $close_p + 1, (1);
1235
} elsif ( $aggreg eq 'AND' ) {
1236
1237
# 1 AND x == x
1238
1
2
next;
1239
} else {
1240
0
0
die "lost @tmp >>>$e $aggreg<<< @conditions";
1241
}
1242
} elsif ( $e eq '(' ) {
1243
23
100
26
if ( $conditions[1] eq ')' ) {
1244
15
35
splice @conditions, 1, 1;
1245
} else {
1246
8
15
push @tmp, $e;
1247
}
1248
} elsif ( $e eq ')' ) {
1249
6
9
unshift @conditions, @tmp, $e;
1250
6
13
@tmp = ();
1251
} else {
1252
0
0
die "lost: @tmp >>>$e<<< @conditions";
1253
}
1254
}
1255
0
0
return 1;
1256
}
1257
1258
=head2 distinct_query STATEMENTREF
1259
1260
takes an incomplete SQL SELECT statement and massages it to return a DISTINCT result set.
1261
1262
=cut
1263
1264
sub distinct_query {
1265
5
5
1
22
my $self = shift;
1266
5
7
my $statementref = shift;
1267
5
4
my $collection = shift;
1268
1269
# Prepend select query for DBs which allow DISTINCT on all column types.
1270
5
15
$$statementref
1271
= "SELECT DISTINCT "
1272
. $collection->query_columns
1273
. " FROM $$statementref";
1274
1275
5
20
$$statementref .= $collection->_group_clause;
1276
5
16
$$statementref .= $collection->_order_clause;
1277
}
1278
1279
=head2 distinct_count STATEMENTREF
1280
1281
takes an incomplete SQL SELECT statement and massages it to return a DISTINCT result set.
1282
1283
=cut
1284
1285
sub distinct_count {
1286
0
0
1
0
my $self = shift;
1287
0
0
my $statementref = shift;
1288
1289
# Prepend select query for DBs which allow DISTINCT on all column types.
1290
0
0
$$statementref = "SELECT COUNT(DISTINCT main.id) FROM $$statementref";
1291
1292
}
1293
1294
=head2 canonical_true
1295
1296
This returns the canonical true value for this database. For example, in SQLite
1297
it is 1 but in Postgres it is 't'.
1298
1299
The default is 1.
1300
1301
=cut
1302
1303
59
59
1
503
sub canonical_true { 1 }
1304
1305
=head2 canonical_false
1306
1307
This returns the canonical false value for this database. For example, in SQLite
1308
it is 0 but in Postgres it is 'f'.
1309
1310
The default is 0.
1311
1312
=cut
1313
1314
62
62
1
587
sub canonical_false { 0 }
1315
1316
=head2 Schema manipulation methods
1317
1318
=head3 rename_column
1319
1320
Rename a column in a table. Takes 'table', 'column' and new name in 'to'.
1321
1322
=cut
1323
1324
sub rename_column {
1325
0
0
1
0
my $self = shift;
1326
0
0
my %args = (table => undef, column => undef, to => undef, @_);
1327
# Oracle: since Oracle 9i R2
1328
# Pg: 7.4 can this and may be earlier
1329
0
0
return $self->simple_query(
1330
"ALTER TABLE $args{'table'} RENAME COLUMN $args{'column'} TO $args{'to'}"
1331
);
1332
}
1333
1334
1335
=head3 rename_table
1336
1337
Renames a table in the DB. Takes 'table' and new name of it in 'to'.
1338
1339
=cut
1340
1341
sub rename_table {
1342
1
1
1
2
my $self = shift;
1343
1
6
my %args = (table => undef, to => undef, @_);
1344
# mysql has RENAME TABLE, but alter can rename temporary
1345
# Oracle, Pg, SQLite are ok with this
1346
1
7
return $self->simple_query("ALTER TABLE $args{'table'} RENAME TO $args{'to'}");
1347
}
1348
1349
=head2 supported_drivers
1350
1351
Returns a list of the drivers L supports.
1352
1353
=cut
1354
1355
sub supported_drivers {
1356
68
68
1
81313
return qw(
1357
SQLite
1358
Informix
1359
mysql
1360
mysqlPP
1361
ODBC
1362
Oracle
1363
Pg
1364
Sybase
1365
);
1366
}
1367
1368
=head2 available_drivers
1369
1370
Returns a list of the available drivers based on the presence of C
1371
modules.
1372
1373
=cut
1374
1375
sub available_drivers {
1376
34
34
1
114
my $self = shift;
1377
1378
34
44
local $@;
1379
34
72
return grep { eval "require DBD::" . $_ } $self->supported_drivers;
272
213466
1380
}
1381
1382
=head2 is_available_driver
1383
1384
Returns a boolean indicating whether the provided driver is available.
1385
1386
=cut
1387
1388
do {
1389
# lazily memoize
1390
my $is_available_driver;
1391
1392
sub is_available_driver {
1393
0
0
1
0
my $self = shift;
1394
0
0
my $driver = shift;
1395
1396
0
0
0
if (!$is_available_driver) {
1397
0
0
%$is_available_driver = map { $_ => 1 } $self->available_drivers;
0
0
1398
}
1399
1400
0
0
return $is_available_driver->{$driver};
1401
}
1402
};
1403
1404
=head2 DESTROY
1405
1406
When we get rid of the L, we need to disconnect
1407
from the database
1408
1409
=cut
1410
1411
sub DESTROY {
1412
31
31
5980
my $self = shift;
1413
1414
# eval in DESTROY can cause $@ issues elsewhere
1415
31
59
local $@;
1416
1417
$self->disconnect
1418
unless $self->dbh
1419
and $self->dbh
1420
# We use an eval {} because DESTROY order during
1421
# global destruction is not guaranteed -- the dbh may
1422
# no longer be tied, which throws an error.
1423
31
50
66
97
and eval { $self->dbh->{InactiveDestroy} };
30
66
74
1424
31
226
delete $DBIHandle{$self};
1425
}
1426
1427
1;
1428
__END__