Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env perl
- ### USAGE EXAMPLES
- # $0 json
- # $0 md
- # $0 html
- ###
- use v5.10; use warnings; use strict; use autodie qw(:all);
- use utf8; no warnings 'utf8';
- use Encode qw(decode_utf8);
- use List::Util qw(first);
- use IPC::Open2;
- use JSON;
- # db connection parameters
- my $host = '127.0.0.1';
- my $port = 5432;
- my $dbname = 'carma';
- my $user = 'carma';
- die 'Unexpected arguments: ', join(' ', @ARGV)
- if scalar(@ARGV) != 1 || !(first {$ARGV[0] eq $_} qw(json md html));
- # opening bi-directional pipe
- my @args = ('psql', '-h', $host, '-p', $port, '-d', $dbname, '-U', $user);
- open2 \*PSQL_OUT, \*PSQL_IN, @args;
- END {close PSQL_OUT; close PSQL_IN} # cleanup
- my $sep = ('~' x 50).'SEPARATOR'.('~' x 50);
- sub separate {say PSQL_IN "SELECT '$sep';"}
- say PSQL_IN '\d+'; # requesting list of tables
- separate; # marking end of output
- my @tables = (); # list of names (strings)
- my @views = (); # list of names (strings)
- {
- local $_;
- my $is_started = 0;
- while (chomp($_ = decode_utf8 <PSQL_OUT>)) {
- last if /$sep/; # end of output of a command
- unless ($is_started) {
- # detecting table header and table body separator
- $is_started = 1 if /^[-+]+$/ && /\+/;
- next;
- }
- my @columns = split /\|/;
- map s/(^\s+|\s+$)//g, @columns;
- next if scalar(@columns) < 2;
- if ($columns[2] eq 'table') {
- push @tables, $columns[1];
- } elsif ($columns[2] eq 'view') {
- push @views, $columns[1];
- }
- }
- }
- # put labeled debugging separator to log (STDERR)
- sub stitle {say STDERR (('-' x 20).' '.shift.' '.('-' x 20))}
- # info just for debug
- stitle 'Tables';
- say STDERR for @tables;
- stitle 'Views';
- say STDERR for @views;
- my %tables_data = ();
- my %views_data = ();
- foreach my $table (@tables) {
- say PSQL_IN '\d+ "', $table, '"';
- separate; # marking end of output
- my $is_fields_started = 0;
- my $is_meta_started = 0;
- my $is_foreign_keys_meta = 0;
- my $is_done = 0;
- my @fields = ();
- my %foreign_refs = ();
- my %table = (fields => \@fields, foreign_refs => \%foreign_refs);
- while (chomp($_ = decode_utf8 <PSQL_OUT>)) {
- if (/$sep/) {$is_done = 1; last} # end of output of a command
- unless ($is_fields_started) {
- # detecting table header and table body separator
- $is_fields_started = 1 if /^[-+]+$/ && /\+/;
- next
- }
- my @columns = split /\|/;
- map s/(^\s+|\s+$)//g, @columns;
- if (scalar(@columns) < 4) {
- if (/^[^|]+:$/) {
- $is_meta_started = 1;
- $is_foreign_keys_meta = 1 if $_ eq 'Foreign-key constraints:';
- last # in this case we're done with fields
- } else {next}
- }
- my %field = (
- name => $columns[0],
- type => $columns[1],
- optional => $columns[3],
- );
- push @fields, \%field;
- }
- if (!$is_done && $is_meta_started) {
- while (chomp($_ = decode_utf8 <PSQL_OUT>)) {
- if (/$sep/) {$is_done = 1; last} # end of output of a command
- if (/^[^|]+:$/) {
- $is_foreign_keys_meta = $_ eq 'Foreign-key constraints:'
- } elsif (/^\s{4}\S/ && $is_foreign_keys_meta) {
- /\s+FOREIGN KEY\s+\(([^)]+)\)\s+REFERENCES\s+("[^"]+"|[^(]+)\(([^)]+)\)/;
- my $field = $1;
- my $ref_table = $2;
- my $ref_field = $3;
- $ref_table =~ s/^"(.*)"$/$1/;
- my %ref = (
- table => $ref_table,
- field => $ref_field,
- );
- $foreign_refs{$field} = \%ref;
- }
- }
- }
- $tables_data{$table} = \%table
- }
- foreach my $view (@views) {
- my @fields = ();
- say PSQL_IN '\d+ "', $view, '"';
- separate; # marking end of output
- my $is_fields_started = 0;
- while (chomp($_ = decode_utf8 <PSQL_OUT>)) {
- last if /$sep/; # end of output of a command
- unless ($is_fields_started) {
- # detecting table header and table body separator
- $is_fields_started = 1 if /^[-+]+$/ && /\+/;
- next
- }
- my @columns = split /\|/;
- map s/(^\s+|\s+$)//g, @columns;
- next if scalar(@columns) < 4;
- my %field = (
- name => $columns[0],
- type => $columns[1],
- );
- push @fields, \%field;
- }
- $views_data{$view} = \@fields
- }
- if ($ARGV[0] eq 'json') {
- my %all = (tables => \%tables_data, views => \%views_data);
- say encode_json \%all
- } elsif ($ARGV[0] eq 'md') {
- say "# Tables";
- say '';
- while (my ($table_name, $table_data) = each %tables_data) {
- my $hash = $table_name; $hash =~ s/ /_/g;
- my @fields = @{$table_data->{fields}};
- my %foreign_refs = %{$table_data->{foreign_refs}};
- $table_name = "`$table_name`" if $table_name ne '';
- say "- <a name='$hash'>$table_name</a>";
- say " - Fields:";
- foreach my $field (@fields) {
- my ($name, $type, $optional) =
- ($field->{name}, $field->{type}, $field->{optional});
- $name = "`$name`" if $name ne '';
- $type = "`$type`" if $type ne '';
- $optional = "`$optional`" if $optional ne '';
- say " - $name";
- say " - Type: $type";
- say " - Nullable: $optional";
- }
- if (scalar(keys(%foreign_refs)) > 0) {
- say " - Foreign keys of the table";
- while (my ($k, $v) = each %foreign_refs) {
- my ($table, $field) = ($v->{table}, $v->{field});
- my $hash = $table; $hash =~ s/ /_/g;
- $k = "`$k`" if $k ne '';
- $table = "`$table`" if $table ne '';
- $field = "`$field`" if $field ne '';
- say " - $k";
- say " - Foreign table reference: <a href='#$hash'>$table</a>";
- say " - Foreign field reference: $field";
- }
- }
- }
- say '';
- say "# Views";
- say '';
- while (my ($view_name, $view_fields) = each %views_data) {
- my $hash = $view_name; $hash =~ s/ /_/g;
- my @fields = @{$view_fields};
- $view_name = "`$view_name`" if $view_name ne '';
- say "- <a name='$hash'>$view_name</a>";
- foreach my $field (@fields) {
- my ($name, $type) = ($field->{name}, $field->{type});
- $name = "`$name`" if $name ne '';
- $type = "`$type`" if $type ne '';
- say " - $name";
- say " - Type: $type";
- }
- }
- } elsif ($ARGV[0] eq 'html') {
- say "<!doctype html>";
- say "<html>";
- say "<head>";
- say "<meta charset='utf-8'>";
- say "<style>table th, table td { border: 1px solid gray; padding: 5px; }</style>";
- say "</head>";
- say "<body>";
- say "<h1>Tables</h1>";
- while (my ($table_name, $table_data) = each %tables_data) {
- my $hash = $table_name; $hash =~ s/ /_/g;
- my @fields = @{$table_data->{fields}};
- my %foreign_refs = %{$table_data->{foreign_refs}};
- say "<h2><a name='$hash'>$table_name</a></h2>";
- say "<table>";
- say "<thead><tr>";
- say "<th>Field name</th>";
- say "<th>Type</th>";
- say "<th>Nullable</th>";
- say "</tr></thead>";
- say "<tbody>";
- foreach my $field (@fields) {
- my ($name, $type, $optional) =
- ($field->{name}, $field->{type}, $field->{optional});
- say "<tr>";
- say "<td>$name</td>";
- say "<td>$type</td>";
- say "<td>$optional</td>";
- say "</tr>";
- }
- say "</tbody>";
- say "</table>";
- if (scalar(keys(%foreign_refs)) > 0) {
- say "<h3>Foreign keys of the table</h3>";
- say "<table>";
- say "<thead><tr>";
- say "<th>Field name</th>";
- say "<th>Foreign table reference</th>";
- say "<th>Foreign field reference</th>";
- say "</tr></thead>";
- say "<tbody>";
- while (my ($k, $v) = each %foreign_refs) {
- my ($table, $field) = ($v->{table}, $v->{field});
- my $hash = $table; $hash =~ s/ /_/g;
- say "<tr>";
- say "<td>$k</td>";
- say "<td><a href='#$hash'>$table</a></td>";
- say "<td>$field</td>";
- say "</tr>";
- }
- say "</tbody>";
- say "</table>";
- }
- }
- say "<hr>";
- say "<h1>Views</h1>";
- while (my ($view_name, $view_fields) = each %views_data) {
- my $hash = $view_name; $hash =~ s/ /_/g;
- my @fields = @{$view_fields};
- say "<h2><a name='$hash'>$view_name</a></h2>";
- say "<table>";
- say "<thead><tr>";
- say "<th>Field name</th>";
- say "<th>Type</th>";
- say "</tr></thead>";
- say "<tbody>";
- foreach my $field (@fields) {
- my ($name, $type) = ($field->{name}, $field->{type});
- say "<tr>";
- say "<td>$name</td>";
- say "<td>$type</td>";
- say "</tr>";
- }
- say "</tbody>";
- say "</table>";
- }
- say "</body>";
- say "</html>";
- } else {die 'Unexpected arguments: ', join(' ', @ARGV)}
Add Comment
Please, Sign In to add comment