File Coverage

blib/lib/Apache/Voodoo/Table/Probe/MySQL.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             ################################################################################
2             #
3             # Apache::Voodoo::Table::Probe::mysql
4             #
5             # Probes a MySQL database to get information about various tables.
6             #
7             # This is old and crufty and not for public use
8             #
9             ################################################################################
10             package Apache::Voodoo::Table::Probe::MySQL;
11              
12             $VERSION = "3.0200";
13              
14 1     1   1201 use strict;
  1         3  
  1         38  
15 1     1   4 use warnings;
  1         2  
  1         33  
16              
17 1     1   6 use DBI;
  1         3  
  1         32  
18 1     1   478 use Tie::Hash::Indexed;
  0            
  0            
19              
20             our $DEBUG = 0;
21              
22             sub new {
23             my $class = shift;
24             my $self = {};
25              
26             $self->{'dbh'} = shift;
27              
28             bless $self, $class;
29             return $self;
30             }
31              
32             sub list_tables {
33             my $self = shift;
34              
35             my $res = $self->{'dbh'}->selectall_arrayref("show tables") || die $DBI::errstr;
36              
37             return map { $_->[0] } @{$res};
38             }
39              
40             sub probe_table {
41             my $self = shift;
42              
43             my $table = shift;
44              
45             my $dbh = $self->{'dbh'};
46              
47             tie my %data, 'Tie::Hash::Indexed';
48              
49             $data{table} = $table;
50             $data{primary_key} = '';
51              
52             tie my %columns, 'Tie::Hash::Indexed';
53             $data{columns} = \%columns;
54              
55             # get foreign key infomation about the given table
56             my $db_name = $dbh->{'Name'};
57             $db_name =~ s/:.*//;
58             my $sth = $dbh->foreign_key_info(undef,undef,undef,undef,$db_name,$table) || die DBI->errstr;
59             my %foreign_keys;
60             foreach (@{$sth->fetchall_arrayref()}) {
61             next unless $_->[2]; # not a foreign key
62             $foreign_keys{$_->[7]} = [ $_->[2], $_->[3] ];
63             }
64              
65             # Sadly the column_info method doesn't tell us if the column is auto increment or not.
66             # So we're going after the column info using ye olde explain.
67             my $table_info = $dbh->selectall_arrayref("explain $table") || return { 'ERRORS' => [ "explain of table $table failed. $DBI::errstr" ] };
68             foreach my $row (@{$table_info}) {
69             my $name = $row->[0];
70              
71             tie my %column, 'Tie::Hash::Indexed';
72              
73             #
74             # figure out the column type
75             #
76             my $type = $row->[1];
77             my ($size) = ($type =~ /\(([\d,]+)\)/);
78              
79             $type =~ s/[,\d\(\) ]+/_/g;
80             $type =~ s/_$//g;
81              
82             if ($self->can($type)) {
83             $self->$type(\%column,$size);
84             }
85             else {
86             push(@{$data{'ERRORS'}},"unsupported type $row->[1]");
87             }
88              
89             # is this param required for add / edit (does the column allow nulls)
90             $column{'required'} = 1 unless $row->[2] eq "YES";
91              
92             if ($row->[3] eq "PRI") {
93             # primary key. NOTE THAT CLUSTERED PRIMARY KEYS ARE NOT SUPPORTED
94             $data{'primary_key'} = $name;
95              
96             # is the primary key user supplied
97             unless ($row->[5] eq "auto_increment") {
98             $data{'pkey_user_supplied'} = 1;
99             }
100             }
101             elsif ($row->[3] eq "UNI") {
102             # unique index.
103             $column{'unique'} = 1;
104             }
105              
106             #
107             # figure out foreign keys
108             #
109             my $ref_table = '';
110             my $ref_id = '';
111             if (scalar(%foreign_keys)) {
112             # there are foreign keys defined for this table
113             if (defined($foreign_keys{$name})) {
114             # this column is a foreign key
115             ($ref_table,$ref_id) = @{$foreign_keys{$name}};
116             }
117             }
118             elsif ($name =~ /^(\w+)_id$/) {
119             # this column follows the standard naming convention
120             # let's assume that it's supposed to be a foreign key.
121             $ref_table = $1;
122             }
123              
124             if ($ref_table) {
125             my $ref_table_info = $dbh->selectall_arrayref("explain $ref_table");
126             if (ref($ref_table_info)) {
127             # figure out table structure
128              
129             my $ref_data = $self->probe_table($ref_table);
130              
131             tie my %ref_info, 'Tie::Hash::Indexed';
132             %ref_info = (
133             'table' => $ref_table,
134             'primary_key' => $ref_id || $ref_data->{'primary_key'},
135             'select_label' => $ref_table,
136             'select_default' => $row->[4],
137             'columns' => [
138             grep { $ref_data->{'columns'}->{$_}->{'type'} eq "varchar" }
139             keys %{$ref_data->{'columns'}}
140             ]
141             );
142              
143             $column{'references'} = \%ref_info;
144             }
145             else {
146             warn("No such table $ref_table: $DBI::errstr");
147             }
148             }
149              
150             $data{'columns'}->{$name} = \%column;
151             }
152              
153             if (defined($data{'ERRORS'})) {
154             print STDERR join("\n",@{$data{'ERRORS'}});
155             print "\n";
156             exit;
157             }
158              
159             return \%data;
160             }
161              
162             sub tinyint_unsigned { shift()->int_handler_unsigned(@_,1); }
163             sub smallint_unsigned { shift()->int_handler_unsigned(@_,2); }
164             sub mediumint_unsigned { shift()->int_handler_unsigned(@_,3); }
165             sub int_unsigned { shift()->int_handler_unsigned(@_,4); }
166             sub integer_unsigned { shift()->int_handler_unsigned(@_,4); }
167             sub bigint_unsigned { shift()->int_handler_unsigned(@_,8); }
168              
169             sub int_handler_unsigned {
170             my ($self,$column,$size,$bytes) = @_;
171              
172             $column->{'type'} = 'unsigned_int';
173             $column->{'bytes'} = $bytes;
174             }
175              
176             sub tinyint { shift()->int_handler(@_,1); }
177             sub smallint { shift()->int_handler(@_,2); }
178             sub mediumint { shift()->int_handler(@_,3); }
179             sub int { shift()->int_handler(@_,4); }
180             sub integer { shift()->int_handler(@_,4); }
181             sub bigint { shift()->int_handler(@_,8); }
182              
183             sub int_handler {
184             my ($self,$column,$size,$bytes) = @_;
185              
186             $column->{'type'} = 'signed_int';
187             $column->{'bytes'} = $bytes;
188             }
189              
190             sub text {
191             my ($self,$column,$size) = @_;
192             $column->{'type'} = 'text';
193             }
194              
195             sub char {
196             my $self = shift;
197             $self->varchar(@_);
198             }
199              
200             sub varchar {
201             my ($self,$column,$size) = @_;
202              
203             $column->{'type'} = 'varchar';
204             $column->{'length'} = $size;
205             }
206              
207             sub decimal_unsigned {
208             my ($self,$column,$size) = @_;
209              
210             my ($l,$r) = split(/,/,$size);
211              
212             $column->{'type'} = 'unsigned_decimal';
213             $column->{'left'} = $l - $r;
214             $column->{'right'} = $r;
215             $column->{'length'} = $r+$l+1;
216             }
217              
218             sub decimal {
219             my ($self,$column,$size) = @_;
220              
221             my ($l,$r) = split(/,/,$size);
222              
223             $column->{'type'} = 'signed_decimal';
224             $column->{'left'} = $l - $r;
225             $column->{'right'} = $r;
226             $column->{'length'} = $r+$l+2;
227             }
228              
229             sub date {
230             my ($self,$column,$size) = @_;
231              
232             $column->{'type'} = 'date';
233             }
234              
235             sub time {
236             my ($self,$column,$size) = @_;
237              
238             $column->{'type'} = 'time';
239             }
240              
241             sub datetime {
242             my ($self,$column,$size) = @_;
243              
244             $column->{'type'} = 'datetime';
245             }
246              
247             sub timestamp {
248             # timestamp is a 'magically' updated column that we don't touch
249             }
250              
251             1;
252              
253             ################################################################################
254             # Copyright (c) 2005-2010 Steven Edwards (maverick@smurfbane.org).
255             # All rights reserved.
256             #
257             # You may use and distribute Apache::Voodoo under the terms described in the
258             # LICENSE file include in this package. The summary is it's a legalese version
259             # of the Artistic License :)
260             #
261             ################################################################################