File Coverage

blib/lib/DBR/Config/ScanDB.pm
Criterion Covered Total %
statement 116 123 94.3
branch 37 74 50.0
condition 10 17 58.8
subroutine 11 11 100.0
pod 0 7 0.0
total 174 232 75.0


line stmt bran cond sub pod time code
1             # the contents of this file are Copyright (c) 2009 Daniel Norman
2             # This program is free software; you can redistribute it and/or
3             # modify it under the terms of the GNU General Public License as
4             # published by the Free Software Foundation.
5              
6             package DBR::Config::ScanDB;
7              
8 18     18   13412 use strict;
  18         52  
  18         1279  
9 18     18   154 use base 'DBR::Common';
  18         45  
  18         1924  
10 18     18   121 use DBR::Config::Field;
  18         43  
  18         545  
11 18     18   120 use DBR::Config::Schema;
  18         36  
  18         52953  
12              
13             sub new {
14 17     17 0 52 my( $package ) = shift;
15 17         710 my %params = @_;
16 17         193 my $self = {
17             session => $params{session},
18             conf_instance => $params{conf_instance},
19             scan_instance => $params{scan_instance},
20             };
21              
22 17         61 bless( $self, $package );
23              
24 17 50       276 return $self->_error('session object must be specified') unless $self->{session};
25 17 50       5771 return $self->_error('conf_instance object must be specified') unless $self->{conf_instance};
26 17 50       81 return $self->_error('scan_instance object must be specified') unless $self->{scan_instance};
27              
28 17 50       99 $self->{schema_id} = $self->{scan_instance}->schema_id or
29             return $self->_error('Cannot scan an instance that has no schema');
30              
31 17         85 return( $self );
32             }
33              
34             sub scan{
35 17     17 0 115 my $self = shift;
36 17         206 my %params = @_;
37              
38 17   50     110 my $tables = $self->scan_tables() || die "failed to scan tables";
39 17         111 my $pkeys = $self->scan_pkeys(); # || die "failed to scan primary keys";
40              
41 17         151 foreach my $table (@{$tables}){
  17         75  
42 41 50       195 print "Scanning $table\n" if $params{pretty};
43            
44 41 50       285 my $fields = $self->scan_fields($table) or return $self->_error( "failed to describe table" );
45 41 50       285 my $pkey = $pkeys ? $pkeys->{$table} : $self->scan_pkeys( $table );
46              
47 41 50       238 $self->update_table($fields,$table,$pkey) or return $self->_error("failed to update table");
48             }
49              
50             #HACK - the scanner should load up the in-memory representation at the same time
51             DBR::Config::Schema->load(
52 17 50       320 session => $self->{session},
53             schema_id => $self->{schema_id},
54             instance => $self->{conf_instance},
55             ) or die "Failed to reload schema";
56              
57 17         435 return 1;
58             }
59              
60              
61             sub scan_pkeys {
62 17     17 0 46 my $self = shift;
63 17         45 my $table = shift; # undef for all
64             #print "SCAN_PKEYS called with table=[$table]\n";
65              
66 17   50     207 my $dbh = $self->{scan_instance}->connect('dbh') || die "failed to connect to scanned db";
67              
68 17         45 my $sth;
69 17         745 local $dbh->{PrintError} = 0;
70 17         118 local $^W = 0;
71 17         47 eval { $sth= $dbh->primary_key_info(undef,undef,$table); };
  17         377  
72 17 50       201748 return $self->_error('failed call to primary_key_info') unless $sth;
73            
74 17         71 my %map = ();
75 17         324 while (my $row = $sth->fetchrow_hashref()) {
76 41 50       1324 next unless $row->{PK_NAME} eq 'PRIMARY KEY';
77 41 50       140 my $table = $row->{TABLE_NAME} or return $self->_error('no TABLE_NAME!');
78 41 50       153 my $field = $row->{COLUMN_NAME} or return $self->_error('no COLUMN_NAME!');
79 41         2103 $map{$table}->{$field} = 1;
80             }
81              
82 17         790 $sth->finish();
83              
84 17         1370 return \%map;
85             }
86              
87             sub scan_tables{
88 17     17 0 40 my $self = shift;
89 17   50     96 my $dbh = $self->{scan_instance}->connect('dbh') || die "failed to connect to scanned db";
90              
91 17 50       241 return $self->_error('failed call to table_info') unless
92             my $sth = $dbh->table_info;
93              
94 17         37725 my @tables;
95 17         648 while (my $row = $sth->fetchrow_hashref()) {
96 90 50       303 my $name = $row->{TABLE_NAME} or return $self->_error('Table entry has no name!');
97              
98 90 100       806 next if ($name eq 'sqlite_sequence'); # Ugly
99              
100 75 100       3157 if($row->{TABLE_TYPE} eq 'TABLE'){
101 41         1291 push @tables, $name;
102             }
103             }
104              
105 17         143 $sth->finish();
106              
107 17         601 return \@tables;
108             }
109              
110             sub scan_fields{
111 41     41 0 576 my $self = shift;
112 41         1487 my $table = shift;
113              
114 41   50     458 my $dbh = $self->{scan_instance}->connect('dbh') || die "failed to connect to scanned db";
115              
116 41 50       519 return $self->_error('failed call to column_info') unless
117             my $sth = $dbh->column_info( undef, undef, $table, undef );
118              
119 41         120343 my @rows;
120 41         757 while (my $row = $sth->fetchrow_hashref()) {
121 172         5968 push @rows, $row;
122             }
123              
124 41         890 $sth->finish();
125              
126 41         2333 return \@rows;
127             }
128              
129             sub update_table{
130 41     41 0 89 my $self = shift;
131 41         83 my $fields = shift;
132 41         101 my $name = shift;
133 41         182 my $pkey = shift;
134              
135 41   50     255 my $dbh = $self->{conf_instance}->connect || die "failed to connect to config db";
136              
137 41 50       582 return $self->_error('failed to select from dbr_tables') unless
138             my $tables = $dbh->select(
139             -table => 'dbr_tables',
140             -fields => 'table_id schema_id name',
141             -where => {
142             schema_id => ['d',$self->{schema_id}],
143             name => $name,
144             }
145             );
146              
147 41         233 my $table = $tables->[0];
148              
149 41         78 my $table_id;
150 41 50       134 if($table){ # update
151 0         0 $table_id = $table->{table_id};
152             }else{
153 41 50       608 return $self->_error('failed to insert into dbr_tables') unless
154             $table_id = $dbh->insert(
155             -table => 'dbr_tables',
156             -fields => {
157             schema_id => ['d',$self->{schema_id}],
158             name => $name,
159             }
160             );
161             }
162              
163 41 50       458 $self->update_fields($fields,$table_id,$pkey) or return $self->_error('Failed to update fields');
164              
165 41         368 return 1;
166             }
167              
168              
169             sub update_fields{
170 41     41 0 193 my $self = shift;
171 41         83 my $fields = shift;
172 41         108 my $table_id = shift;
173 41         143 my $pkey_map = shift;
174              
175 41   50     228 my $dbh = $self->{conf_instance}->connect || die "failed to connect to config db";
176              
177 41 50       496 return $self->_error('failed to select from dbr_fields') unless
178             my $records = $dbh->select(
179             -table => 'dbr_fields',
180             -fields => 'field_id table_id name data_type is_nullable is_signed max_value',
181             -where => {
182             table_id => ['d',$table_id]
183             }
184             );
185              
186 41         263 my %fieldmap;
187 41         94 map {$fieldmap{$_->{name}} = $_} @{$records};
  0         0  
  41         150  
188              
189 41         1593 foreach my $field (@{$fields}) {
  41         140  
190 172 50       1230 my $name = $field->{'COLUMN_NAME'} or return $self->_error('No COLUMN_NAME is present');
191 172 50       975 my $type = $field->{'TYPE_NAME'} or return $self->_error('No TYPE_NAME is present' );
192 172         356 my $size = $field->{'COLUMN_SIZE'};
193              
194 172         407 my $nullable = $field->{'NULLABLE'};
195 172 50       463 return $self->_error('No NULLABLE is present' ) unless defined($nullable);
196              
197 172   66     1215 my $pkey = $field->{'mysql_is_pri_key'} || $pkey_map->{$name};
198 172         305 my $extra = $field->{'mysql_type_name'};
199              
200 172         254 my $is_signed = 0;
201 172 50       432 if(defined $extra){
202 0 0       0 $is_signed = ($extra =~ / unsigned/i)?0:1;
203 0 0       0 $is_signed = 0 if $type =~ /unsigned/i; #Lame... SQLite sometimes returns the data type as 'int unsigned'
204             }
205 172         14486 $type =~ /^\s+|\s+$/g;
206 172         997 ($type) = split (/\s+/,$type);
207 172 50       1338 my $typeid = DBR::Config::Field->get_type_id($type) or die( "Invalid type '$type'" );
208              
209 172         349 my $record = $fieldmap{$name};
210              
211 172 100 100     2480 my $ref = {
212             is_nullable => ['d', $nullable ? 1:0 ],
213             is_signed => ['d', $is_signed ],
214             data_type => ['d', $typeid ],
215             max_value => ['d', $size || 0 ],
216             };
217              
218 172 100       636 if(defined($pkey)){
219 41 50       221 $ref->{is_pkey} = ['d', $pkey ? 1:0 ],
220             }
221              
222 172 50       375 if ($record) { # update
223 0 0       0 return $self->_error('failed to insert into dbr_tables') unless
224             $dbh->update(
225             -table => 'dbr_fields',
226             -fields => $ref,
227             -where => { field_id => ['d',$record->{field_id}] },
228             );
229             } else {
230 172         2238 $ref->{name} = $name;
231 172         550 $ref->{table_id} = ['d', $table_id ];
232              
233 172 50       1309 return $self->_error('failed to insert into dbr_tables') unless
234             my $field_id = $dbh->insert(
235             -table => 'dbr_fields',
236             -fields => $ref,
237             );
238             }
239              
240 172         1605 delete $fieldmap{$name};
241              
242             }
243              
244 41         192 foreach my $name (keys %fieldmap) {
245 0         0 my $record = $fieldmap{$name};
246              
247 0 0       0 return $self->_error('failed to delete from dbr_tables') unless
248             $dbh->delete(
249             -table => 'dbr_fields',
250             -where => { field_id => ['d',$record->{field_id}] },
251             );
252             }
253              
254 41         359 return 1;
255             }
256              
257             1;