name
|
701
|
|
|
|
|
|
|
my $profile = $_->{Profiles}; |
|
702
|
|
|
|
|
|
|
print $user->id, " has address: ", $profile->address; |
|
703
|
|
|
|
|
|
|
} |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
The above only does 1 SELECT. Note that the join search function |
|
706
|
|
|
|
|
|
|
returns an array of hashes that map from the SQL table name to the |
|
707
|
|
|
|
|
|
|
DBIx::OO instance. |
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
You can pass additional WHERE, ORDER, LIMIT and OFFSET clauses to the |
|
710
|
|
|
|
|
|
|
join functions as well: |
|
711
|
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
@data = Users->search_join_profile({ 'Users.last_name' => 'Doe' }, |
|
713
|
|
|
|
|
|
|
'Users.nickname', |
|
714
|
|
|
|
|
|
|
10); |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
The above fetches the first 10 members of the Doe family ordered by |
|
717
|
|
|
|
|
|
|
nickname. |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
Due to lack of support from SQL::Abstract side, the JOIN is actually a |
|
720
|
|
|
|
|
|
|
select like this: |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
SELECT ... FROM table1, table2 WHERE table1.foreign = table2.id |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
In the future I hope to add better support for this, that is, use |
|
725
|
|
|
|
|
|
|
"INNER JOIN" and eventually support other JOIN types as well. |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=head3 Notes |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=over |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=item 1. |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
The C accessors will actually retrieve data at each call. |
|
734
|
|
|
|
|
|
|
Therefore: |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
$p1 = $user->fk_pages; |
|
737
|
|
|
|
|
|
|
$p2 = $user->fk_pages; |
|
738
|
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
will retrieve 2 different arrays, containing different sets of objects |
|
740
|
|
|
|
|
|
|
(even if they point to the same records), hitting the database twice. |
|
741
|
|
|
|
|
|
|
This is subject to change, but for now you have to be careful about |
|
742
|
|
|
|
|
|
|
this. It's best to keep a reference to the returned object(s) rather |
|
743
|
|
|
|
|
|
|
than calling fk_pages() all over the place. |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
=item 2. |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
has_many() creates accessors that select multiple objects. The |
|
748
|
|
|
|
|
|
|
database will be hit once, though, and multiple objects are created |
|
749
|
|
|
|
|
|
|
from the returned data. If this isn't desirable, feel free to LIMIT |
|
750
|
|
|
|
|
|
|
your results. |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=back |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=cut |
|
755
|
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
### TODO: this can be optimized: cache the where clause and generated SQL. |
|
757
|
|
|
|
|
|
|
sub has_a { |
|
758
|
0
|
|
|
0
|
1
|
|
my ($class, $name, $type, $arg, $order) = @_; |
|
759
|
0
|
|
|
|
|
|
my $fk_name = $class->get_fk_name($name); |
|
760
|
2
|
|
|
2
|
|
12
|
no strict 'refs'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
1771
|
|
|
761
|
0
|
|
|
|
|
|
my $colmap; |
|
762
|
|
|
|
|
|
|
my $mk_colmap = sub { |
|
763
|
0
|
0
|
|
0
|
|
|
if (!defined $colmap) { |
|
764
|
0
|
|
|
|
|
|
my ($class) = @_; |
|
765
|
0
|
|
|
|
|
|
$colmap = {}; |
|
766
|
0
|
0
|
|
|
|
|
if (!$arg) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
767
|
0
|
|
|
|
|
|
$colmap->{$name} = $type->columns('P')->[0]; |
|
768
|
|
|
|
|
|
|
} elsif (!ref $arg) { |
|
769
|
0
|
|
|
|
|
|
$colmap->{$class->columns('P')->[0]} = $arg; |
|
770
|
|
|
|
|
|
|
} elsif (ref $arg eq 'HASH') { |
|
771
|
0
|
|
|
|
|
|
$colmap = $arg; |
|
772
|
|
|
|
|
|
|
} elsif (ref $arg eq 'ARRAY') { |
|
773
|
0
|
|
|
|
|
|
@{$colmap}{@$arg} = @{$type->columns('P')}; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
} |
|
775
|
|
|
|
|
|
|
} |
|
776
|
0
|
|
|
|
|
|
}; |
|
777
|
|
|
|
|
|
|
## declare the fk_colname function |
|
778
|
|
|
|
|
|
|
{ |
|
779
|
0
|
|
|
|
|
|
*{"$class\::$fk_name"} = sub { |
|
|
0
|
|
|
|
|
|
|
|
780
|
0
|
|
|
0
|
|
|
my ($self, $order2) = @_; |
|
781
|
0
|
0
|
|
|
|
|
$order2 = $order |
|
782
|
|
|
|
|
|
|
if !defined $order2; |
|
783
|
0
|
|
|
|
|
|
&$mk_colmap($self); |
|
784
|
0
|
|
|
|
|
|
my %where; |
|
785
|
0
|
|
|
|
|
|
@where{values %$colmap} = @{$self->{values}}{keys %$colmap}; |
|
|
0
|
|
|
|
|
|
|
|
786
|
0
|
|
|
|
|
|
my $a = $type->search(\%where, $order, 1); |
|
787
|
0
|
|
|
|
|
|
return $a->[0]; |
|
788
|
0
|
|
|
|
|
|
}; |
|
789
|
|
|
|
|
|
|
} |
|
790
|
|
|
|
|
|
|
## simple 2 tables JOIN facility |
|
791
|
|
|
|
|
|
|
{ |
|
792
|
0
|
|
|
|
|
|
my %join_colmap; |
|
|
0
|
|
|
|
|
|
|
|
793
|
0
|
|
|
|
|
|
my ($t1, $t2); |
|
794
|
0
|
|
|
|
|
|
my ($c1, $c2); |
|
795
|
0
|
|
|
|
|
|
my @cols; |
|
796
|
0
|
|
|
|
|
|
*{"$class\::search_join_${name}"} = sub { |
|
797
|
0
|
|
|
0
|
|
|
my ($class, $where2, $order2, $limit, $offset) = @_; |
|
798
|
0
|
0
|
|
|
|
|
$order2 = $order |
|
799
|
|
|
|
|
|
|
if !defined $order2; |
|
800
|
0
|
|
|
|
|
|
my $sa = $class->get_sql_abstract; |
|
801
|
0
|
0
|
|
|
|
|
if (!%join_colmap) { |
|
802
|
0
|
|
|
|
|
|
&$mk_colmap($class); |
|
803
|
0
|
|
|
|
|
|
($t1, $t2) = ($class->table, $type->table); |
|
804
|
0
|
|
|
|
|
|
$c1 = $class->_get_columns([ 'P', 'E' ]); |
|
805
|
0
|
|
|
|
|
|
$c2 = $type->_get_columns([ 'P', 'E' ]); |
|
806
|
0
|
|
|
|
|
|
@cols = map { "$t1.$_" } @$c1; |
|
|
0
|
|
|
|
|
|
|
|
807
|
0
|
|
|
|
|
|
push(@cols, |
|
808
|
0
|
|
|
|
|
|
map { "$t2.$_" } @$c2); |
|
809
|
0
|
|
|
|
|
|
my @k = map { "$t1.$_" } keys %$colmap; |
|
|
0
|
|
|
|
|
|
|
|
810
|
0
|
|
|
|
|
|
my @v = map { my $tmp = '= ' . $sa->_quote("$t2.$_"); |
|
|
0
|
|
|
|
|
|
|
|
811
|
0
|
|
|
|
|
|
\$tmp } values %$colmap; |
|
812
|
0
|
|
|
|
|
|
@join_colmap{@k} = @v; |
|
813
|
|
|
|
|
|
|
} |
|
814
|
0
|
|
|
|
|
|
my %where = %join_colmap; |
|
815
|
0
|
0
|
|
|
|
|
@where{keys %$where2} = values %$where2 |
|
816
|
|
|
|
|
|
|
if $where2; |
|
817
|
0
|
|
|
|
|
|
my ($sql, @bind) = $sa->select([ $t1, $t2 ], |
|
818
|
|
|
|
|
|
|
\@cols, \%where, $order2, $limit, $offset); |
|
819
|
0
|
|
|
|
|
|
my $sth = $class->_run_sql($sql, \@bind); |
|
820
|
0
|
|
|
|
|
|
my @ret; |
|
821
|
0
|
|
|
|
|
|
my $slicepoint = scalar(@$c1) - 1; |
|
822
|
0
|
|
|
|
|
|
my $end = $slicepoint + scalar(@$c2); |
|
823
|
0
|
|
|
|
|
|
while (my $row = $sth->fetchrow_arrayref) { |
|
824
|
0
|
|
|
|
|
|
my $obj = {}; |
|
825
|
0
|
|
|
|
|
|
my $o1 = $obj->{$t1} = $class->new; |
|
826
|
0
|
|
|
|
|
|
my $o2 = $obj->{$t2} = $type->new; |
|
827
|
0
|
|
|
|
|
|
@{$o1->{values}}{@$c1} = @{$row}[0..$slicepoint]; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
828
|
0
|
|
|
|
|
|
@{$o2->{values}}{@$c2} = @{$row}[$slicepoint+1..$end]; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
829
|
0
|
|
|
|
|
|
push @ret, $obj; |
|
830
|
|
|
|
|
|
|
} |
|
831
|
0
|
|
|
|
|
|
return @ret; |
|
832
|
0
|
|
|
|
|
|
}; |
|
833
|
|
|
|
|
|
|
} |
|
834
|
0
|
|
|
|
|
|
undef $class; |
|
835
|
|
|
|
|
|
|
} |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
=head2 C |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
Alias to has_a(). |
|
840
|
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
=cut |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
*might_have = \&has_a; |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
### TODO: this can be optimized: cache the where clause and generated SQL. |
|
846
|
|
|
|
|
|
|
sub has_many { |
|
847
|
0
|
|
|
0
|
1
|
|
my ($class, $name, $type, $arg, $order, $limit, $offset) = @_; |
|
848
|
0
|
|
|
|
|
|
my $colmap; |
|
849
|
0
|
|
|
|
|
|
my $fk_name = $class->get_fk_name($name); |
|
850
|
2
|
|
|
2
|
|
14
|
no strict 'refs'; |
|
|
2
|
|
|
|
|
20
|
|
|
|
2
|
|
|
|
|
1278
|
|
|
851
|
|
|
|
|
|
|
my $mk_colmap = sub { |
|
852
|
0
|
0
|
|
0
|
|
|
if (!defined $colmap) { |
|
853
|
0
|
|
|
|
|
|
my $self = shift; |
|
854
|
0
|
|
|
|
|
|
$colmap = {}; |
|
855
|
0
|
0
|
|
|
|
|
if (!$arg) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
856
|
0
|
|
|
|
|
|
$colmap->{$name} = $type->columns('P')->[0]; |
|
857
|
|
|
|
|
|
|
} elsif (!ref $arg) { |
|
858
|
0
|
|
|
|
|
|
$colmap->{$self->columns('P')->[0]} = $arg; |
|
859
|
|
|
|
|
|
|
} elsif (ref $arg eq 'HASH') { |
|
860
|
0
|
|
|
|
|
|
$colmap = $arg; |
|
861
|
|
|
|
|
|
|
} elsif (ref $arg eq 'ARRAY') { |
|
862
|
0
|
|
|
|
|
|
@{$colmap}{@$arg} = @{$type->columns('P')}; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
} |
|
864
|
|
|
|
|
|
|
} |
|
865
|
0
|
|
|
|
|
|
}; |
|
866
|
0
|
|
|
|
|
|
*{"$class\::$fk_name"} = sub { |
|
867
|
0
|
|
|
0
|
|
|
my ($self, $where2, $order2, $limit2, $offset2) = @_; |
|
868
|
0
|
0
|
|
|
|
|
$order2 = $order |
|
869
|
|
|
|
|
|
|
if !defined $order2; |
|
870
|
0
|
0
|
|
|
|
|
$limit2 = $limit |
|
871
|
|
|
|
|
|
|
if !defined $limit2; |
|
872
|
0
|
0
|
|
|
|
|
$offset2 = $offset |
|
873
|
|
|
|
|
|
|
if !defined $offset2; |
|
874
|
0
|
|
|
|
|
|
&$mk_colmap($self); |
|
875
|
0
|
|
|
|
|
|
my %where; |
|
876
|
0
|
|
|
|
|
|
@where{values %$colmap} = @{$self->{values}}{keys %$colmap}; |
|
|
0
|
|
|
|
|
|
|
|
877
|
0
|
0
|
|
|
|
|
@where{keys %$where2} = values %$where2 |
|
878
|
|
|
|
|
|
|
if $where2; |
|
879
|
0
|
|
|
|
|
|
return $type->search(\%where, $order2, $limit2, $offset2); |
|
880
|
0
|
|
|
|
|
|
}; |
|
881
|
0
|
|
|
|
|
|
*{"$class\::add_to_$name"} = sub { |
|
882
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
883
|
0
|
0
|
|
|
|
|
my %val = ref $_[0] eq 'HASH' ? %{$_[0]} : @_; |
|
|
0
|
|
|
|
|
|
|
|
884
|
0
|
|
|
|
|
|
&$mk_colmap($self); |
|
885
|
0
|
|
|
|
|
|
@val{values %$colmap} = @{$self->{values}}{keys %$colmap}; |
|
|
0
|
|
|
|
|
|
|
|
886
|
0
|
|
|
|
|
|
return $type->create(\%val); |
|
887
|
0
|
|
|
|
|
|
}; |
|
888
|
0
|
|
|
|
|
|
*{"$class\::count_$name"} = sub { |
|
889
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
890
|
0
|
0
|
|
|
|
|
my %val = ref $_[0] eq 'HASH' ? %{$_[0]} : @_; |
|
|
0
|
|
|
|
|
|
|
|
891
|
0
|
|
|
|
|
|
&$mk_colmap($self); |
|
892
|
0
|
|
|
|
|
|
@val{values %$colmap} = @{$self->{values}}{keys %$colmap}; |
|
|
0
|
|
|
|
|
|
|
|
893
|
0
|
|
|
|
|
|
return $type->count(\%val); |
|
894
|
0
|
|
|
|
|
|
}; |
|
895
|
0
|
|
|
|
|
|
undef $class; |
|
896
|
|
|
|
|
|
|
} |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
=head2 C |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
You can use has_mapping to map one object to another using an |
|
901
|
|
|
|
|
|
|
intermediate table. You can have these tables: |
|
902
|
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
Users: id, first_name, etc. |
|
904
|
|
|
|
|
|
|
Groups: id, description, etc. |
|
905
|
|
|
|
|
|
|
Users_To_Groups: user, group |
|
906
|
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
This is quite classical, I suppose, to declare many-to-many |
|
908
|
|
|
|
|
|
|
relationships. The Users_To_Groups contains records that map one user |
|
909
|
|
|
|
|
|
|
to one group. To get the ID-s of all groups that a certain user |
|
910
|
|
|
|
|
|
|
belongs to, you would say: |
|
911
|
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
SELECT group FROM Users_To_Group where user = '$user' |
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
But since you usually need the Group objects directly, you could speed |
|
915
|
|
|
|
|
|
|
things up with a join: |
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
SELECT Groups.id, Groups.description, ... FROM Groups, Users_To_Groups |
|
918
|
|
|
|
|
|
|
WHERE Users_To_Groups.group = Groups.id |
|
919
|
|
|
|
|
|
|
AND Users_To_Groups.user = '$user'; |
|
920
|
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
The relationship declared with has_mapping() does exactly that. You |
|
922
|
|
|
|
|
|
|
would call it like this: |
|
923
|
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
package Users; |
|
925
|
|
|
|
|
|
|
__PACKAGE__->table('Users'); |
|
926
|
|
|
|
|
|
|
__PACKAGE__->columns(P => [ 'id' ], ...); |
|
927
|
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
__PACKAGE__->has_mapping(groups, 'Groups', |
|
929
|
|
|
|
|
|
|
'Users_To_Groups', 'user', 'group'); |
|
930
|
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
package Groups; |
|
932
|
|
|
|
|
|
|
__PACKAGE__->table('Groups'); |
|
933
|
|
|
|
|
|
|
__PACKAGE__->columns(P => [ 'id' ], ...); |
|
934
|
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
# You can get the reverse mapping as well: |
|
936
|
|
|
|
|
|
|
__PACKAGE__->has_mapping(users, 'Users', |
|
937
|
|
|
|
|
|
|
'Users_To_Groups', 'group', 'user'); |
|
938
|
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
package Users_To_Groups; |
|
940
|
|
|
|
|
|
|
__PACKAGE__->table('Users_To_Groups'); |
|
941
|
|
|
|
|
|
|
__PACKAGE__->columns(P => [ 'user', 'group' ]); |
|
942
|
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
Note that Users_To_Groups has a multiple primary key. This isn't |
|
944
|
|
|
|
|
|
|
required, but you should at least have an unique index for the (user, |
|
945
|
|
|
|
|
|
|
group) pair. |
|
946
|
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
=head3 Arguments |
|
948
|
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
I started with an example because the function itself is quite |
|
950
|
|
|
|
|
|
|
complicated. Here are arguments documentation: |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
=over |
|
953
|
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
=item name |
|
955
|
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
This is used to name the accessors. By default we will prepend a |
|
957
|
|
|
|
|
|
|
"fk_" (see L). |
|
958
|
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
=item type |
|
960
|
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
The type of the target objects. |
|
962
|
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
=item maptype |
|
964
|
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
The mapping object type. This is the name of the object that maps one |
|
966
|
|
|
|
|
|
|
type to another. Even though you'll probably never need to |
|
967
|
|
|
|
|
|
|
instantiate such an object, it still has to be declared. |
|
968
|
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
=item map1 |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
Specifies how we map from current package (__PACKAGE__) to the |
|
972
|
|
|
|
|
|
|
C object. This can be a scalar or an hash ref. If it's a |
|
973
|
|
|
|
|
|
|
scalar, we will assume that __PACKAGE__ has a simple primary key (not |
|
974
|
|
|
|
|
|
|
multiple) and C is the name of the column from C that |
|
975
|
|
|
|
|
|
|
we should map this key to. If it's a hash reference, it should |
|
976
|
|
|
|
|
|
|
directly specify the mapping; the keys will be taken from __PACKAGE__ |
|
977
|
|
|
|
|
|
|
and the values from C. If that sounds horrible, check the |
|
978
|
|
|
|
|
|
|
example below. |
|
979
|
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
=item map2 |
|
981
|
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
Similar to C, but C specifies the mapping from C |
|
983
|
|
|
|
|
|
|
to the target C. If a scalar, it will be the name of the column |
|
984
|
|
|
|
|
|
|
from C that maps to the primary key of the target package |
|
985
|
|
|
|
|
|
|
(assumed to be a simple primary key). If a hash reference, it |
|
986
|
|
|
|
|
|
|
specifies the full mapping. |
|
987
|
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
=item order, limit, offset |
|
989
|
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
Similar to has_many, these can specify default ORDER BY and/or |
|
991
|
|
|
|
|
|
|
LIMIT/OFFSET clauses for the resulted query. |
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
=back |
|
994
|
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
=head3 Example |
|
996
|
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
Here's the mapping overview: |
|
998
|
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
map1 map2 |
|
1000
|
|
|
|
|
|
|
__PACKAGE__ ===> C ===> C |
|
1001
|
|
|
|
|
|
|
current package table that holds the target package |
|
1002
|
|
|
|
|
|
|
the mapping |
|
1003
|
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
=cut |
|
1005
|
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
sub has_mapping { |
|
1007
|
0
|
|
|
0
|
1
|
|
my ($class, $name, $type, $maptype, $arg1, $arg2, $order, $limit, $offset) = @_; |
|
1008
|
0
|
|
|
|
|
|
my $fk_name = $class->get_fk_name($name); |
|
1009
|
2
|
|
|
2
|
|
13
|
no strict 'refs'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
6729
|
|
|
1010
|
0
|
|
|
|
|
|
my ($tcols, $select); |
|
1011
|
0
|
|
|
|
|
|
my @keys; |
|
1012
|
0
|
|
|
|
|
|
*{"$class\::$fk_name"} = sub { |
|
1013
|
0
|
|
|
0
|
|
|
my ($self, $order2, $limit2, $offset2) = @_; |
|
1014
|
0
|
0
|
|
|
|
|
$order2 = $order |
|
1015
|
|
|
|
|
|
|
if !defined $order2; |
|
1016
|
0
|
0
|
|
|
|
|
$limit2 = $limit |
|
1017
|
|
|
|
|
|
|
if !defined $limit2; |
|
1018
|
0
|
0
|
|
|
|
|
$offset2 = $offset |
|
1019
|
|
|
|
|
|
|
if !defined $offset2; |
|
1020
|
|
|
|
|
|
|
|
|
1021
|
0
|
|
|
|
|
|
my $sa = $self->get_sql_abstract; |
|
1022
|
0
|
|
|
|
|
|
my @bind; |
|
1023
|
0
|
0
|
|
|
|
|
if (!$select) { |
|
1024
|
0
|
0
|
|
|
|
|
if (!ref $arg1) { |
|
|
|
0
|
|
|
|
|
|
|
1025
|
0
|
|
|
|
|
|
my %tmp; |
|
1026
|
0
|
|
|
|
|
|
$tmp{$self->columns('P')->[0]} = $arg1; |
|
1027
|
0
|
|
|
|
|
|
$arg1 = \%tmp; |
|
1028
|
|
|
|
|
|
|
} elsif (ref $arg1 eq 'ARRAY') { |
|
1029
|
0
|
|
|
|
|
|
my %tmp; |
|
1030
|
0
|
|
|
|
|
|
@tmp{@{$self->columns('P')}} = @$arg1; |
|
|
0
|
|
|
|
|
|
|
|
1031
|
0
|
|
|
|
|
|
$arg1 = \%tmp; |
|
1032
|
|
|
|
|
|
|
} |
|
1033
|
0
|
0
|
|
|
|
|
if (!ref $arg2) { |
|
|
|
0
|
|
|
|
|
|
|
1034
|
0
|
|
|
|
|
|
my %tmp; |
|
1035
|
0
|
|
|
|
|
|
$tmp{$arg2} = $type->columns('P')->[0]; |
|
1036
|
0
|
|
|
|
|
|
$arg2 = \%tmp; |
|
1037
|
|
|
|
|
|
|
} elsif (ref $arg2 eq 'ARRAY') { |
|
1038
|
0
|
|
|
|
|
|
my %tmp; |
|
1039
|
0
|
|
|
|
|
|
@tmp{@$arg2} = @{$type->columns('P')}; |
|
|
0
|
|
|
|
|
|
|
|
1040
|
0
|
|
|
|
|
|
$arg2 = \%tmp; |
|
1041
|
|
|
|
|
|
|
} |
|
1042
|
|
|
|
|
|
|
|
|
1043
|
0
|
|
|
|
|
|
my %where = (); |
|
1044
|
0
|
|
|
|
|
|
my ($st, $tt, $mt) = ($self->table, $type->table, $maptype->table); |
|
1045
|
0
|
|
|
|
|
|
while (my ($k, $v) = each %$arg1) { |
|
1046
|
0
|
|
|
|
|
|
my $tmp = '= ' . $sa->_quote("$mt.$v"); |
|
1047
|
0
|
|
|
|
|
|
$where{"$st.$k"} = \$tmp; # SCALAR ref means literal SQL |
|
1048
|
0
|
|
|
|
|
|
$where{"$mt.$v"} = $self->get($k); |
|
1049
|
0
|
|
|
|
|
|
push @keys, $k; # remember these keys to reconstruct @bind later |
|
1050
|
|
|
|
|
|
|
} |
|
1051
|
0
|
|
|
|
|
|
while (my ($k, $v) = each %$arg2) { |
|
1052
|
0
|
|
|
|
|
|
my $tmp = '= ' . $sa->_quote("$tt.$v"); |
|
1053
|
0
|
|
|
|
|
|
$where{"$mt.$k"} = \$tmp; # SCALAR ref means literal SQL |
|
1054
|
|
|
|
|
|
|
} |
|
1055
|
0
|
|
|
|
|
|
$tcols = $type->_get_columns([ 'P', 'E' ]); |
|
1056
|
0
|
|
|
|
|
|
my @fields = map { "$tt.$_" } @$tcols; |
|
|
0
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
|
|
1058
|
0
|
|
|
|
|
|
($select, @bind) = $sa->select([ $st, $mt, $tt ], \@fields, \%where); |
|
1059
|
|
|
|
|
|
|
} else { |
|
1060
|
0
|
|
|
|
|
|
@bind = $self->get(@keys); |
|
1061
|
|
|
|
|
|
|
} |
|
1062
|
0
|
|
|
|
|
|
my $sql = $select . $sa->order_and_limit($order2, $limit2, $offset2); |
|
1063
|
0
|
|
|
|
|
|
my $sth = $type->_run_sql($sql, \@bind); |
|
1064
|
0
|
|
|
|
|
|
my @ret; |
|
1065
|
0
|
|
|
|
|
|
while (my $row = $sth->fetchrow_arrayref) { |
|
1066
|
0
|
|
|
|
|
|
my $obj = $type->new; |
|
1067
|
0
|
|
|
|
|
|
@{$obj->{values}}{@$tcols} = @$row; |
|
|
0
|
|
|
|
|
|
|
|
1068
|
0
|
|
|
|
|
|
push @ret, $obj; |
|
1069
|
|
|
|
|
|
|
} |
|
1070
|
|
|
|
|
|
|
|
|
1071
|
0
|
0
|
|
|
|
|
return wantarray ? @ret : \@ret; |
|
1072
|
0
|
|
|
|
|
|
}; |
|
1073
|
|
|
|
|
|
|
} |
|
1074
|
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
=head2 C |
|
1076
|
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
my $u = Users->create({ id => 'foo', |
|
1078
|
|
|
|
|
|
|
first_name => 'John', |
|
1079
|
|
|
|
|
|
|
last_name => 'Doe' }); |
|
1080
|
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
Creates a new record and stores it in the database. Returns the newly |
|
1082
|
|
|
|
|
|
|
created object. We recommend passing a hash reference, but you can |
|
1083
|
|
|
|
|
|
|
pass a hash by value as well. |
|
1084
|
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
=cut |
|
1086
|
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
sub create { |
|
1088
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1089
|
0
|
0
|
|
|
|
|
my %val = ref $_[0] eq 'HASH' ? %{$_[0]} : @_; |
|
|
0
|
|
|
|
|
|
|
|
1090
|
0
|
|
|
|
|
|
my $class = __T($self); |
|
1091
|
|
|
|
|
|
|
|
|
1092
|
0
|
|
|
|
|
|
my $obj = $class->new; |
|
1093
|
0
|
|
|
|
|
|
$obj->before_set(\%val, 1); |
|
1094
|
0
|
|
|
|
|
|
$obj->{values} = \%val; |
|
1095
|
0
|
|
|
|
|
|
$obj->_apply_defaults; |
|
1096
|
|
|
|
|
|
|
|
|
1097
|
0
|
|
|
|
|
|
my $sa = $self->get_sql_abstract; |
|
1098
|
0
|
|
|
|
|
|
my ($sql, @bind) = $sa->insert($self->table, \%val); |
|
1099
|
0
|
|
|
|
|
|
my $dbh = $self->get_dbh; |
|
1100
|
0
|
|
|
|
|
|
$self->_run_sql($sql, \@bind); |
|
1101
|
|
|
|
|
|
|
|
|
1102
|
0
|
|
|
|
|
|
my $pk = $self->columns('P'); |
|
1103
|
0
|
0
|
0
|
|
|
|
$val{$pk->[0]} = $self->_get_last_id($dbh) |
|
1104
|
|
|
|
|
|
|
if @$pk == 1 && !exists $val{$pk->[0]}; |
|
1105
|
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
# since users may specify SQL functions using an array ref, we |
|
1107
|
|
|
|
|
|
|
# remove them in order to get full values later. |
|
1108
|
0
|
|
|
|
|
|
while (my ($k, $v) = each %val) { |
|
1109
|
0
|
0
|
|
|
|
|
delete $val{$k} |
|
1110
|
|
|
|
|
|
|
if ref $v; |
|
1111
|
|
|
|
|
|
|
} |
|
1112
|
|
|
|
|
|
|
|
|
1113
|
0
|
|
|
|
|
|
return $obj; |
|
1114
|
|
|
|
|
|
|
} |
|
1115
|
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
=head2 clone(@except) |
|
1117
|
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
Clones an object, returning a hash (reference) suitable for create(). |
|
1119
|
|
|
|
|
|
|
Here's how you would call it: |
|
1120
|
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
my $val = $page->clone; |
|
1122
|
|
|
|
|
|
|
my $new_page = Pages->create($val); |
|
1123
|
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
Or, supposing you don't want to copy the value of the "created" field: |
|
1125
|
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
my $val = $page->clone('created'); |
|
1127
|
|
|
|
|
|
|
my $new_page = Pages->create($val); |
|
1128
|
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
=cut |
|
1130
|
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
sub clone { |
|
1132
|
0
|
|
|
0
|
1
|
|
my ($self, @except) = @_; |
|
1133
|
0
|
|
|
|
|
|
my %val; |
|
1134
|
0
|
|
|
|
|
|
my $cols = $self->clone_columns(@except); |
|
1135
|
0
|
|
|
|
|
|
@val{@$cols} = $self->get(@$cols); |
|
1136
|
0
|
|
|
|
|
|
return \%val; |
|
1137
|
|
|
|
|
|
|
} |
|
1138
|
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
=head2 C |
|
1140
|
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
Initializes one or more objects from the given data. $data can be a |
|
1142
|
|
|
|
|
|
|
hashref (in which case a single object will be created and returned) |
|
1143
|
|
|
|
|
|
|
or an arrayref (multiple objects will be created and returned as an |
|
1144
|
|
|
|
|
|
|
array reference). |
|
1145
|
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
The hashes simply contain the data, as retrieved from the database. |
|
1147
|
|
|
|
|
|
|
That is, map column name to field value. |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
This method is convenient in those cases where you already have the |
|
1150
|
|
|
|
|
|
|
data (suppose you SELECT-ed it in a different way than using DBIx::OO) |
|
1151
|
|
|
|
|
|
|
and want to initialize DBIx::OO objects without the penalty of going |
|
1152
|
|
|
|
|
|
|
through the DB again. |
|
1153
|
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
=cut |
|
1155
|
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
sub init_from_data { |
|
1157
|
0
|
|
|
0
|
1
|
|
my ($class, $data) = @_; |
|
1158
|
0
|
0
|
|
|
|
|
if (ref $data eq 'ARRAY') { |
|
1159
|
0
|
|
|
|
|
|
my @a = (); |
|
1160
|
0
|
|
|
|
|
|
foreach my $h (@$data) { |
|
1161
|
0
|
|
|
|
|
|
push @a, $class->init_from_data($h); |
|
1162
|
|
|
|
|
|
|
} |
|
1163
|
0
|
|
|
|
|
|
return \@a; |
|
1164
|
|
|
|
|
|
|
} else { |
|
1165
|
0
|
|
|
|
|
|
my $obj = $class->new; |
|
1166
|
0
|
|
|
|
|
|
$obj->{values} = $data; |
|
1167
|
0
|
|
|
|
|
|
return $obj; |
|
1168
|
|
|
|
|
|
|
} |
|
1169
|
|
|
|
|
|
|
} |
|
1170
|
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
=head2 C |
|
1172
|
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
my $u = Users->retrieve('foo'); |
|
1174
|
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
Retrieves an object from the database. You need to pass its ID (the |
|
1176
|
|
|
|
|
|
|
value of the primary key). If the primary key consists on more |
|
1177
|
|
|
|
|
|
|
columns, you can pass the values in order as an array, or you can pass |
|
1178
|
|
|
|
|
|
|
a hash reference. |
|
1179
|
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
Returns undef if no objects were found. |
|
1181
|
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
=cut |
|
1183
|
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
sub retrieve { |
|
1185
|
0
|
|
|
0
|
1
|
|
my $class = __T($_[0]); |
|
1186
|
0
|
|
|
|
|
|
my $self = shift; |
|
1187
|
0
|
|
|
|
|
|
my $obj; |
|
1188
|
0
|
0
|
|
|
|
|
if (ref $self) { # refresh existing object |
|
1189
|
0
|
|
|
|
|
|
$obj = $self; |
|
1190
|
|
|
|
|
|
|
# reset values |
|
1191
|
0
|
|
|
|
|
|
$obj->{values} = $self->_get_pk_where; |
|
1192
|
0
|
|
|
|
|
|
$obj->{modified} = {}; |
|
1193
|
|
|
|
|
|
|
} else { # create new object |
|
1194
|
0
|
|
|
|
|
|
$obj = $class->new; |
|
1195
|
0
|
0
|
|
|
|
|
if (!ref $_[0]) { |
|
|
|
0
|
|
|
|
|
|
|
1196
|
0
|
|
|
|
|
|
my $pk = $class->columns('P'); |
|
1197
|
0
|
|
|
|
|
|
@{$obj->{values}}{@$pk} = @_; |
|
|
0
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
} elsif (ref $_[0] eq 'HASH') { |
|
1199
|
0
|
|
|
|
|
|
my ($h) = @_; |
|
1200
|
0
|
|
|
|
|
|
@{$obj->{values}}{keys %$h} = values %$h; |
|
|
0
|
|
|
|
|
|
|
|
1201
|
|
|
|
|
|
|
} |
|
1202
|
|
|
|
|
|
|
} |
|
1203
|
0
|
|
|
|
|
|
eval { |
|
1204
|
0
|
|
|
|
|
|
$obj->_retrieve_columns([ 'P', 'E' ]); |
|
1205
|
|
|
|
|
|
|
}; |
|
1206
|
0
|
0
|
|
|
|
|
if ($@) { |
|
1207
|
|
|
|
|
|
|
### XXX: a warning should be in order here? We can't be sure |
|
1208
|
|
|
|
|
|
|
### why did the operation failed... |
|
1209
|
0
|
|
|
|
|
|
undef $obj; |
|
1210
|
|
|
|
|
|
|
} |
|
1211
|
0
|
|
|
|
|
|
return $obj; |
|
1212
|
|
|
|
|
|
|
} |
|
1213
|
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
=head2 C |
|
1215
|
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
$a = Users->search({ created => [ '>=', '2006-01-01 00:00:00' ]}); |
|
1217
|
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
Searches the database and returns an array of objects that match the |
|
1219
|
|
|
|
|
|
|
search criteria. All arguments are optional. If you pass no |
|
1220
|
|
|
|
|
|
|
arguments, it will return an array containing all objects in the DB. |
|
1221
|
|
|
|
|
|
|
The syntax of C<$where> and C<$order> are described in |
|
1222
|
|
|
|
|
|
|
L. |
|
1223
|
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
In scalar context it will return a reference to the array. |
|
1225
|
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
The C<$limit> and C<$offset> arguments are added by DBIx::OO and allow you |
|
1227
|
|
|
|
|
|
|
to limit/paginate your query. |
|
1228
|
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
UPDATE 0.0.7: |
|
1230
|
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
Certain queries are difficult to express in SQL::Abstract syntax. The |
|
1232
|
|
|
|
|
|
|
search accepts a literal WHERE clause too, but until version 0.0.7 |
|
1233
|
|
|
|
|
|
|
there was no way to specify bind variables. For example, now you can |
|
1234
|
|
|
|
|
|
|
do this: |
|
1235
|
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
@admins = Users->search("mode & ? <> 0 and created > ?", |
|
1237
|
|
|
|
|
|
|
undef, undef, undef, |
|
1238
|
|
|
|
|
|
|
MODE_FLAGS->{admin}, |
|
1239
|
|
|
|
|
|
|
strftime('%Y-%m-%d', localtime)). |
|
1240
|
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
In order to pass bind variables, you must pass order, limit and offset |
|
1242
|
|
|
|
|
|
|
(give undef if you don't care about them) and add your bind variables |
|
1243
|
|
|
|
|
|
|
immediately after. |
|
1244
|
|
|
|
|
|
|
|
|
1245
|
|
|
|
|
|
|
=cut |
|
1246
|
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
sub search { |
|
1248
|
0
|
|
|
0
|
1
|
|
my $class = __T(shift); |
|
1249
|
0
|
|
|
|
|
|
my ($where, $order, $limit, $offset) = @_; |
|
1250
|
0
|
|
|
|
|
|
splice @_, 0, 4; |
|
1251
|
0
|
|
|
|
|
|
my $sa = $class->get_sql_abstract; |
|
1252
|
0
|
|
|
|
|
|
my $cols = $class->_get_columns([ 'P', 'E' ]); |
|
1253
|
0
|
|
|
|
|
|
my ($sql, @bind) = $sa->select($class->table, $cols, $where, $order, $limit, $offset); |
|
1254
|
0
|
0
|
|
|
|
|
if (@_) { |
|
1255
|
0
|
|
|
|
|
|
push @bind, @_; |
|
1256
|
|
|
|
|
|
|
} |
|
1257
|
0
|
|
|
|
|
|
my $sth = $class->_run_sql($sql, \@bind); |
|
1258
|
0
|
|
|
|
|
|
my @ret = (); |
|
1259
|
0
|
|
|
|
|
|
while (my $row = $sth->fetchrow_arrayref) { |
|
1260
|
0
|
|
|
|
|
|
my $obj = $class->new; |
|
1261
|
0
|
|
|
|
|
|
@{$obj->{values}}{@$cols} = @$row; |
|
|
0
|
|
|
|
|
|
|
|
1262
|
0
|
|
|
|
|
|
push @ret, $obj; |
|
1263
|
|
|
|
|
|
|
} |
|
1264
|
0
|
0
|
|
|
|
|
return wantarray ? @ret : \@ret; |
|
1265
|
|
|
|
|
|
|
} |
|
1266
|
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
=head2 C |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
retrieve_all() is an alias to search() -- since with no arguments it |
|
1270
|
|
|
|
|
|
|
fetches all objects. |
|
1271
|
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
=cut |
|
1273
|
|
|
|
|
|
|
|
|
1274
|
|
|
|
|
|
|
*retrieve_all = *search; |
|
1275
|
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
=head2 C |
|
1277
|
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
$u->set(first_name => 'Foo', |
|
1279
|
|
|
|
|
|
|
last_name => 'Bar'); |
|
1280
|
|
|
|
|
|
|
$u->update; |
|
1281
|
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
Saves any modified columns to the database. |
|
1283
|
|
|
|
|
|
|
|
|
1284
|
|
|
|
|
|
|
=cut |
|
1285
|
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
sub update { |
|
1287
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
|
1288
|
0
|
0
|
|
|
|
|
if (ref $class) { |
|
1289
|
0
|
|
|
|
|
|
$class->_do_update; |
|
1290
|
|
|
|
|
|
|
} else { |
|
1291
|
0
|
|
|
|
|
|
my ($fieldvals, $where) = @_; |
|
1292
|
0
|
|
|
|
|
|
my $sa = $class->get_sql_abstract; |
|
1293
|
0
|
|
|
|
|
|
my ($sql, @bind) = $sa->update($class->table, $fieldvals, $where); |
|
1294
|
0
|
|
|
|
|
|
$class->_run_sql($sql, \@bind); |
|
1295
|
|
|
|
|
|
|
} |
|
1296
|
|
|
|
|
|
|
} |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
|
|
|
|
|
|
=head2 C |
|
1299
|
|
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
$u = Users->retrieve('foo'); |
|
1301
|
|
|
|
|
|
|
$u->delete; |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
Removes the object's record from the database. Note that the Perl |
|
1304
|
|
|
|
|
|
|
object remains intact and you can actually revive it (if you're not |
|
1305
|
|
|
|
|
|
|
losing it) using undelete(). |
|
1306
|
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
=cut |
|
1308
|
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
sub delete { |
|
1310
|
0
|
|
|
0
|
1
|
|
my ($self, $where) = @_; |
|
1311
|
0
|
|
|
|
|
|
my ($sql, @bind); |
|
1312
|
0
|
|
|
|
|
|
my $sa = $self->get_sql_abstract; |
|
1313
|
0
|
0
|
|
|
|
|
if (!defined $where) { |
|
1314
|
|
|
|
|
|
|
# we're deleting one object |
|
1315
|
0
|
|
|
|
|
|
($sql, @bind) = $sa->delete($self->table, $self->_get_pk_where); |
|
1316
|
|
|
|
|
|
|
} else { |
|
1317
|
|
|
|
|
|
|
# deleting multiple objects at once |
|
1318
|
0
|
|
|
|
|
|
($sql, @bind) = $sa->delete($self->table, $where); |
|
1319
|
|
|
|
|
|
|
} |
|
1320
|
0
|
|
|
|
|
|
$self->_run_sql($sql, \@bind); |
|
1321
|
|
|
|
|
|
|
} |
|
1322
|
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
=head2 C |
|
1324
|
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
$u = Users->retrieve('foo'); |
|
1326
|
|
|
|
|
|
|
$u->delete; # record's gone |
|
1327
|
|
|
|
|
|
|
$u->undelete; # resurrected |
|
1328
|
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
This function can "ressurect" an object that has been deleted (that |
|
1330
|
|
|
|
|
|
|
is, it re-INSERT-s the record into the database), provided that you |
|
1331
|
|
|
|
|
|
|
still have a reference to the object. I'm not sure how useful it is, |
|
1332
|
|
|
|
|
|
|
but it helped me test the delete() function. :-) |
|
1333
|
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
Other (unuseful) thing you can do with it is manually emulating the |
|
1335
|
|
|
|
|
|
|
create() function: |
|
1336
|
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
$u = new Users; |
|
1338
|
|
|
|
|
|
|
$u->{values}{id} = 'foo'; |
|
1339
|
|
|
|
|
|
|
$u->first_name('Foo'); |
|
1340
|
|
|
|
|
|
|
$u->last_name('Bar'); |
|
1341
|
|
|
|
|
|
|
$u->undelete; |
|
1342
|
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
Note we can't call the column accessors, nor use set/get, before we |
|
1344
|
|
|
|
|
|
|
have a primary key. |
|
1345
|
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
This method is not too useful in itself, but it helps understanding |
|
1347
|
|
|
|
|
|
|
the internals of DBIx::OO. If you want to read more about this, see |
|
1348
|
|
|
|
|
|
|
L. |
|
1349
|
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
=cut |
|
1351
|
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
sub undelete { |
|
1353
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
|
1354
|
0
|
|
|
|
|
|
$self->_apply_defaults; |
|
1355
|
0
|
|
|
|
|
|
my $sa = $self->get_sql_abstract; |
|
1356
|
0
|
|
|
|
|
|
my ($sql, @bind) = $sa->insert($self->table, $self->{values}); |
|
1357
|
0
|
|
|
|
|
|
$self->_run_sql($sql, \@bind); |
|
1358
|
0
|
|
|
|
|
|
$self->{modified} = {}; |
|
1359
|
|
|
|
|
|
|
} |
|
1360
|
|
|
|
|
|
|
|
|
1361
|
|
|
|
|
|
|
=head2 C, or C |
|
1362
|
|
|
|
|
|
|
|
|
1363
|
|
|
|
|
|
|
$u = Users->retrieve('foo'); |
|
1364
|
|
|
|
|
|
|
$u->first_name(undef); |
|
1365
|
|
|
|
|
|
|
$u->revert; |
|
1366
|
|
|
|
|
|
|
|
|
1367
|
|
|
|
|
|
|
Discards any changes to the object, reverting to the state in the |
|
1368
|
|
|
|
|
|
|
database. Note this doesn't SELECT new data, it just reverts to |
|
1369
|
|
|
|
|
|
|
values saved in the C hash. See L for more |
|
1370
|
|
|
|
|
|
|
info. |
|
1371
|
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
C is an alias to C. |
|
1373
|
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
=cut |
|
1375
|
|
|
|
|
|
|
|
|
1376
|
|
|
|
|
|
|
sub revert { |
|
1377
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
1378
|
|
|
|
|
|
|
# delete @{$self->{values}}{keys %{$self->{modified}}}; |
|
1379
|
0
|
|
|
|
|
|
my $m = $self->{modified}; |
|
1380
|
0
|
|
|
|
|
|
@{$self->{values}}{keys %$m} = values %$m; |
|
|
0
|
|
|
|
|
|
|
|
1381
|
0
|
|
|
|
|
|
$self->{modified} = {}; |
|
1382
|
|
|
|
|
|
|
} |
|
1383
|
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
*discard_changes = \&revert; |
|
1385
|
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
=head2 get_sql_abstract |
|
1387
|
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
Returns the instance of SQL::Abstract::WithLimit (our custom |
|
1389
|
|
|
|
|
|
|
derivative) suitable for generating SQL. This is cached (will be |
|
1390
|
|
|
|
|
|
|
created only the first time get_sql_abstract is called). |
|
1391
|
|
|
|
|
|
|
|
|
1392
|
|
|
|
|
|
|
=cut |
|
1393
|
|
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
sub get_sql_abstract { |
|
1395
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
|
1396
|
0
|
|
|
|
|
|
my $sa = $class->__dboo_sqlabstract; |
|
1397
|
0
|
0
|
|
|
|
|
if (!defined $sa) { |
|
1398
|
0
|
|
|
|
|
|
$sa = SQL::Abstract::WithLimit->new(quote_char => '`', # NOTE: MySQL quote style |
|
1399
|
|
|
|
|
|
|
name_sep => '.'); |
|
1400
|
0
|
|
|
|
|
|
$class->__dboo_sqlabstract($sa); |
|
1401
|
|
|
|
|
|
|
} |
|
1402
|
0
|
|
|
|
|
|
return $sa; |
|
1403
|
|
|
|
|
|
|
} |
|
1404
|
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
=head2 count |
|
1406
|
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
Returns the result of an SQL COUNT(*) for the specified where clause. |
|
1408
|
|
|
|
|
|
|
Call this as a package method, for example: |
|
1409
|
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
$number_of_romanians = Users->count({ country => 'RO' }); |
|
1411
|
|
|
|
|
|
|
|
|
1412
|
|
|
|
|
|
|
The argument is an SQL::Abstract where clause. |
|
1413
|
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
=cut |
|
1415
|
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
sub count { |
|
1417
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
|
1418
|
0
|
0
|
|
|
|
|
my $where = ref $_[0] eq 'HASH' ? $_[0] : { @_ }; |
|
1419
|
0
|
|
|
|
|
|
my $sql = 'SELECT COUNT(*) FROM ' . $class->table; |
|
1420
|
0
|
|
|
|
|
|
($where, my @bind) = $class->get_sql_abstract->where($where); |
|
1421
|
0
|
|
|
|
|
|
my $sth = $class->_run_sql($sql.$where, \@bind); |
|
1422
|
0
|
|
|
|
|
|
return $sth->fetchrow_arrayref->[0]; |
|
1423
|
|
|
|
|
|
|
} |
|
1424
|
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
sub _get_pk_where { |
|
1426
|
0
|
|
|
0
|
|
|
my ($self) = @_; |
|
1427
|
0
|
|
|
|
|
|
my $pc = $self->columns('P'); |
|
1428
|
0
|
|
|
|
|
|
my %where = (); |
|
1429
|
0
|
|
|
|
|
|
@where{@$pc} = @{$self->{values}}{@$pc}; |
|
|
0
|
|
|
|
|
|
|
|
1430
|
0
|
|
|
|
|
|
return \%where; |
|
1431
|
|
|
|
|
|
|
} |
|
1432
|
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
sub _run_sql { |
|
1434
|
0
|
|
|
0
|
|
|
my ($class, $sql, $bind) = @_; |
|
1435
|
|
|
|
|
|
|
# { |
|
1436
|
|
|
|
|
|
|
# ## DEBUG |
|
1437
|
|
|
|
|
|
|
# no warnings 'uninitialized'; |
|
1438
|
|
|
|
|
|
|
# my @a = map { defined $_ ? $_ : 'NULL' } @$bind; |
|
1439
|
|
|
|
|
|
|
# print STDERR "\033[1;33mSQL: $sql\nVAL: ", join(", ", @a), "\n\033[0m"; |
|
1440
|
|
|
|
|
|
|
# } |
|
1441
|
0
|
|
|
|
|
|
my $dbh = $class->get_dbh; |
|
1442
|
0
|
|
|
|
|
|
my $sth = $dbh->prepare($sql); |
|
1443
|
0
|
0
|
|
|
|
|
if ($bind) { |
|
1444
|
0
|
|
|
|
|
|
$sth->execute(@$bind); |
|
1445
|
|
|
|
|
|
|
} else { |
|
1446
|
0
|
|
|
|
|
|
$sth->execute(); |
|
1447
|
|
|
|
|
|
|
} |
|
1448
|
0
|
|
|
|
|
|
return $sth; |
|
1449
|
|
|
|
|
|
|
} |
|
1450
|
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
sub _do_update { |
|
1452
|
0
|
|
|
0
|
|
|
my ($self) = @_; |
|
1453
|
0
|
|
|
|
|
|
my %set = (); |
|
1454
|
0
|
|
|
|
|
|
my @k = keys %{$self->{modified}}; |
|
|
0
|
|
|
|
|
|
|
|
1455
|
0
|
0
|
|
|
|
|
if (@k) { |
|
1456
|
0
|
|
|
|
|
|
@set{@k} = @{$self->{values}}{@k}; |
|
|
0
|
|
|
|
|
|
|
|
1457
|
0
|
|
|
|
|
|
my $where = $self->_get_pk_where; |
|
1458
|
0
|
|
|
|
|
|
my $sa = $self->get_sql_abstract; |
|
1459
|
0
|
|
|
|
|
|
my ($sql, @bind) = $sa->update($self->table, \%set, $where); |
|
1460
|
0
|
|
|
|
|
|
$self->_run_sql($sql, \@bind); |
|
1461
|
0
|
|
|
|
|
|
$self->{modified} = {}; |
|
1462
|
0
|
|
|
|
|
|
while (my ($k, $v) = each %set) { |
|
1463
|
0
|
0
|
|
|
|
|
delete $self->{values}{$k} |
|
1464
|
|
|
|
|
|
|
if ref $v; |
|
1465
|
|
|
|
|
|
|
} |
|
1466
|
|
|
|
|
|
|
} |
|
1467
|
|
|
|
|
|
|
} |
|
1468
|
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
sub _get_columns { |
|
1470
|
0
|
|
|
0
|
|
|
my ($self, $groups, $exclude) = @_; |
|
1471
|
0
|
|
|
|
|
|
my $ek; |
|
1472
|
0
|
0
|
0
|
|
|
|
if (!$groups || @$groups == 0) { |
|
|
|
0
|
|
|
|
|
|
|
1473
|
0
|
|
|
|
|
|
$ek = $self->columns; |
|
1474
|
|
|
|
|
|
|
} elsif (@$groups == 1) { |
|
1475
|
0
|
|
|
|
|
|
$ek = $self->columns($groups->[0]); |
|
1476
|
|
|
|
|
|
|
} else { |
|
1477
|
0
|
|
|
|
|
|
$ek = []; |
|
1478
|
0
|
|
|
|
|
|
foreach my $g (@$groups) { |
|
1479
|
0
|
|
|
|
|
|
my $a = $self->columns($g); |
|
1480
|
0
|
0
|
|
|
|
|
push @$ek, @{$a} |
|
|
0
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
if $a; |
|
1482
|
|
|
|
|
|
|
} |
|
1483
|
|
|
|
|
|
|
} |
|
1484
|
0
|
0
|
0
|
|
|
|
if (defined $exclude && %$exclude) { |
|
1485
|
0
|
|
|
|
|
|
$ek = [ grep { !exists $exclude->{$_} } @$ek ]; |
|
|
0
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
} |
|
1487
|
0
|
|
|
|
|
|
return $ek; |
|
1488
|
|
|
|
|
|
|
} |
|
1489
|
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
sub _retrieve_columns { |
|
1491
|
0
|
|
|
0
|
|
|
my ($self, $groups, $exclude) = @_; |
|
1492
|
0
|
0
|
|
|
|
|
if (!ref $groups) { |
|
1493
|
0
|
|
|
|
|
|
$groups = [ $groups ]; |
|
1494
|
|
|
|
|
|
|
} |
|
1495
|
0
|
|
0
|
|
|
|
my $ek = $self->_get_columns($groups, $exclude || $self->{modified}); |
|
1496
|
0
|
|
|
|
|
|
my $where = $self->_get_pk_where; |
|
1497
|
0
|
|
|
|
|
|
my $sa = $self->get_sql_abstract; |
|
1498
|
0
|
|
|
|
|
|
my ($sql, @bind) = $sa->select($self->table, $ek, $where); |
|
1499
|
0
|
|
|
|
|
|
my $sth = $self->_run_sql($sql, \@bind); |
|
1500
|
0
|
|
|
|
|
|
my $data = $sth->fetchrow_arrayref; |
|
1501
|
0
|
|
|
|
|
|
@{$self->{values}}{@$ek} = @$data; |
|
|
0
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
} |
|
1503
|
|
|
|
|
|
|
|
|
1504
|
|
|
|
|
|
|
sub _get_last_id { |
|
1505
|
0
|
|
|
0
|
|
|
my ($self, $dbh) = @_; |
|
1506
|
|
|
|
|
|
|
my $id = $dbh->last_insert_id(undef, undef, $self->table, undef) |
|
1507
|
|
|
|
|
|
|
|| $dbh->{mysql_insertid} |
|
1508
|
0
|
0
|
0
|
|
|
|
|| eval { $dbh->func('last_insert_rowid') } |
|
1509
|
|
|
|
|
|
|
or $self->_croak("Can't get last insert id"); |
|
1510
|
0
|
|
|
|
|
|
return $id; |
|
1511
|
|
|
|
|
|
|
} |
|
1512
|
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
sub _col_in_group { |
|
1514
|
0
|
|
|
0
|
|
|
my ($class, $col, $group) = @_; |
|
1515
|
0
|
|
|
|
|
|
my $h = $class->__dboo_colgroups; |
|
1516
|
0
|
0
|
|
|
|
|
return if !$h; |
|
1517
|
0
|
|
|
|
|
|
return $h->{$col} eq $group; |
|
1518
|
|
|
|
|
|
|
} |
|
1519
|
|
|
|
|
|
|
|
|
1520
|
|
|
|
|
|
|
sub _croak { |
|
1521
|
0
|
|
|
0
|
|
|
Carp::croak(join("\n", @_)); |
|
1522
|
|
|
|
|
|
|
} |
|
1523
|
|
|
|
|
|
|
|
|
1524
|
|
|
|
|
|
|
sub _apply_defaults { |
|
1525
|
0
|
|
|
0
|
|
|
my ($self) = @_; |
|
1526
|
0
|
|
|
|
|
|
my $class = __T($self); |
|
1527
|
0
|
|
|
|
|
|
my $def = $class->__dboo_defaults; |
|
1528
|
0
|
0
|
0
|
|
|
|
if ($def && %$def) { |
|
1529
|
0
|
|
|
|
|
|
my $val = $self->{values}; |
|
1530
|
0
|
|
|
|
|
|
while (my ($k, $v) = each %$def) { |
|
1531
|
0
|
0
|
|
|
|
|
if (!exists $val->{$k}) { |
|
1532
|
0
|
0
|
|
|
|
|
if (ref $v eq 'CODE') { |
|
1533
|
0
|
|
|
|
|
|
$v = &$v(); |
|
1534
|
|
|
|
|
|
|
} |
|
1535
|
0
|
|
|
|
|
|
$val->{$k} = $v; |
|
1536
|
|
|
|
|
|
|
} |
|
1537
|
|
|
|
|
|
|
} |
|
1538
|
|
|
|
|
|
|
} |
|
1539
|
|
|
|
|
|
|
} |
|
1540
|
|
|
|
|
|
|
|
|
1541
|
|
|
|
|
|
|
## thanks Altblue! |
|
1542
|
|
|
|
|
|
|
sub _to_utf8 { |
|
1543
|
0
|
|
|
0
|
|
|
my ($str) = @_; |
|
1544
|
0
|
0
|
|
|
|
|
return $str |
|
1545
|
|
|
|
|
|
|
if Encode::is_utf8($str); |
|
1546
|
0
|
|
|
|
|
|
eval { |
|
1547
|
0
|
|
|
|
|
|
$str = Encode::decode_utf8($str); |
|
1548
|
|
|
|
|
|
|
}; |
|
1549
|
0
|
0
|
|
|
|
|
if ($@) { |
|
1550
|
0
|
|
|
|
|
|
$str = Encode::decode('Detect', $str); |
|
1551
|
|
|
|
|
|
|
} |
|
1552
|
0
|
|
|
|
|
|
return $str; |
|
1553
|
|
|
|
|
|
|
} |
|
1554
|
|
|
|
|
|
|
|
|
1555
|
|
|
|
|
|
|
=head2 C, C |
|
1556
|
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
Enable or disable foreign key checks in the backend DB server. These |
|
1558
|
|
|
|
|
|
|
are hard-coded in MySQL syntax for now so be careful not to use them |
|
1559
|
|
|
|
|
|
|
with other servers. ;-) |
|
1560
|
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
=cut |
|
1562
|
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
sub disable_fk_checks { |
|
1564
|
0
|
|
|
0
|
1
|
|
my ($pak) = @_; |
|
1565
|
|
|
|
|
|
|
# XXX: MySQL only for now |
|
1566
|
0
|
|
|
|
|
|
$pak->get_dbh->do('set foreign_key_checks = 0'); |
|
1567
|
|
|
|
|
|
|
} |
|
1568
|
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
sub enable_fk_checks { |
|
1570
|
0
|
|
|
0
|
1
|
|
my ($pak) = @_; |
|
1571
|
|
|
|
|
|
|
# XXX: MySQL only for now |
|
1572
|
0
|
|
|
|
|
|
$pak->get_dbh->do('set foreign_key_checks = 1'); |
|
1573
|
|
|
|
|
|
|
} |
|
1574
|
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
sub DESTROY { |
|
1576
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1577
|
0
|
|
|
|
|
|
my @a = keys %{$self->{modified}}; |
|
|
0
|
|
|
|
|
|
|
|
1578
|
0
|
0
|
|
|
|
|
if (@a) { |
|
1579
|
0
|
|
|
|
|
|
my @id = $self->id; |
|
1580
|
0
|
|
|
|
|
|
warn("Destroying ", ref $self, " with ID: ", join(':', @id), ' having uncomitted data: ', join(':', @a)); |
|
1581
|
|
|
|
|
|
|
} |
|
1582
|
|
|
|
|
|
|
} |
|
1583
|
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
## database autocreate/update facility |
|
1585
|
|
|
|
|
|
|
|
|
1586
|
|
|
|
|
|
|
=head2 C |
|
1587
|
|
|
|
|
|
|
|
|
1588
|
|
|
|
|
|
|
You can use this facility to automatically create / upgrade your |
|
1589
|
|
|
|
|
|
|
database. It takes a very simple (rudimentary even) approach, but we |
|
1590
|
|
|
|
|
|
|
found it to be useful. Here's the "big" idea. |
|
1591
|
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
package MyDB::Users; |
|
1593
|
|
|
|
|
|
|
use base 'MyDB'; |
|
1594
|
|
|
|
|
|
|
|
|
1595
|
|
|
|
|
|
|
__PACKAGE__->table('Users'); |
|
1596
|
|
|
|
|
|
|
__PACKAGE__->columns(P => [ 'id' ], |
|
1597
|
|
|
|
|
|
|
E => [qw/ first_name last_name /]); |
|
1598
|
|
|
|
|
|
|
|
|
1599
|
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
sub get_autocreate_data {q{ |
|
1601
|
|
|
|
|
|
|
#### (users:0) #### |
|
1602
|
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
CREATE TABLE Users ( id VARCHAR(32) NOT NULL PRIMARY KEY, |
|
1604
|
|
|
|
|
|
|
first_name VARCHAR(64), |
|
1605
|
|
|
|
|
|
|
last_name VARCHAR(64) ); |
|
1606
|
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
# you can put Perl comments too. |
|
1608
|
|
|
|
|
|
|
|
|
1609
|
|
|
|
|
|
|
CREATE INDEX idx_Users_first_name ON Users(first_name) |
|
1610
|
|
|
|
|
|
|
}} |
|
1611
|
|
|
|
|
|
|
|
|
1612
|
|
|
|
|
|
|
OK, now you can write this make_database.pl script: |
|
1613
|
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
/usr/bin/perl -w |
|
1615
|
|
|
|
|
|
|
|
|
1616
|
|
|
|
|
|
|
use MyDB; |
|
1617
|
|
|
|
|
|
|
MyDB->autocreate(qw( MyDB::Users )); |
|
1618
|
|
|
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
When you run this script the first time, it will create the Users |
|
1620
|
|
|
|
|
|
|
table. (An internal _dbix_oo_versions table gets created as well; |
|
1621
|
|
|
|
|
|
|
we're using it inside DBIx::OO in order to keep track of existing |
|
1622
|
|
|
|
|
|
|
table versions). Note that if you run it again, it doesn't do |
|
1623
|
|
|
|
|
|
|
anything--the database is up to date. |
|
1624
|
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
Later. You sold a billion copies of your software, customers are |
|
1626
|
|
|
|
|
|
|
happy but they are crying loud for an "email" field in their user |
|
1627
|
|
|
|
|
|
|
profiles, also wondering what was your idea to index on first_name and |
|
1628
|
|
|
|
|
|
|
not on last_name! In order to make it easy for them to upgrade their |
|
1629
|
|
|
|
|
|
|
databases, you need to modify MyDB::Users. Besides declaring the |
|
1630
|
|
|
|
|
|
|
'email' column using __PACKAGE__->columns, B the following to |
|
1631
|
|
|
|
|
|
|
your get_autocreate_data section: |
|
1632
|
|
|
|
|
|
|
|
|
1633
|
|
|
|
|
|
|
#### (users:1) #### |
|
1634
|
|
|
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
# (note that we incremented the version number) |
|
1636
|
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
# add the 'email' field |
|
1638
|
|
|
|
|
|
|
ALTER TABLE Users ADD (email VARCHAR(128)); |
|
1639
|
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
# index it |
|
1641
|
|
|
|
|
|
|
CREATE UNIQUE INDEX idx_Users_email ON Users(email); |
|
1642
|
|
|
|
|
|
|
|
|
1643
|
|
|
|
|
|
|
# and add that last_name index |
|
1644
|
|
|
|
|
|
|
CREATE INDEX idx_Users_last_name ON Users(last_name); |
|
1645
|
|
|
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
Now you can just tell your users to run make_database.pl again and |
|
1647
|
|
|
|
|
|
|
everything gets updated. |
|
1648
|
|
|
|
|
|
|
|
|
1649
|
|
|
|
|
|
|
The #### (foo:N) #### syntax is meant simply to declare an ID and a |
|
1650
|
|
|
|
|
|
|
version number. "foo" can be anything you want -- it doesn't have to |
|
1651
|
|
|
|
|
|
|
be the table name. You can actually create multiple tables, if you |
|
1652
|
|
|
|
|
|
|
need to. |
|
1653
|
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
=cut |
|
1655
|
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
sub autocreate { |
|
1657
|
0
|
|
|
0
|
1
|
|
my ($class, @packages) = @_; |
|
1658
|
0
|
|
|
|
|
|
$class->disable_fk_checks; |
|
1659
|
0
|
|
|
|
|
|
$class->transaction_start; |
|
1660
|
0
|
|
|
|
|
|
eval { |
|
1661
|
2
|
|
|
2
|
|
230262
|
use Module::Load qw( load ); |
|
|
2
|
|
|
|
|
2904
|
|
|
|
2
|
|
|
|
|
12
|
|
|
1662
|
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
# make sure _dbix_oo_versions gets created first |
|
1664
|
0
|
|
|
|
|
|
my @sql_lines = split(/^/m, get_autocreate_data()); |
|
1665
|
0
|
|
|
|
|
|
$class->__do_autocreate(@sql_lines); |
|
1666
|
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
# autocreate other packages that were passed |
|
1668
|
0
|
|
|
|
|
|
foreach my $pak (@packages) { |
|
1669
|
0
|
|
|
|
|
|
load $pak; |
|
1670
|
0
|
|
|
|
|
|
@sql_lines = split(/^/m, $pak->get_autocreate_data()); |
|
1671
|
0
|
|
|
|
|
|
$class->__do_autocreate(@sql_lines); |
|
1672
|
|
|
|
|
|
|
} |
|
1673
|
|
|
|
|
|
|
}; |
|
1674
|
0
|
0
|
|
|
|
|
if ($@) { |
|
1675
|
0
|
|
|
|
|
|
$class->transaction_rollback; |
|
1676
|
0
|
|
|
|
|
|
print STDERR "\033[1;31m- There was a problem auto-creating or upgrading tables, can't continue -\033[0m\n"; |
|
1677
|
0
|
|
|
|
|
|
die $@; |
|
1678
|
|
|
|
|
|
|
} else { |
|
1679
|
0
|
|
|
|
|
|
$class->transaction_commit; |
|
1680
|
|
|
|
|
|
|
} |
|
1681
|
0
|
|
|
|
|
|
foreach my $pak (@packages) { |
|
1682
|
0
|
|
|
|
|
|
$pak->autopopulate; |
|
1683
|
|
|
|
|
|
|
} |
|
1684
|
0
|
|
|
|
|
|
$class->enable_fk_checks; |
|
1685
|
|
|
|
|
|
|
} |
|
1686
|
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
=head2 autopopulate |
|
1688
|
|
|
|
|
|
|
|
|
1689
|
|
|
|
|
|
|
This is supposed to initialize tables. Untested and may not work -- |
|
1690
|
|
|
|
|
|
|
don't use it. |
|
1691
|
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
=cut |
|
1693
|
|
|
|
|
|
|
|
|
1694
|
0
|
|
|
0
|
1
|
|
sub autopopulate {} |
|
1695
|
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
=head2 get_autocreate_data |
|
1697
|
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
See the documentation of L. |
|
1699
|
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
=cut |
|
1701
|
|
|
|
|
|
|
|
|
1702
|
0
|
|
|
0
|
1
|
|
sub get_autocreate_data {q{ |
|
1703
|
|
|
|
|
|
|
#### (_dbix_oo_versions:0) #### |
|
1704
|
|
|
|
|
|
|
|
|
1705
|
|
|
|
|
|
|
CREATE TABLE _dbix_oo_versions ( TB_name VARCHAR(255) PRIMARY KEY, |
|
1706
|
|
|
|
|
|
|
TB_version INTEGER ); |
|
1707
|
|
|
|
|
|
|
}} |
|
1708
|
|
|
|
|
|
|
|
|
1709
|
|
|
|
|
|
|
my $AUTOCREATE_LINE_RE = qr/^\s*####\s*\(([a-z0-9_-]+):([0-9]+)\)\s*####\s*$/i; |
|
1710
|
|
|
|
|
|
|
# my $AUTOCREATE_SPLIT_SQLS = qr/^\s*##\s*$/m; |
|
1711
|
|
|
|
|
|
|
my $AUTOCREATE_SPLIT_SQLS = qr/;\s*$/m; |
|
1712
|
|
|
|
|
|
|
my $AUTOCREATE_TABLES_TABLE = '_dbix_oo_versions'; |
|
1713
|
|
|
|
|
|
|
|
|
1714
|
|
|
|
|
|
|
sub __do_autocreate { |
|
1715
|
0
|
|
|
0
|
|
|
my ($class, @lines) = @_; |
|
1716
|
|
|
|
|
|
|
|
|
1717
|
0
|
|
|
|
|
|
my $tables = $class->__autocreate_parse_lines(\@lines); |
|
1718
|
|
|
|
|
|
|
|
|
1719
|
0
|
|
|
|
|
|
my $dbh = $class->get_dbh; |
|
1720
|
0
|
|
|
|
|
|
my $sth = $dbh->table_info('', '', $AUTOCREATE_TABLES_TABLE); |
|
1721
|
0
|
|
|
|
|
|
my $existing_tables = $sth->fetchall_hashref('TABLE_NAME'); |
|
1722
|
0
|
|
|
|
|
|
my $has_version = exists $existing_tables->{$AUTOCREATE_TABLES_TABLE}; |
|
1723
|
0
|
|
|
|
|
|
$sth->finish; |
|
1724
|
|
|
|
|
|
|
|
|
1725
|
0
|
|
|
|
|
|
while (my ($t, $versions) = each %$tables) { |
|
1726
|
0
|
|
|
|
|
|
$class->__autocreate_one_table($t, $versions, $has_version); |
|
1727
|
|
|
|
|
|
|
} |
|
1728
|
|
|
|
|
|
|
} |
|
1729
|
|
|
|
|
|
|
|
|
1730
|
|
|
|
|
|
|
sub __autocreate_one_table { |
|
1731
|
0
|
|
|
0
|
|
|
my ($class, $t, $versions, $has_version) = @_; |
|
1732
|
0
|
|
|
|
|
|
my $dbh = $class->get_dbh; |
|
1733
|
0
|
|
|
|
|
|
my $cv = -1; |
|
1734
|
0
|
0
|
|
|
|
|
if ($has_version) { |
|
1735
|
0
|
|
|
|
|
|
my $sql = $dbh->prepare("SELECT TB_version FROM $AUTOCREATE_TABLES_TABLE WHERE TB_name = ?"); |
|
1736
|
0
|
|
|
|
|
|
$sql->execute($t); |
|
1737
|
0
|
|
|
|
|
|
($cv) = $sql->fetchrow_array; |
|
1738
|
0
|
|
|
|
|
|
$sql->finish; |
|
1739
|
0
|
0
|
|
|
|
|
if (!defined $cv) { |
|
1740
|
0
|
|
|
|
|
|
$cv = -1; |
|
1741
|
0
|
|
|
|
|
|
$sql = $dbh->prepare("INSERT INTO $AUTOCREATE_TABLES_TABLE (TB_name, TB_version) VALUES (?, ?)"); |
|
1742
|
0
|
|
|
|
|
|
$sql->execute($t, $cv); |
|
1743
|
0
|
|
|
|
|
|
$sql->finish; |
|
1744
|
|
|
|
|
|
|
} |
|
1745
|
|
|
|
|
|
|
} |
|
1746
|
0
|
|
|
|
|
|
my $sql_insert = $dbh->prepare("INSERT INTO $AUTOCREATE_TABLES_TABLE (TB_name, TB_version) VALUES (?, ?)"); |
|
1747
|
0
|
|
|
|
|
|
my $sql_delete = $dbh->prepare("DELETE FROM $AUTOCREATE_TABLES_TABLE WHERE TB_name = ?"); |
|
1748
|
0
|
|
|
|
|
|
foreach my $v (sort keys %$versions) { |
|
1749
|
0
|
0
|
|
|
|
|
if ($v > $cv) { |
|
1750
|
|
|
|
|
|
|
# print STDERR "$versions->{$v}\n"; |
|
1751
|
0
|
|
|
|
|
|
my @statements = split($AUTOCREATE_SPLIT_SQLS, $versions->{$v}); |
|
1752
|
0
|
|
|
|
|
|
foreach my $sql (@statements) { |
|
1753
|
0
|
|
|
|
|
|
$sql =~ s/#.*$//mg; |
|
1754
|
0
|
|
|
|
|
|
$sql =~ s/^\s+//; |
|
1755
|
0
|
|
|
|
|
|
$sql =~ s/\s+$//; |
|
1756
|
0
|
|
|
|
|
|
$sql =~ s/,\s*\)/)/g; |
|
1757
|
0
|
0
|
|
|
|
|
if ($sql) { |
|
1758
|
|
|
|
|
|
|
# print STDERR " $sql\n"; |
|
1759
|
0
|
|
|
|
|
|
my $n = index($sql, "\n"); |
|
1760
|
0
|
|
|
|
|
|
print STDERR "... $t: " . substr($sql, 0, $n) . "\n"; |
|
1761
|
0
|
|
|
|
|
|
$dbh->do($sql); |
|
1762
|
|
|
|
|
|
|
} |
|
1763
|
|
|
|
|
|
|
} |
|
1764
|
0
|
|
|
|
|
|
$sql_delete->execute($t); |
|
1765
|
0
|
|
|
|
|
|
$sql_insert->execute($t, $v); |
|
1766
|
|
|
|
|
|
|
} |
|
1767
|
|
|
|
|
|
|
} |
|
1768
|
0
|
|
|
|
|
|
$sql_insert->finish; |
|
1769
|
0
|
|
|
|
|
|
$sql_delete->finish; |
|
1770
|
|
|
|
|
|
|
} |
|
1771
|
|
|
|
|
|
|
|
|
1772
|
|
|
|
|
|
|
sub __autocreate_parse_lines { |
|
1773
|
0
|
|
|
0
|
|
|
my ($class, $lines) = @_; |
|
1774
|
0
|
|
|
|
|
|
my ($h, $ct, $cv, $cs) = ({}, undef, undef, undef); |
|
1775
|
|
|
|
|
|
|
my $doit = sub { |
|
1776
|
0
|
0
|
|
0
|
|
|
if (defined $ct) { |
|
1777
|
0
|
|
0
|
|
|
|
$h->{$ct} ||= {}; |
|
1778
|
0
|
|
|
|
|
|
$cs =~ s/^\s+//; |
|
1779
|
0
|
|
|
|
|
|
$cs =~ s/\s+$//; |
|
1780
|
0
|
|
|
|
|
|
$h->{$ct}{$cv} = $cs; |
|
1781
|
|
|
|
|
|
|
} |
|
1782
|
0
|
|
|
|
|
|
}; |
|
1783
|
0
|
|
|
|
|
|
foreach my $i (@$lines) { |
|
1784
|
0
|
0
|
|
|
|
|
if ($i =~ $AUTOCREATE_LINE_RE) { |
|
|
|
0
|
|
|
|
|
|
|
1785
|
0
|
|
|
|
|
|
&$doit; |
|
1786
|
0
|
|
|
|
|
|
$ct = $1; |
|
1787
|
0
|
|
|
|
|
|
$cv = $2; |
|
1788
|
0
|
|
|
|
|
|
$cs = ''; |
|
1789
|
|
|
|
|
|
|
} elsif (defined $ct) { |
|
1790
|
0
|
|
|
|
|
|
$cs .= $i; |
|
1791
|
|
|
|
|
|
|
} |
|
1792
|
|
|
|
|
|
|
} |
|
1793
|
0
|
|
|
|
|
|
&$doit; |
|
1794
|
|
|
|
|
|
|
# print STDERR Data::Dumper::Dumper($h); |
|
1795
|
0
|
|
|
|
|
|
return $h; |
|
1796
|
|
|
|
|
|
|
} |
|
1797
|
|
|
|
|
|
|
|
|
1798
|
|
|
|
|
|
|
=head1 CAVEATS |
|
1799
|
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
There are a number of problems you might encounter, mostly related to |
|
1801
|
|
|
|
|
|
|
the fact that we don't cache objects. |
|
1802
|
|
|
|
|
|
|
|
|
1803
|
|
|
|
|
|
|
=head2 Concurrent objects |
|
1804
|
|
|
|
|
|
|
|
|
1805
|
|
|
|
|
|
|
$u1 = Users->retrieve('foo'); |
|
1806
|
|
|
|
|
|
|
$u2 = Users->retrieve('foo'); |
|
1807
|
|
|
|
|
|
|
|
|
1808
|
|
|
|
|
|
|
C<$u1> and C<$u2> now point to different objects, but both point to |
|
1809
|
|
|
|
|
|
|
the same record in the database. Now the problem: |
|
1810
|
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
$u1->first_name('Foo'); |
|
1812
|
|
|
|
|
|
|
$u2->first_name('Bar'); |
|
1813
|
|
|
|
|
|
|
$u1->update; |
|
1814
|
|
|
|
|
|
|
|
|
1815
|
|
|
|
|
|
|
Which one gets set? 'Foo', but $u2 has uncommitted changes. When you |
|
1816
|
|
|
|
|
|
|
further say $u2->update, it will set the name to 'Bar'. If you say |
|
1817
|
|
|
|
|
|
|
$u2->revert, it will revert to whatever was there I 'Foo'. |
|
1818
|
|
|
|
|
|
|
This can lead to potential problems. |
|
1819
|
|
|
|
|
|
|
|
|
1820
|
|
|
|
|
|
|
Class::DBI (almost) doesn't have this problem (it can appear when you |
|
1821
|
|
|
|
|
|
|
have multiple processes accessing the database concurrently, such as |
|
1822
|
|
|
|
|
|
|
httpd processes). |
|
1823
|
|
|
|
|
|
|
|
|
1824
|
|
|
|
|
|
|
=head1 UNDER THE HOOD |
|
1825
|
|
|
|
|
|
|
|
|
1826
|
|
|
|
|
|
|
A DBIx::OO object is a hash blessed into the DBIx::OO package. |
|
1827
|
|
|
|
|
|
|
The hash currently contains 2 keys: |
|
1828
|
|
|
|
|
|
|
|
|
1829
|
|
|
|
|
|
|
=over |
|
1830
|
|
|
|
|
|
|
|
|
1831
|
|
|
|
|
|
|
=item B |
|
1832
|
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
A hash containing the field => value pairs that are currently |
|
1834
|
|
|
|
|
|
|
retrieved from the database. |
|
1835
|
|
|
|
|
|
|
|
|
1836
|
|
|
|
|
|
|
=item B |
|
1837
|
|
|
|
|
|
|
|
|
1838
|
|
|
|
|
|
|
Another hash that maps field_name => 'original value' for the fields |
|
1839
|
|
|
|
|
|
|
that were modified and not yet committed of the current object. |
|
1840
|
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
=back |
|
1842
|
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
If a field is not present in B and is requested with get(), |
|
1844
|
|
|
|
|
|
|
then the database will be queried for it and for all other fields that |
|
1845
|
|
|
|
|
|
|
aren't present in "values" but are listed in the Bssential group. |
|
1846
|
|
|
|
|
|
|
|
|
1847
|
|
|
|
|
|
|
If a field is present in B, then it will be saved in the DB |
|
1848
|
|
|
|
|
|
|
on the next update() call. An object can discard these operations |
|
1849
|
|
|
|
|
|
|
with the discard() method. Discard restores the values using those |
|
1850
|
|
|
|
|
|
|
stored in the C hash. |
|
1851
|
|
|
|
|
|
|
|
|
1852
|
|
|
|
|
|
|
Each operation plays around these hashes. For instance, when you call |
|
1853
|
|
|
|
|
|
|
search(), a single SQL will run and then we'll iterate over the |
|
1854
|
|
|
|
|
|
|
results, create objects and assign the SELECT-ed values to the |
|
1855
|
|
|
|
|
|
|
B hash. |
|
1856
|
|
|
|
|
|
|
|
|
1857
|
|
|
|
|
|
|
A retrieve() operation creates a new object and assign the passed |
|
1858
|
|
|
|
|
|
|
value to its primary key, then it will call the internal |
|
1859
|
|
|
|
|
|
|
_retrieve_columns([ 'P', 'E' ]) function in order to fetch essential |
|
1860
|
|
|
|
|
|
|
object data from the DB. Note that a call to _retrieve_columns is not |
|
1861
|
|
|
|
|
|
|
actually necessary, since it will happen anyway the first time you |
|
1862
|
|
|
|
|
|
|
want to retrieve a field that doesn't exist in B -- but it's |
|
1863
|
|
|
|
|
|
|
good to call it because retrieve() should return B if the |
|
1864
|
|
|
|
|
|
|
object can't be found in the DB. |
|
1865
|
|
|
|
|
|
|
|
|
1866
|
|
|
|
|
|
|
=head1 BUGS |
|
1867
|
|
|
|
|
|
|
|
|
1868
|
|
|
|
|
|
|
Yeah, the documentation sucks. Other bugs? |
|
1869
|
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
1871
|
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
L, L, L |
|
1873
|
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1875
|
|
|
|
|
|
|
|
|
1876
|
|
|
|
|
|
|
Mihai Bazon, |
|
1877
|
|
|
|
|
|
|
http://www.dynarch.com/ |
|
1878
|
|
|
|
|
|
|
http://www.bazon.net/mishoo/ |
|
1879
|
|
|
|
|
|
|
|
|
1880
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
1881
|
|
|
|
|
|
|
|
|
1882
|
|
|
|
|
|
|
Copyright (c) Mihai Bazon 2006. All rights reserved. |
|
1883
|
|
|
|
|
|
|
|
|
1884
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or modify it |
|
1885
|
|
|
|
|
|
|
under the same terms as Perl itself. |
|
1886
|
|
|
|
|
|
|
|
|
1887
|
|
|
|
|
|
|
=head1 THANKS |
|
1888
|
|
|
|
|
|
|
|
|
1889
|
|
|
|
|
|
|
I'd like to thank irc.n0i.net -- our small but wonderful community |
|
1890
|
|
|
|
|
|
|
that's always there when you need it. |
|
1891
|
|
|
|
|
|
|
|
|
1892
|
|
|
|
|
|
|
=head1 DISCLAIMER OF WARRANTY |
|
1893
|
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
|
1895
|
|
|
|
|
|
|
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT |
|
1896
|
|
|
|
|
|
|
WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER |
|
1897
|
|
|
|
|
|
|
PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, |
|
1898
|
|
|
|
|
|
|
EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE |
|
1899
|
|
|
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
1900
|
|
|
|
|
|
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE |
|
1901
|
|
|
|
|
|
|
SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME |
|
1902
|
|
|
|
|
|
|
THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. |
|
1903
|
|
|
|
|
|
|
|
|
1904
|
|
|
|
|
|
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
|
1905
|
|
|
|
|
|
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
|
1906
|
|
|
|
|
|
|
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE |
|
1907
|
|
|
|
|
|
|
TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR |
|
1908
|
|
|
|
|
|
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE |
|
1909
|
|
|
|
|
|
|
SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING |
|
1910
|
|
|
|
|
|
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A |
|
1911
|
|
|
|
|
|
|
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF |
|
1912
|
|
|
|
|
|
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
|
1913
|
|
|
|
|
|
|
DAMAGES. |
|
1914
|
|
|
|
|
|
|
|
|
1915
|
|
|
|
|
|
|
=cut |
|
1916
|
|
|
|
|
|
|
|
|
1917
|
|
|
|
|
|
|
|
|
1918
|
|
|
|
|
|
|
|
|
1919
|
|
|
|
|
|
|
|
|
1920
|
|
|
|
|
|
|
|
|
1921
|
|
|
|
|
|
|
|
|
1922
|
|
|
|
|
|
|
|
|
1923
|
|
|
|
|
|
|
package SQL::Abstract::WithLimit; |
|
1924
|
2
|
|
|
2
|
|
2564
|
use base 'SQL::Abstract'; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
1066
|
|
|
1925
|
|
|
|
|
|
|
|
|
1926
|
|
|
|
|
|
|
### MySQL and Postgres syntax; Buzz off for others. :-p |
|
1927
|
|
|
|
|
|
|
sub select { |
|
1928
|
0
|
|
|
0
|
|
|
my ($self, $table, $cols, $where, $order, $limit, $offset) = @_; |
|
1929
|
0
|
|
|
|
|
|
my ($sql, @bind) = $self->SUPER::select($table, $cols, $where, $order); |
|
1930
|
0
|
|
|
|
|
|
$sql .= $self->order_and_limit(undef, $limit, $offset); |
|
1931
|
0
|
0
|
|
|
|
|
return wantarray ? ($sql, @bind) : $sql; |
|
1932
|
|
|
|
|
|
|
} |
|
1933
|
|
|
|
|
|
|
|
|
1934
|
|
|
|
|
|
|
sub _order_by { |
|
1935
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1936
|
0
|
|
|
|
|
|
my $ref = ref $_[0]; |
|
1937
|
|
|
|
|
|
|
|
|
1938
|
0
|
|
|
|
|
|
my @vals = $ref eq 'ARRAY' ? @{$_[0]} : |
|
|
0
|
|
|
|
|
|
|
|
1939
|
0
|
0
|
|
|
|
|
$ref eq 'SCALAR' ? ${$_[0]} : |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1940
|
|
|
|
|
|
|
$ref eq '' ? $_[0] : |
|
1941
|
|
|
|
|
|
|
SQL::Abstract::puke("Unsupported data struct $ref for ORDER BY"); |
|
1942
|
|
|
|
|
|
|
|
|
1943
|
0
|
0
|
|
|
|
|
my $val = join ', ', map { |
|
1944
|
0
|
|
|
|
|
|
s/^\^// ? |
|
1945
|
|
|
|
|
|
|
$self->_quote($_) . $self->_sqlcase(' desc') |
|
1946
|
|
|
|
|
|
|
: $self->_quote($_) |
|
1947
|
|
|
|
|
|
|
} @vals; |
|
1948
|
0
|
0
|
|
|
|
|
return $val ? $self->_sqlcase(' order by')." $val" : ''; |
|
1949
|
|
|
|
|
|
|
} |
|
1950
|
|
|
|
|
|
|
|
|
1951
|
|
|
|
|
|
|
sub order_and_limit { |
|
1952
|
0
|
|
|
0
|
|
|
my ($self, $order, $limit, $offset) = @_; |
|
1953
|
0
|
0
|
|
|
|
|
my $q = $order ? $self->_order_by($order) : ''; |
|
1954
|
0
|
0
|
|
|
|
|
$q .= " LIMIT $limit" |
|
1955
|
|
|
|
|
|
|
if defined $limit; |
|
1956
|
0
|
0
|
|
|
|
|
$q .= " OFFSET $offset" |
|
1957
|
|
|
|
|
|
|
if defined $offset; |
|
1958
|
0
|
|
|
|
|
|
return $q; |
|
1959
|
|
|
|
|
|
|
} |
|
1960
|
|
|
|
|
|
|
|
|
1961
|
|
|
|
|
|
|
*quote_field = \&SQL::Abstract::_quote; |