File Coverage

blib/lib/Autodia/Handler/SQL.pm
Criterion Covered Total %
statement 15 89 16.8
branch 0 28 0.0
condition 0 3 0.0
subroutine 5 8 62.5
pod n/a
total 20 128 15.6


line stmt bran cond sub pod time code
1             ################################################################
2             # AutoDIA - Automatic Dia XML. (C)Copyright 2003 A Trevena #
3             # #
4             # AutoDIA comes with ABSOLUTELY NO WARRANTY; see COPYING file #
5             # This is free software, and you are welcome to redistribute #
6             # it under certain conditions; see COPYING file for details #
7             ################################################################
8             package Autodia::Handler::SQL;
9              
10             require Exporter;
11              
12 1     1   1783 use strict;
  1         2  
  1         38  
13              
14 1     1   5 use vars qw($VERSION @ISA @EXPORT);
  1         1  
  1         59  
15 1     1   5 use Autodia::Handler;
  1         2  
  1         48  
16              
17             @ISA = qw(Autodia::Handler Exporter);
18              
19 1     1   6 use Autodia::Diagram;
  1         2  
  1         27  
20 1     1   11 use Data::Dumper;
  1         1  
  1         1374  
21              
22             #---------------------------------------------------------------
23              
24             my %data_types = (
25             varchar => [qw/varchar2 nvarchar varchar/],
26             char => [qw/char nchar/],
27             integer => [qw/longint shortint int bigint smallint tinyint/],
28             text => [qw/ntext text/],
29             blob => [qw/blob binary varbinary image/],
30             float => [qw/long curr currency doublef float decimal numeric real money smallmoney/],
31             date => [qw/datetime smalldate smalldatetime time date/],
32             boolean => [qw/bool boolean bit/],
33             set => [qw/enum set/],
34             );
35              
36             #####################
37             # Constructor Methods
38              
39             # new inherited from Autodia::Handler
40              
41             #------------------------------------------------------------------------
42             # Access Methods
43              
44             # parse_file inherited from Autodia::Handler
45              
46             #-----------------------------------------------------------------------------
47             # Internal Methods
48              
49             # _initialise inherited from Autodia::Handler
50              
51             sub _parse {
52 0     0     my $self = shift;
53 0           my $fh = shift;
54 0           my $filename = shift;
55 0           my $Diagram = $self->{Diagram};
56              
57             # process tables
58              
59 0           my %fields = ();
60 0           my %primary_keys = ();
61              
62 0           my $in_table = 0;
63 0           my ($Class,$table);
64 0           foreach my $fileline (<$fh>) {
65 0 0         next if ($self->_discard_line($fileline)); # discard comments and garbage
66             # If we have a create line, then we need to finish off the
67             # last table (if any) and start a new one.
68 0 0         if ($fileline =~ /^\s*create\s+table\s+(?:\[\w+\]\.)?[\`\'\"\[]?([\w\s]+)[\`\'\"\]]? ?\(?/i) {
69 0           $table = $1;
70             # create new 'class' representing table
71 0           $Class = Autodia::Diagram::Class->new($table);
72             # add 'class' to diagram
73 0           my $exists = $self->{Diagram}->add_class($Class);
74 0 0         $Class = $exists if ($exists);
75             } else {
76             # recognise lines that define columns
77 0           my $matched = 0;
78 0           foreach my $type (keys %data_types) {
79 0           my $pattern = join('|', ($type,@{$data_types{$type}}));
  0            
80 0 0         if ($fileline =~ /\s*\[?(\S+?)\]?\s+\[?($pattern)\]?\s*([\w\s\(\)]*),?\s*/i) {
81 0           $matched = 1;
82 0           my ($field,$field_type,$extra_info) = ($1,$2,$3);
83 0 0         if ($extra_info =~ /^\s*(\([\d\s]+\))/) {
84 0           $field_type .= $1;
85             }
86             $Class->add_attribute({
87 0           name => $field,
88             visibility => 0,
89             type => $field_type,
90             });
91              
92 0 0         if (my $dep = $self->_is_foreign_key($table, $field)) {
93 0           my $Superclass = Autodia::Diagram::Superclass->new($dep);
94 0           my $exists_already = $self->{Diagram}->add_superclass($Superclass);
95 0 0         if (ref $exists_already) {
96 0           $Superclass = $exists_already;
97             }
98             # create new relationship
99 0           my $Relationship = Autodia::Diagram::Inheritance->new($Class, $Superclass);
100             # add Relationship to superclass
101 0           $Superclass->add_inheritance($Relationship);
102             # add Relationship to class
103 0           $Class->add_inheritance($Relationship);
104             # add Relationship to diagram
105 0           $self->{Diagram}->add_inheritance($Relationship);
106             } else {
107 0           push (@{$fields{$field}}, $Class);
  0            
108             }
109              
110 0 0         if ($extra_info =~ m/(identity|primary[\s_-]key)\s*/i ) {
111 0           my $pk_fields = $field;
112 0           my $primary_key = { name=>'Primary Key', type=>'pk', Param=>[], visibility=>0, };
113 0           foreach my $pk_field (split(/\s*,\s*/,$pk_fields) ) {
114 0           push (@{$primary_key->{Param}}, { Name=>$pk_field, Type=>''});
  0            
115 0           $primary_keys{$pk_field}= $Class;
116             }
117 0           $Class->add_operation($primary_key);
118             }
119              
120 0           last;
121             }
122             }
123 0 0         unless ($matched) {
124             # check for indexes and primary keys
125 0 0         if ( $fileline =~ m/primary[\s_-]key\s*\((.*)\)\s*/i ) {
126 0           my $pk_fields = $1;
127 0           my $primary_key = { name=>'Primary Key', type=>'pk', Param=>[], visibility=>0, };
128 0           foreach my $pk_field (split(/\s*,\s*/,$pk_fields) ) {
129 0           push (@{$primary_key->{Param}}, { Name=>$pk_field, Type=>''});
  0            
130 0           $primary_keys{$pk_field} = $Class;
131             }
132 0           $Class->add_operation($primary_key);
133             }
134             }
135             }
136             }
137              
138             # build additional fk's
139              
140 0           foreach my $primary_key (keys %primary_keys) {
141 0           my $Superclass = $primary_keys{$primary_key};
142 0           foreach my $Class (@{$fields{$primary_key}}) {
  0            
143             # create new relationship
144 0           my $Relationship = Autodia::Diagram::Inheritance->new($Class, $Superclass);
145             # add Relationship to superclass
146 0           $Superclass->add_inheritance($Relationship);
147             # add Relationship to class
148 0           $Class->add_inheritance($Relationship);
149             # add Relationship to diagram
150 0           $self->{Diagram}->add_inheritance($Relationship);
151             }
152             }
153              
154             }
155              
156              
157             sub _is_foreign_key {
158 0     0     my ($self, $table, $field) = @_;
159 0           my $is_fk = undef;
160 0 0 0       if (($field !~ m/$table.u?id/i) && ($field =~ m/^(.*)[_-]u?id$/i)) {
161 0           $is_fk = $1;
162             }
163 0           return $is_fk;
164             }
165              
166             sub _discard_line
167             {
168 0     0     my $self = shift;
169 0           my $line = shift;
170 0           my $return = 0;
171 0 0         $return = 1 if ( $line =~ m/^(INSERT|DROP|LOCK|ALTER|EXEC|GO)/i );
172 0 0         $return = 1 if ( $line =~ m/^\s*(#|--|\/\*|\d+|\))/);
173 0 0         $return = 1 if ( $line =~ m/^\s*$/);
174 0           return $return;
175             }
176              
177              
178             ####-----
179              
180             1;
181              
182             ###############################################################################
183              
184             =head1 NAME
185              
186             Autodia::Handler::SQL.pm - AutoDia handler for SQL
187              
188             =head1 INTRODUCTION
189              
190             Autodia::Handler::SQL parses files into a Diagram Object, which all handlers use. The role of the handler is to parse through the file extracting information such as table names, field names, relationships and keys.
191              
192             SQL is registered in the Autodia.pm module, which contains a hash of language names and the name of their respective language - in this case:
193              
194             %language_handlers = { .. , sql => "Autodia::Handler::SQL", .. };
195              
196             =head1 CONSTRUCTION METHOD
197              
198             use Autodia::Handler::SQL;
199              
200             my $handler = Autodia::Handler::SQL->New(\%Config);
201             This creates a new handler using the Configuration hash to provide rules selected at the command line.
202              
203             =head1 ACCESS METHODS
204              
205             $handler->Parse(filename); # where filename includes full or relative path.
206              
207             This parses the named file and returns 1 if successful or 0 if the file could not be opened.
208              
209             $handler->output();
210              
211             This outputs the Dia XML file according to the rules in the %Config hash passed at initialisation of the object. It also allows you to output VCG, Dot or images rendered through GraphViz and VCG.
212              
213             =cut
214              
215              
216              
217              
218              
219