X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FTranslator%2FProducer%2FGraphViz.pm;h=e0d18f86af70fb8a2716751fb549a475485b7697;hb=b03926cdac036bdbcf69e478ca29e1121c814646;hp=5be1a64d09fb1b392d076df1054c5a5aa797e5f4;hpb=821a0fde221f5accf93e3f65efa77b5a6733cb5e;p=dbsrgits%2FSQL-Translator.git diff --git a/lib/SQL/Translator/Producer/GraphViz.pm b/lib/SQL/Translator/Producer/GraphViz.pm index 5be1a64..e0d18f8 100644 --- a/lib/SQL/Translator/Producer/GraphViz.pm +++ b/lib/SQL/Translator/Producer/GraphViz.pm @@ -1,9 +1,7 @@ package SQL::Translator::Producer::GraphViz; # ------------------------------------------------------------------- -# $Id$ -# ------------------------------------------------------------------- -# Copyright (C) 2002-4 SQLFairy Authors +# Copyright (C) 2002-2009 SQLFairy Authors # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -56,7 +54,9 @@ the GraphViz module). It's nifty--you should try it! =item * out_file -the name of the file where the graphviz graphic is to be written +The name of the file where the resulting GraphViz output will be +written. Alternatively an open filehandle can be supplied. If +undefined (the default) - the result is returned as a string. =item * layout (DEFAULT: 'dot') @@ -178,11 +178,11 @@ set on each table. it describes the index types along with which columns are included in the index. this option requires that show_fields is a true value as well -=item * show_index_name +=item * show_index_names if show_indexes is set to a true value, then the value of this parameter determines whether or not to print names of indexes. -if show_index_name is false, then a list of indexed columns +if show_index_names is false, then a list of indexed columns will appear below the field list. otherwise, it will be a list prefixed with the name of each index. it defaults to true. @@ -206,12 +206,12 @@ as far as I am aware, is only implemented in MySQL) use strict; use GraphViz; -use Data::Dumper; use SQL::Translator::Schema::Constants; use SQL::Translator::Utils qw(debug); +use Scalar::Util qw/openhandle/; use vars qw[ $VERSION $DEBUG ]; -$VERSION = sprintf "%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/; +$VERSION = '1.99'; $DEBUG = 0 unless defined $DEBUG; use constant VALID_LAYOUT => { @@ -236,31 +236,6 @@ use constant VALID_NODE_SHAPE => { octagon => 1, }; -use constant VALID_OUTPUT => { - canon => 1, - text => 1, - ps => 1, - hpgl => 1, - pcl => 1, - mif => 1, - pic => 1, - gd => 1, - gd2 => 1, - gif => 1, - jpeg => 1, - png => 1, - wbmp => 1, - cmap => 1, - ismap => 1, - imap => 1, - vrml => 1, - vtx => 1, - mp => 1, - fig => 1, - svg => 1, - plain => 1, -}; - sub produce { my $t = shift; my $schema = $t->schema; @@ -288,7 +263,7 @@ sub produce { my $show_datatypes = $args->{'show_datatypes'}; my $show_sizes = $args->{'show_sizes'}; my $show_indexes = $args->{'show_indexes'}; - my $show_index_name = $args->{'show_index_name'} || 1; + my $show_index_names = defined $args->{'show_index_names'} ? $args->{'show_index_names'} : 1; my $friendly_ints = $args->{'friendly_ints'}; my $friendly_ints_ex = $args->{'friendly_ints_extended'}; my $show_constraints = $args->{'show_constraints'}; @@ -304,8 +279,6 @@ sub produce { ) if $natural_join; die "Invalid layout '$layout'" unless VALID_LAYOUT->{ $layout }; - die "Invalid output type: '$output_type'" - unless VALID_OUTPUT->{ $output_type }; die "Invalid node shape'$node_shape'" unless VALID_NODE_SHAPE->{ $node_shape }; @@ -314,9 +287,6 @@ sub produce { $_ = 0 if $_ < 0; } - # - # Create GraphViz and see if we can produce the output type. - # my %args = ( directed => $natural_join ? 0 : 1, layout => $layout, @@ -360,29 +330,40 @@ sub produce { $args{'graph'}->{$key} = $val; } - my $gv = GraphViz->new( %args ) or die "Can't create GraphViz object\n"; + # + # Create a blank GraphViz object and see if we can produce the output type. + # + my $gv = GraphViz->new( %args ) or die "Can't create GraphViz object\n"; + my $output_method = "as_$output_type"; + + # the generators are AUTOLOADed so can't use ->can ($output_method) + eval { $gv->$output_method }; + die "Invalid output type: '$output_type'" if $@; + my %nj_registry; # for locations of fields for natural joins my @fk_registry; # for locations of fields for foreign keys for my $table ( $schema->get_tables ) { - my $table_name = $table->name; my @fields = $table->get_fields; if ( $show_fk_only ) { @fields = grep { $_->is_foreign_key } @fields; } - my $label = '{' . $table_name; + my $field_str = ''; if ($show_fields) { - my $field_str = ''; + + my @fmt_fields; foreach my $field (@fields) { - $field_str .= '-\ ' . $field->name; + + my $field_type; if ($show_datatypes) { - my $dt = lc($field->data_type); + + $field_type = $field->data_type; # For the integer type, transform into different types based on # requested size, if a size is given. - if ($friendly_ints && $dt eq 'integer' && $field->size) { + if ($field->size and $friendly_ints and (lc $field_type) eq 'integer') { # Automatically translate to int2, int4, int8 # Type (Bits) Max. Signed/Unsigned Length # tinyint* (8) 128 3 @@ -392,74 +373,123 @@ sub produce { # mediumint* (24) 8388607 7 # 16777215 8 # int (32) 2147483647 10 - # 4294967295 10 + # 4294967295 11 # bigint (64) 9223372036854775807 19 # 18446744073709551615 20 # # * tinyint and mediumint are nonstandard extensions which are # only available under MySQL (to my knowledge) my $size = $field->size; - if ($size > 10) { - $dt = 'bigint'; + if ($size <= 3 and $friendly_ints_ex) { + $field_type = 'tinyint', + } + elsif ($size <= 5) { + $field_type = 'smallint'; } - elsif ($size > 5) { - $dt = 'integer'; - if ($friendly_ints_ex && $size <= 8) { - $dt = 'mediumint'; - } + elsif ($size <= 8 and $friendly_ints_ex) { + $field_type = 'mediumint'; + } + elsif ($size <= 11) { + $field_type = 'integer'; } else { - $dt = 'smallint'; - if ($friendly_ints_ex && $size <= 3) { - $dt = 'tinyint'; - } + $field_type = 'bigint'; } } - $field_str .= '\ ' . $dt; - if ($show_sizes && $field->size && ($dt =~ /^(var)?char2?$/ || $dt eq 'numeric' || $dt eq 'decimal')) { - $field_str .= '(' . $field->size . ')'; + if ( + $show_sizes + and + $field->size + and + ($field_type =~ /^(var)?char2?$/ or $field_type eq 'numeric' or $field_type eq 'decimal') + ) { + $field_type .= '(' . $field->size . ')'; } } + my $constraints; if ($show_constraints) { my @constraints; push(@constraints, 'PK') if $field->is_primary_key; push(@constraints, 'FK') if $field->is_foreign_key; push(@constraints, 'U') if $field->is_unique; - if (scalar(@constraints)) { - $field_str .= '\ [' . join(',\ ', @constraints) . ']'; - } + + $constraints = join (',', @constraints); } - $field_str .= '\l'; + + # construct the field line from all info gathered so far + push @fmt_fields, join (' ', + '-', + $field->name, + $field_type || (), + $constraints ? "[$constraints]" : (), + ); + } - $label .= '|' . $field_str; + + # join field lines with graphviz formatting + $field_str = join ('\l', @fmt_fields) . '\l'; } + my $index_str = ''; if ($show_indexes) { - my $index_str = ''; + + my @fmt_indexes; foreach my $index ($table->get_indices) { next unless $index->is_valid; - $index_str .= '*\ '; - if ($show_index_name) { - $index_str .= $index->name . ': '; - } - $index_str .= join(', ', $index->fields); - if ($index->type eq 'UNIQUE') { - $index_str .= '\ [U]'; - } - $index_str .= '\l'; - } - # Only add the last box if index_str is non-null - if (length $index_str) { - $label .= '|' . $index_str; + push @fmt_indexes, join (' ', + '*', + $show_index_names ? $index->name . ':' : (), + join (', ', $index->fields), + ($index->type eq 'UNIQUE') ? '[U]' : (), + ); } + + # join index lines with graphviz formatting (if any indexes at all) + $index_str = join ('\l', @fmt_indexes) . '\l' if @fmt_indexes; + } + + my $table_name = $table->name; + my $name_str = $table_name . '\n'; + + # escape spaces + for ($name_str, $field_str, $index_str) { + $_ =~ s/ /\\ /g; } - $label .= '}'; -# $gv->add_node( $table_name, label => $label ); -# $gv->add_node( $table_name, label => $label, ($node_shape eq 'record' ? ( shape => $node_shape ) : ()) ); - $gv->add_node( $table_name, label => $label, shape => $node_shape ); + + + # only the 'record' type supports nice formatting + if ($node_shape eq 'record') { + + # the necessity to supply shape => 'record' is a graphviz bug + $gv->add_node( $table_name, + shape => 'record', + label => sprintf ('{%s}', + join ('|', + $name_str, + $field_str || (), + $index_str || (), + ), + ), + ); + } + else { + my $sep = sprintf ('%s\n', + '-' x ( (length $table_name) + 2) + ); + + $gv->add_node( $table_name, + label => join ($sep, + $name_str, + $field_str || (), + $index_str || (), + ), + ); + } + + debug("Processing table '$table_name'"); debug("Fields = ", join(', ', map { $_->name } @fields)); @@ -533,15 +563,19 @@ sub produce { # # Print the image. # - my $output_method = "as_$output_type"; if ( $out_file ) { + if (openhandle ($out_file)) { + print $out_file $gv->$output_method; + } + else { open my $fh, ">$out_file" or die "Can't write '$out_file': $!\n"; binmode $fh; print $fh $gv->$output_method; close $fh; + } } else { - return $gv->$output_method; + return $gv->$output_method; } }