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
|
|
|
|
|
|
|
|