File Coverage

blib/lib/DBIx/CopyRecord.pm
Criterion Covered Total %
statement 30 121 24.7
branch 1 38 2.6
condition 1 6 16.6
subroutine 9 14 64.2
pod 0 4 0.0
total 41 183 22.4


line stmt bran cond sub pod time code
1             package DBIx::CopyRecord;
2              
3 1     1   20290 use strict;
  1         2  
  1         33  
4 1     1   2894 use DBI;
  1         23464  
  1         81  
5              
6             BEGIN {
7 1     1   13 use Carp;
  1         7  
  1         113  
8 1     1   6 use Exporter ();
  1         2  
  1         31  
9 1     1   5 use vars qw($VERSION $PACKAGE $AUTOLOAD);
  1         2  
  1         92  
10 1     1   2 $VERSION = '1.01';
11              
12 1         1 $Carp::CarpLevel = 1;
13 1         221 $PACKAGE = "DBIx::CopyRecord";
14              
15             #debug constant
16 1     1   5 use constant DEBUG => 0;
  1         3  
  1         86  
17              
18             }
19              
20             sub new {
21 1     1 0 12 my ( $class, @args ) = @_;
22              
23 1   33     8 my $self = bless( {}, ref($class) || $class );
24              
25 1 50       5 if ( !defined $args[0] ) {
26 0         0 croak "$PACKAGE->new requires one value. \$dbh\n";
27             }
28 1         6 $self->{_dbh} = $args[0];
29              
30 1         1 if (DEBUG) {
31             select (STDOUT);
32             $| = 1;
33 1     1   1267 use Data::Dumper;
  1         16432  
  1         1540  
34             }
35              
36 1         3 return $self;
37             }
38              
39 0     0     sub DESTROY () {
40             }
41              
42             =head1 NAME
43              
44             DBIx::CopyRecord - copy record(s) while maintaining referential integrity within a database.
45              
46             =head1 DESCRIPTION
47              
48             This module can copy record(s) while maintaining referential integrity within a database. The C method is all that's needed. It's useful for copying
49             related record(s) and assigning a new key value to the new record(s).
50              
51             You can define all of the relationships in the arguments to the copy command. For example, if your DB is not using foreign keys. Or, simply tell the method what the name of the foreign key is and the module will do the rest.
52              
53             The copy method will return the assigned key value so that you can use it.
54              
55             =head1 USAGE
56              
57             use DBIx::CopyRecord;
58             my $CR = DBIx::CopyRecord->new( DB HANDLE );
59              
60             RV = $CR->copy(
61             { table_name => TABLE NAME,
62             primary_key => PRIMARY KEY COLUMN,
63             primary_key_value => VALUE, NULL or SELECT,
64             where => WHERE CONDITION,
65             override => {
66             billed = 'N',
67             invoice_date = 'NULL'
68             }
69             child => [ { table_name => CHILD TABLE NAME,
70             primary_key => CHILD PRIMARY KEY COLUMN,
71             primary_key_value => CHILD VALUE, NULL or SELECT,
72             foreign_key => COLUMN NAME OF },
73             { table_name => CHILD TABLE NAME } ] });
74              
75              
76             Child table_name entry without additional arguments will attempt to figure out
77             the primary key and foreign key from the database.
78              
79             =cut
80              
81             sub copy() {
82              
83 0     0 0   my $self = shift;
84 0           my ($args) = @_;
85 0           my ( $key_value, $parent, $child);
86              
87 0 0         if ( !defined $args ) {
88 0           croak "$PACKAGE->copy requires one value. \n";
89             }
90 0           $self->{args} = get_real_values($args);
91 0           $self->check_required_fields();
92              
93             ### Process parent record
94 0 0         if ( $self->{args}->{parent} ) {
95 0           $parent = $self->{args}->{parent} ;
96 0           $self->{new_key_value} = $self->_copy($parent);
97             }
98              
99             ### Process child records
100 0 0         if ($self->{args}->{child}) {
101 0           foreach $child (@{$self->{args}->{child}}) {
  0            
102              
103             ### If there's no child where clause use parent
104 0 0         if ( ! defined $$child{where} ) {
105 0           $$child{where} = $$parent{where};
106             }
107              
108             ### If there's no child foreign_key value use returned value from parent
109 0 0         if ( ! defined $$child{foreign_key_value} ) {
110 0           $$child{foreign_key_value} = $self->{new_key_value};
111             }
112              
113 0           $self->_copy($child);
114             }
115             }
116              
117 0           return $self->{new_key_value};
118             }
119              
120             #
121             # Actual work is done here.
122             #
123             sub _copy() {
124 0     0     my $self = shift;
125 0           my ($args) = @_;
126 0           my ( @field_name_list, @field_value_list, $assigned_id );
127             my (
128 0           $select_query_sql, $select_queryh, $insert_query_sql,
129             $insert_queryh, $field_part, $value_part,
130             $record_hashref, $field_name, $field_value
131             );
132              
133             # Select all columns from source table
134 0           $select_query_sql = qq(
135             SELECT *
136             FROM $$args{table_name}
137             WHERE $$args{where} );
138              
139 0           print STDERR "\n$select_query_sql\n" if DEBUG;
140              
141 0           $select_queryh = $self->{_dbh}->prepare($select_query_sql);
142 0           $select_queryh->execute();
143              
144             ### Loop through all matching records
145 0           while ( $record_hashref = $select_queryh->fetchrow_hashref ) {
146              
147             ### Initialize
148 0           $field_part = '';
149 0           $value_part = '';
150 0           $insert_query_sql = '';
151 0           @field_name_list = ();
152 0           @field_value_list = ();
153              
154             ### Override what needs to be
155 0 0         if ( $$args{override} ) {
156 0           my $override = $$args{override};
157 0           $override = get_real_values($override);
158              
159 0           foreach ( keys %$override ) {
160 0           print STDERR "Reassigning: $_ from $$record_hashref{$_} to $$override{$_}\n" if DEBUG;
161 0 0         if ( $$override{$_} ne 'NULL' ) {
162 0           $$record_hashref{$_} = $$override{$_};
163             }
164             else {
165 0           delete $$record_hashref{$_};
166             }
167             }
168             }
169              
170             ### Process foreign key
171 0 0         if ( $$args{foreign_key_value} ) {
172 0           my $foreign_key_value = $$args{foreign_key_value};
173 0           $foreign_key_value = get_real_values($foreign_key_value);
174 0           $$record_hashref{$$args{foreign_key}} = $$args{foreign_key_value};
175             }
176              
177             ### Get CHAR field names
178 0           my $sth = $self->{_dbh}->column_info( undef, undef, $$args{table_name}, "%" );
179 0           my $cnames = $sth->fetchall_hashref("COLUMN_NAME");
180              
181 0           while ( ( $field_name, $field_value ) = each %$record_hashref ) {
182 0 0 0       if ( $field_name ne $$args{primary_key} || $$args{primary_key_value} ne 'NULL' ) {
183 0 0         if ( $$cnames{$field_name}{TYPE_NAME} =~ /[CHAR|DATE|TIME]/ ) {
184 0           $field_value = qq('$field_value'); ### Enclose CHAR fields in quotes
185             }
186 0           push( @field_name_list, $field_name );
187 0           push( @field_value_list, $field_value );
188             }
189             }
190              
191 0           $field_part = join( ', ', @field_name_list );
192 0           $value_part = join( ', ', @field_value_list );
193              
194             ### insert new record
195 0           $insert_query_sql = qq( INSERT INTO $$args{table_name} ( $field_part ) VALUES ( $value_part ) );
196              
197 0           print STDERR "$insert_query_sql\n" if DEBUG;
198              
199 0           $insert_queryh = $self->{_dbh}->prepare($insert_query_sql);
200 0           $insert_queryh->execute();
201             }
202              
203 0 0         if ( lc $self->{_dbh}->{Driver}->{Name} eq 'mysql' ){
204 0           my $select_idh = $self->{_dbh}->prepare("SELECT LAST_INSERT_ID()");
205 0           $select_idh->execute();
206 0           $assigned_id = $select_idh->fetchrow();
207             }
208 0           return $assigned_id;
209             }
210              
211             sub check_required_fields {
212 0     0 0   my $self = shift;
213 0           my %required_fields_list = (
214             parent => ['table_name', 'primary_key', 'where' ],
215             child => ['table_name', 'primary_key', 'primary_key_value', 'foreign_key']
216             );
217 0           my ($child, $test_value, $required);
218              
219 0 0         if ( $self->{args}->{parent} ) {
220 0           foreach $required (@{$required_fields_list{parent}}){
  0            
221 0           print "Checking:$required in parent. Value is $self->{args}->{parent}->{$required}\n";
222 0           $test_value = $self->{args}->{parent}->{$required} ;
223 0 0         if (! $test_value ) {
224 0           croak "$PACKAGE: $required is required in parent.\n";
225             }
226             }
227             }
228              
229 0 0         if ($self->{args}->{child}) {
230 0           foreach $child (@{$self->{args}->{child}}) {
  0            
231 0           foreach $required (@{$required_fields_list{child}}){
  0            
232 0           print "Checking:$required in child. Value is $$child{$required}\n";
233 0           $test_value = $$child{$required} ;
234 0 0         if (! $test_value ) {
235 0           croak "$PACKAGE: $required is required in child.\n";
236             }
237             }
238             }
239             }
240             }
241              
242             sub get_real_values {
243 0     0 0   my $args = shift;
244              
245 0 0         if ( not ref $args ) {
    0          
    0          
246 0           $args;
247             }
248             elsif ( ref $args eq "ARRAY" ) {
249 0           [ map get_real_values($_), @$args ];
250             }
251             elsif ( ref $args eq "HASH" ) {
252 0           +{ map { $_ => get_real_values( $args->{$_} ) } keys %$args };
  0            
253             }
254             }
255              
256             =head1 AUTHOR
257              
258             Jack Bilemjian
259              
260             =head1 COPYRIGHT
261              
262             This program is free software; you can redistribute
263             it and/or modify it under the same terms as Perl itself.
264              
265             The full text of the license can be found in the
266             LICENSE file included with this module.
267              
268              
269             =head1 SEE ALSO
270              
271             DBI(1).
272              
273             =cut
274              
275             1;
276