2 # See the NOTICE file distributed with this work for additional information
3 # regarding copyright ownership.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
18 # Generate an HTML documentation page from an SQL file.
20 # It needs to have a "javascript like" documentation above each table.
21 # See the content of the method sql_documentation_format();
22 ####################################################################################
26 #use warnings; # commented out because this script is a repeat offender
28 use File::Basename ();
30 use List::Util qw(max sum);
40 my ($sql_file,$fk_sql_file,$html_file,$db_team,$show_colour,$version,$header_flag,$sort_headers,$sort_tables,$intro_file,$embed_diagrams,$help,$help_format);
41 my ($url,$skip_conn,$db_connection);
43 usage() if (!scalar(@ARGV));
47 'fk=s' => \$fk_sql_file,
50 'c!' => \$show_colour,
52 'embed_diagrams!' => \$embed_diagrams,
53 'show_header!' => \$header_flag,
54 'sort_headers=i' => \$sort_headers,
55 'sort_tables=i' => \$sort_tables,
57 'skip_connection' => \$skip_conn,
58 'intro=s' => \$intro_file,
60 'help_format' => \$help_format,
68 print
"> Error! Please give a sql file using the option '-i' \n";
72 $show_colour = 1
if (!defined($show_colour));
73 $header_flag = 1
if (!defined($header_flag));
74 $sort_headers = 1
if (!defined($sort_headers));
75 $sort_tables = 1
if (!defined($sort_tables));
77 $skip_conn = undef
if ($skip_conn == 0);
79 # Dababase connection (optional)
80 if (defined($url) && !defined($skip_conn)) {
83 ) or die("DATABASE CONNECTION ERROR: Could not get a database adaptor for $url\n");
93 my $default_colour =
'#000'; # Black
95 my $documentation = {};
96 my $tables_names = {
'default' => []};
97 my @header_names = (
'default');
98 my @colours = ($default_colour);
103 my $header =
'default';
107 my $count_sql_col = 0;
108 my $tag_content =
'';
110 my $parenth_count = 0;
121 # Create a complex hash "%$documentation" to store all the documentation content
123 open my $sql_fh,
'<', $sql_file or die
"Can't open $sql_file : $!";
127 next
if ($_ =~ /^\s*(DROP|PARTITION)/i);
128 next
if ($_ =~ /^\s*(#|--)/); # Escape characters
131 if ($_ =~ /^\/\*\*/) { $in_doc=1; next; } # start of a table documentation
132 if ($_ =~ /^\s*create\s+table\s+(
if\s+not\s+exists\s+)?`?(\w+)`?/i) { # start to parse the content of the table
139 print STDERR
"The documentation of the table $2 has not be found!\n";
143 next
if ($in_doc==0 and $in_table==0);
147 #================================================#
148 # Parsing the documentation part of the SQL file #
149 #================================================#
152 if ($doc =~ /^\@header\s*(.+)$/i and $header_flag == 1) {
154 unless (exists $tables_names->{$header}) {
155 push (@header_names,$header);
156 $tables_names->{$header} = [];
161 elsif ($doc =~ /^\@table\s*(\w+)/i) {
163 push(@{$tables_names->{$header}},$table);
164 $documentation->{$header}{
'tables'}{$table} = {
'desc' =>
'',
'colour' =>
'',
'column' => [],
'example' => [],
'see' => [],
'info' => [] };
165 $tag = $tag_content =
'';
167 # Description (used for both set, table and info tags)
168 elsif ($doc =~ /^\@(desc)\s*(.+)$/i) {
171 # Colour of the table header (used for both set, table) (optional)
172 elsif ($doc =~ /^\@(colour)\s*(.+)$/i) {
176 elsif ($doc =~ /^\@(column)\s*(.+)$/i) {
180 elsif ($doc =~ /^\@(example)\s*(.+)$/i) {
184 elsif ($doc =~ /^\@(see)\s*(\w+)\s*$/i) {
187 # Addtional information block
188 elsif ($doc =~ /^\@(
info)\s*(.+)$/i) {
193 # End of documentation
199 # Add legend colour description
200 elsif ($doc =~ /^\@(legend)\s*(#\S+)\s+(.+)$/i) {
203 elsif ($doc =~ /^\s*(.+)$/) { # If a tag content is split in several lines
204 $tag_content .=
" $1";
208 #=====================================================#
209 # Parsing of the SQL table to fetch the columns types #
210 #=====================================================#
211 elsif ($in_table==1) {
213 # END OF TABLE DEFINITION
214 # Can't do this easily with a simply regex as there are varying valid formats
215 # The end of the table definition is actually defined by 2nd enclosing bracket
217 # Regex counting VOODOO!
218 # This basically puts the regex in a list context
219 # before inc/dec'ing with it in a scalar context.
220 $parenth_count +=()= $doc =~ /\(/gi;
221 $parenth_count -=()= $doc =~ /\)/gi;
224 if ($parenth_count == 0) { # End of the sql table definition
228 if (scalar @{$documentation->{$header}{
'tables'}{$table}{column}} > $count_sql_col) {
229 print STDERR
"Description of a non existant column in the table $table!\n";
243 # Remove the comments
246 # Skip the blank lines
247 next
if ($doc =~ /^\s+$/);
249 if ($doc =~ /FOREIGN\s+KEY\s+\((\S+)\)\s+REFERENCES\s+(\S+)\s*\((\S+)\)/i) { # foreign key
250 push @{$documentation->{$header}{
'tables'}{$table}->{foreign_keys}}, [$1,$2,$3];
253 elsif ($doc =~ /^\s*(primary\s+key)\s*\w*\s*\((.+)\)/i or $doc =~ /^\s*(unique)\s*\((.+)\)/i){ # Primary or unique;
257 elsif ($doc =~ /^\s*(unique\s+)?(key|index)\s+([^\s\(]+)\s*\((.+)\)/i) { # Keys and indexes
261 elsif ($doc =~ /^\s*(unique)\s+(\S*)\s*\((.+)\)/i) { # Unique
265 elsif ($doc =~ /^\s*(key|index)\s+\((.+)\)/i) { # Keys
270 #----------------------------------#
271 # COLUMNS & TYPES & DEFAULT VALUES #
272 #----------------------------------#
277 # All the type is contained in the same line (type followed by parenthesis)
278 if ($doc =~ /^\W*(\w+)\W+(\w+\s?\(.*\))/ ){
281 if ($doc =~ /
default\s+([^,\s]+)\s*.*(,|#).*/i) { $col_def = $1; } # Default value
284 # The type is written in several lines
285 elsif ($doc =~ /^\W*(\w+)\W+(
enum|set)(\s?\(.*)/i){ # The content is split in several lines
287 $col_type=
"$2$3<br />";
289 while ($end_type != 1){
290 my $line = <$sql_fh>;
294 # Regex counting VOODOO again
295 $parenth_count +=()= $line =~ /\(/gi;
296 $parenth_count -=()= $line =~ /\)/gi;
298 if ($line =~ /\)/) { # Close parenthesis
300 $line =~ /^\s*(.+)\)/;
303 else { # Add the content of the line
305 $col_type .= $1.
'<br />';
307 if ($line =~ /
default\s+([^,\s]+)\s*.*(,|#).*/i) { $col_def = $1; } # Default value
311 # All the type is contained in the same line (type without parenthesis)
312 elsif ($doc =~ /^\s*\W*(\w+)\W+(\w+)/ ){
315 if ($doc =~ /
default\s*([^,\s]+)\s*.*(,|#).*/i) { $col_def = $1; } # Default value
319 if (!defined($col_def) || $col_def eq
'') {
320 $col_def = ($doc =~ /not\s+
null/i ) ?
'*not set*' :
'NULL';
325 if ($doc =~ /\bprimary\s+key\b/i) {
326 push @$pk, $col_name;
333 my %table_documentation;
334 foreach my $c (keys %$documentation) {
335 my $h = $documentation->{$c};
336 foreach my $table_name (keys %{$h->{tables}}) {
337 $table_documentation{$table_name} = $h->{tables}->{$table_name};
338 $h->{tables}->{$table_name}->{category} = $c;
343 open $sql_fh,
'<', $fk_sql_file or die
"Can't open $fk_sql_file : $!";
348 if ($doc =~ /ALTER\s+TABLE\s+(\S+)\s+ADD.*FOREIGN\s+KEY\s+\((\S+)\)\s+REFERENCES\s+(\S+)\((\S+)\)/i) {
349 push @{$table_documentation{$1}->{foreign_keys}}, [$2,$3,$4];
350 } elsif ($doc =~ /ALTER.*FOREIGN/i) {
351 die
"Unrecognized: $doc";
358 my ($graph, $table_name) = @_;
359 my $table_doc = $table_documentation{$table_name};
360 my @rows =
map {sprintf(
'<tr><td bgcolor="white" port="port%s">%s%s</td></tr>', $_->{name}, ($_->{index} =~ /\bprimary\b/i ?
'<B>PK</B> ' :
''), $_->{name})} @{$table_doc->{column}};
361 $graph->add_node($table_name,
363 'style' =>
'filled,rounded',
364 'fillcolor' => $table_doc->{colour},
365 'label' => sprintf(
'<<table border="0"><th><td><font point-size="16">%s</font></td></th><hr/>%s</table>>', $table_name, join(
'', @rows)),
370 my ($show_clusters, $column_links) = @_;
372 'label' =>
"$db_team schema diagram",
375 ? (
'rankdir' =>
'LR',
'concentrate' =>
'true', )
376 : (
'splines' =>
'ortho', ),
378 foreach my $table_name (sort keys %table_documentation) {
381 foreach my $table_name (sort keys %table_documentation) {
382 foreach my $fk (@{$table_documentation{$table_name}->{foreign_keys}}) {
383 $graph->add_edge($table_name => $fk->[1],
386 'from_port' =>
"$fk->[0]:e",
387 'to_port' =>
"$fk->[2]:w",
393 if ($show_clusters) {
394 foreach my $h (sort keys %$documentation) {
396 my $c =
blend_colors($documentation->{$h}->{colour},
'#FFFFFF', 0.8);
397 $graph->cluster_2_attributes()->{$cluster_id} = {
398 'cluster_label' => $h,
399 'style' =>
'rounded,filled,noborder',
400 'fill_colour_pair' => [
"#$c"],
403 $graph->cluster_2_nodes()->{$cluster_id} = \@cluster_nodes;
404 foreach my $t (sort keys %{$documentation->{$h}->{tables}}) {
405 push @cluster_nodes, $t;
414 my ($color1, $color2, $alpha) = @_;
415 my @rgb1 =
map {hex($_)} unpack(
"(A2)*", substr $color1, 1);
416 my @rgb2 =
map {hex($_)} unpack(
"(A2)*", substr $color2, 1);
417 my @rgb =
map {int($rgb1[$_] + $alpha * ($rgb2[$_]-$rgb1[$_]))} 0..2;
418 my $c = join(
'',
map {sprintf(
'%X', $_)} @rgb);
423 my ($graph, $table_name, $fields) = @_;
424 my $table_doc = $table_documentation{$table_name};
427 foreach my $c (@{$table_doc->{column}}) {
428 if ($fields->{$c->{name}}) {
429 push @rows, sprintf(
'<tr><td bgcolor="white" port="port%s">%s%s</td></tr>', $c->{name}, ($c->{index} =~ /\bprimary\b/i ?
'<B>PK</B> ' :
''), $c->{name});
431 } elsif (!$has_ellipsis) {
432 push @rows,
'<tr><td bgcolor="white"><i>...</i></td></tr>';
436 $graph->add_node($table_name,
438 'style' =>
'filled,rounded',
439 'fillcolor' => $table_doc->{colour},
440 'label' => sprintf(
'<<table border="0"><th><td><font point-size="16">%s</font></td></th><hr/>%s</table>>', $table_name, join(
'', @rows)),
445 my ($cluster, $column_links) = @_;
447 'label' =>
"$db_team schema diagram: $cluster tables",
450 ? (
'rankdir' =>
'LR',
'concentrate' =>
'true', )
451 : (
'splines' =>
'ortho', ),
453 foreach my $table_name (sort keys %{$documentation->{$cluster}->{tables}}) {
456 my %clusters_to_draw = ($cluster => 1);
457 my %other_table_fields;
459 foreach my $table_name (sort keys %table_documentation) {
460 foreach my $fk (@{$table_documentation{$table_name}->{foreign_keys}}) {
461 if ($table_documentation{$table_name}->{category} eq $cluster) {
462 $other_table_fields{$fk->[1]}->{$fk->[2]} = 1;
463 $clusters_to_draw{ $table_documentation{$fk->[1]}->{category} } = 1;
464 push @drawn_fks, [$table_name, @$fk];
465 } elsif ($table_documentation{$fk->[1]}->{category} eq $cluster) {
466 $other_table_fields{$table_name}->{$fk->[0]} = 1;
467 $clusters_to_draw{ $table_documentation{$table_name}->{category} } = 1;
468 push @drawn_fks, [$table_name, @$fk];
472 foreach my $table_name (sort keys %other_table_fields) {
473 sub_table_box($graph, $table_name, $other_table_fields{$table_name} || {})
if $cluster ne $table_documentation{$table_name}->{category};
475 foreach my $fk (@drawn_fks) {
476 my $table_name = shift @$fk;
477 $graph->add_edge($table_name => $fk->[1],
480 'from_port' => $fk->[0].
':e',
481 'to_port' => $fk->[2].
':w',
486 foreach my $h (sort keys %clusters_to_draw) {
487 #next unless $h eq $cluster;
489 my $c =
blend_colors($documentation->{$h}->{colour},
'#FFFFFF', 0.8);
490 $graph->cluster_2_attributes()->{$cluster_id} = {
491 'cluster_label' => $h,
493 (
'style' =>
'rounded,filled,noborder',
'fill_colour_pair' => [
"#$c"], )
494 : (
'style' =>
'filled,noborder',
'fill_colour_pair' => [
'white'] ),
497 $graph->cluster_2_nodes()->{$cluster_id} = \@cluster_nodes;
498 foreach my $t (sort keys %{$documentation->{$h}->{tables}}) {
499 push @cluster_nodes, $t
if (($h eq $cluster) || $other_table_fields{$t});
512 # Sort the headers names by alphabetic order
513 if ($sort_headers == 1) {
514 @header_names = sort(@header_names);
516 # Sort the tables names by alphabetic order
517 if ($sort_tables == 1) {
518 while ( my($header_name,$tables) = each (%{$tables_names})) {
519 @{$tables} = sort(@{$tables});
523 # Remove the empty headers (e.g. "default")
524 @header_names = grep {scalar(@{$tables_names->{$_}})} @header_names;
527 #####################
528 ## Schema diagrams ##
529 #####################
531 if ($embed_diagrams) {
533 $diagram_dotcode{
''} = $graph->as_debug();
534 foreach my $c (@header_names) {
536 $diagram_dotcode{$c} = $graph->as_debug();
545 my $html_content =
'';
548 if ($show_colour and scalar @colours > 1 and $header_flag != 1) {
549 $html_content .= qq{A colour legend is available at the <a href=
"#legend">bottom of the page</a>.\n<br /><br />};
552 #=============================================#
553 # List of tables by header (i.e. like a menu) #
554 #=============================================#
559 $html_content .=
".. raw:: latex\n\n \\begin{landscape}\n\n";
561 #========================================#
562 # Display the detailled tables by header #
563 #========================================#
565 foreach my $header_name (@header_names) {
566 my $tables = $tables_names->{$header_name};
571 my $category_title = $documentation->{$header_name}->{
'colour'} ? sprintf(
':schema_table_header:`<%s,square>%s`', $documentation->{$header_name}->{
'colour'}, $header_name) : $header_name;
572 $html_content .=
rst_title($category_title,
'~') .
"\n";
574 #------------------------#
575 # Additional information #
576 #------------------------#
577 $html_content .=
rst_add_indent_to_block($documentation->{$header_name}{
'desc'},
" ") .
"\n\n" if $documentation->{$header_name}{
'desc'};
578 if ($embed_diagrams) {
580 $html_content .=
".. schema_diagram::\n\n" .
rst_add_indent_to_block($diagram_dotcode{$header_name},
' ') .
"\n\n";
586 foreach my $t_name (@{$tables}) {
587 my $data = $documentation->{$header_name}{
'tables'}{$t_name};
589 my $table_title = $documentation->{$header_name}->{
'colour'} ? sprintf(
':schema_table_header:`<%s,round>%s`', $documentation->{$header_name}->{
'colour'}, $t_name) : $t_name;
590 $html_content .=
rst_title($table_title,
'+');
598 $html_content .=
".. raw:: latex\n\n \\end{landscape}\n\n";
601 ######################
602 ## HTML/output file ##
603 ######################
606 open $output_fh,
'>', $html_file or die
"Can't open $html_file : $!";
608 $output_fh = \*STDOUT;
611 print $output_fh $html_content.
"\n";
616 my ($title, $underscore_symbol) = @_;
617 return $title .
"\n" . ($underscore_symbol x length($title)) .
"\n";
622 my @lines = split /\n/, $block;
623 return max(
map {length($_)} @lines);
632 my ($data, $class) = @_;
634 my @widths =
map {
column_width($data, $_)} 0..(scalar(@{$data->[0]})-1);
635 my $w =
":widths: ".join(
" ", @widths);
638 return ".. list-table::\n" .
rst_add_indent_to_block(
":header-rows: 1\n$w\n" . ($class ?
":class: $class\n" :
"") .
"\n" . $table_content,
" ") .
"\n\n";
643 my $first_cell = shift @$row;
648 my ($block, $indent, $first_indent) = @_;
650 my @lines = split /\n/, $block;
651 my $first_line = shift @lines;
653 return $first_indent . $first_line .
"\n" . join(
"\n",
map {$indent . $_} @lines);
655 return $first_indent . $first_line;
659 my ($data, $list_symbol) = @_;
661 return join(
"\n",
map {$list_symbol .
" " . $_} @$data);
668 # List the table names. Group them if possible
669 # By default there is one group, named "default" and it contains all the tables.
675 if ($embed_diagrams) {
676 $rest .=
rst_title(
'Schema diagram',
'=') .
"\n";
677 $rest .=
"The $db_team schema diagrams are automatically generated as PNG images with Graphviz, and show the links between columns of each table.\n";
678 $rest .=
"Here follows the overall schema diagram, while the individual diagrams of each category are available below, together with the table descriptions.\n\n";
682 $rest .=
rst_title(
'Table list',
'=') .
"\n";
683 $rest .=
"Here is the list of all the tables found in a typical $db_team database, grouped by categories.\n\n";
685 if (scalar(@header_names) == 1) {
689 # Remove the empty headers (e.g. "default")
690 my @useful_header_names = grep {scalar(@{$tables_names->{$_}})} @header_names;
692 # No more than 3 categories at a time
693 my $max_headers_per_line = 3;
694 while (scalar(@useful_header_names)) {
695 my @headers_this_time = splice(@useful_header_names, 0, $max_headers_per_line);
697 my @first_row =
map {$documentation->{$_}->{
'colour'} ? sprintf(
':schema_table_header:`<%s,square>%s`', $documentation->{$_}->{
'colour'}, $_) : $_} @headers_this_time;
699 my @data = (\@first_row, \@second_row);
709 # Method to pick up the documentation information contained in the SQL file.
710 # If the line starts by a @<tag>, the previous tag content is added to the documentation hash.
711 # This method allows to describe the content of a tag in several lines.
717 # Description tag (info, table or header)
718 if ($tag eq
'desc') {
719 # Additional description
721 $tag_content = $info.
'@info@'.$tag_content;
722 # Table: additional information
724 push(@{$documentation->{$header}{
'tables'}{$table}{
'info'}},$tag_content);
726 # Header: additional information
728 if (!$documentation->{$header}{
'info'}) {
729 $documentation->{$header}{
'info'} = [];
731 push(@{$documentation->{$header}{
'info'}},$tag_content);
736 elsif(!$documentation->{$header}{
'tables'}) {
737 $documentation->{$header}{
'desc'} = $tag_content;
741 $documentation->{$header}{
'tables'}{$table}{$tag} = $tag_content;
744 elsif ($tag eq
'colour') {
745 if(!$documentation->{$header}{
'tables'}) {
746 $documentation->{$header}{
'colour'} = $tag_content;
749 elsif ($table ne
'') {
750 $documentation->{$header}{
'tables'}{$table}{$tag} = $tag_content;
751 if (! grep {$tag_content eq $_} @colours) {
752 push (@colours,$tag_content);
756 elsif ($tag eq
'column') {
757 $tag_content =~ /(\w+)[\s\t]+(.*)/;
759 my $column = {
'name' => $1,
766 print STDERR
"COLUMN: The description content of the column '$1' is missing in the table $table!\n";
768 push(@{$documentation->{$header}{
'tables'}{$table}{$tag}},$column);
771 push(@{$documentation->{$header}{
'tables'}{$table}{$tag}},$tag_content);
774 # New tag initialised
780 $tag = $tag_content =
'';
785 # Method generating the HTML code to display the description content
789 # Search if there are some @link tags in the description text.
796 # Method generating the HTML code of the table listing the columns of the given SQL table.
800 my $cols = $data->{column};
803 my @header_row = (
'Column',
'Type',
'Default value',
'Description',
'Index');
804 push @data, \@header_row;
806 foreach my $col (@$cols) {
807 my $name = $col->{name};
808 my $type = $col->{type};
809 my $default = $col->{
default};
810 my $desc = $col->{desc};
811 my $index = $col->{index};
818 my @row = (
"**$name**", $type, $default, $desc, $index);
826 # Method generating the HTML code to display the content of the tags @example (description + SQL query + Table of SQL query results)
830 my $examples = $data->{example};
833 my $nb = (scalar(@$examples) > 1) ? 1 :
'';
835 foreach my $ex (@$examples) {
836 my @lines = split(
"\n",$ex);
837 my $nb_display = ($nb ne
'') ?
" $nb" : $nb;
838 $html .=
rst_title(
"Example$nb_display:",
'-') .
"\n";
842 # Parse the example lines
843 foreach my $line (@lines) {
846 # Pick up the SQL query if it exists
847 if ($line =~ /(.*)\s*\@sql\s*(.+)/) {
850 } elsif (!defined($sql)){
860 # Search if there are some @link tags in the example description.
863 # Add a table of examples
866 if (!defined($skip_conn) && defined($url)) {
879 # Method generating the HTML code to display the content of the tags @see
885 $html .= qq{ <td
class=
"sql_schema_extra_left">\ <p style=
"font-weight:bold">See also:</p>\n <ul>\n};
886 foreach my $see (@$sees) {
887 $html .= qq{ <li><a href=
"#$see">$see</a></li>\n};
889 $html .= qq{ </ul>\n </td>\n};
896 # Method searching the tag @link into the string given as argument and replace it by an internal HTML link
900 while ($desc =~ /\@link\s?(\w+)/) {
902 if ((!grep {$link eq $_} @{$data->{see}}) and defined($link)) {
903 push @{$data->{see}}, $link;
905 my $table_to_link = $link;
906 $desc =~ s/\@link\s?\w+/$table_to_link/;
912 # Method parsing the index information from the SQL table description in order to display it in the
913 # HTML table listing the columns of the corresponding SQL table.
915 my $idx_type = shift;
917 my $idx_name = shift;
919 my $index = $idx_type;
920 if (!defined($idx_name)) {
921 $idx_name = $idx_col;
923 if ($idx_type !~ /primary/i) {
924 $index .=
": *$idx_name*";
926 my @idx_cols = split(
',',$idx_col); # The index can involve several columns
929 foreach my $i_col (@idx_cols) {
931 # In case of index using a number characters for a column
932 if ($i_col =~ /(.+)\(\d+\)/) {
935 $is_found{$i_col} = 0;
936 foreach my $col (@{$documentation->{$header}{tables}{$table}{column}}) {
937 if ($col->{name} eq $i_col) {
938 if ($col->{index} ne
'') {
939 $col->{index} .=
"\n";
941 $col->{index} .= lc($index);
942 $is_found{$i_col} = 1;
947 # Description missing
948 while (my ($k,$v) = each(%is_found)) {
950 print STDERR
"INDEX: The description of the column '$k' is missing in the table $table!\n";
956 # Method parsing the column type and default value from the SQL table description, in order to display them in the
957 # HTML table listing the columns of the corresponding SQL table.
961 my $c_default = shift;
965 foreach my $col (@{$documentation->{$header}{
'tables'}{$table}{column}}) {
966 if ($col->{name} eq $c_name) {
967 $col->{type} = $c_type;
968 $col->{
default} = $c_default
if ($c_default ne
'');
973 # Description missing
975 print STDERR
"COLUMN: The description of the column '$c_name' is missing in the table $table!\n";
980 # Display the types "enum" and "set" as an HTML list (<ul><li>)
983 $type =~ /^\s*(
enum|set)\s*\((.*)\)/i;
986 return $type unless ($c_data);
991 $c_data =~ s/<br \/>//g;
993 my @items_list = split(',
',$c_data);
995 return $type unless (scalar(@items_list) > 1);
997 return $c_type . ":\n\n" . rst_bullet_list(\@items_list);
1001 # Method to query the database with the SQL query example, get the result and display it
1003 sub get_example_table {
1009 $sql =~ /select\s+(.+)\s+from/i;
1011 $cols = '*
' if $cols =~ /^\*\s/;
1014 foreach my $col (split(',
',$cols)) {
1016 # Columns selection like the expressions "table.*" or "*"
1018 $table_name = $table if ($cols eq '*
');
1019 $table_name = $1 if ($col =~ /(\S+)\.\*/ and !defined($table_name));
1020 if (defined($table_name)) {
1021 my $table_cols = $db_connection->selectall_arrayref(qq{SHOW COLUMNS FROM $table_name});
1022 foreach my $col (@$table_cols) {
1023 push(@tcols,$col->[0]);
1029 $col = $1 if ($col =~ /\s+as\s+(\S+)$/i);
1035 my $results = $db_connection->selectall_arrayref($sql);
1036 if (scalar(@$results)) {
1038 push @data, \@tcols;
1041 foreach my $result (@$results) {
1042 last if ($count >= $SQL_LIMIT);
1043 push @data, [map {$_ // 'NULL
'} @$result];
1046 return rst_list_table(\@data);
1048 my $msg = qq{The SQL query displayed above returned no results!};
1049 print STDERR qq{SQL: $sql\n$msg\n};
1050 return ".. error::\n\n $msg\n\n";
1056 # Removed the character(s) ` from the read line.
1064 # Insert the introduction text of the web page
1066 my $intro_file = shift;
1067 return qq{This document describes the tables that make up the $db_team schema. Tables are grouped into categories, and the purpose of each table is explained.\n} if (!defined $intro_file);
1070 open my $fh, '<
', $intro_file or die "Can't open $intro_file: $!
";
1071 my $intro_html = <$fh>;
1074 $intro_html =~ s/####DB_VERSION####/$version/g if (defined($version));
1084 sub sql_documentation_format {
1087 #--------------------------#
1088 # Example of documentation #
1089 #--------------------------#
1120 create table variation (
1121 variation_id int(10) unsigned not null auto_increment, # PK
1122 source_id int(10) unsigned not null,
1124 validation_status SET('cluster','freq','submitter','doublehit','hapmap','1000Genome','failed','precious'),
1125 ancestral_allele text,
1126 flipped tinyint(1) unsigned NULL DEFAULT NULL,
1127 class_so_id ENUM('SO:0001483','SO:1000002','SO:0000667') DEFAULT 'SO:0001059', # default to sequence_alteration
1129 primary key( variation_id ),
1131 key source_idx (source_id)
1139 #========================================================================================================================#
1142 #------------------#
1143 # Tags description #
1144 #------------------#
1145 : begin and end of the document block
1147 @header : tag to create a group of tables
1148 @table : name of the sql table
1149 @desc : description of the role/content of the table, set or info tags
1150 @colour : tag to colour the header of the table (e.g. if the tables are coloured in the graphic SQL schema and you want to reproduce it in the HTML version)
1151 @column : column_name [tab(s)] Column description. Note: 1 ligne = 1 column
1152 @see : tables names linked to the described table
1153 @link : Internal link to an other table description. The format is ... @link table_name ...
1154 @info : tag to describe additional information about a table or a set of tables
1155 @legend : tag to fill the colour legend table at the end of the HTML page
1156 @example : tag to add some examples, like examples of SQL queries
1157 @sql : tag inside the @example tag, used to delimit a SQL query
1167 Usage: perl sql2html.pl [OPTION]
1169 Convert the SQL documentation into an HTML document.
1173 -help Print this message
1174 -help_format Print the description of the documentation format in the SQL files
1176 An input file must be specified. This file must be a SQL file, with the "Java-doc like documentation
".
1177 For more information, please visit the following page:
1178 http://www.ebi.ac.uk/seqdb/confluence/display/EV/SQL+documentation
1180 -i A SQL file name (Required)
1181 -fk An external SQL file name with foreign keys statements (Optional)
1182 -o An HTML output file name (Required)
1183 -d The name of the database (e.g Core, Variation, Functional Genomics, ...)
1184 -c A flag to display the colours associated with the tables (1) or not (0). By default, the value is set to 1.
1185 -v Version of the schema. Replace the string ####DB_VERSION#### by the value of the parameter "-v
", in the introduction text. (Optional)
1186 -intro A html/text file to include in the Introduction section (Optional. If not provided a default text will be inserted)
1187 -html_head A html/text file to include extra text inside the html <head></head> tags. (Optional)
1188 -show_header A flag to display headers for a group of tables (1) or not (0). By default, the value is set to 1.
1189 -embed_diagrams A flag to include schema diagrams as dot graphs
1190 -sort_headers A flag to sort (1) or not (0) the headers by alphabetic order. By default, the value is set to 1.
1191 -sort_tables A flag to sort (1) or not (0) the tables by alphabetic order. By default, the value is set to 1.
1193 Other optional options:
1195 # If you want to add some SQL query results as examples:
1196 -url URL of the database that has some data
1197 -skip_connection Avoid to run the MySQL queries contained in the "@example
" tags.