Adding new files.
Ken Youens-Clark [Tue, 26 Aug 2003 03:56:43 +0000 (03:56 +0000)]
bin/sqlt-dumper [new file with mode: 0755]
bin/sqlt-graph [new file with mode: 0755]

diff --git a/bin/sqlt-dumper b/bin/sqlt-dumper
new file mode 100755 (executable)
index 0000000..6993097
--- /dev/null
@@ -0,0 +1,203 @@
+#!/usr/bin/perl
+
+# -------------------------------------------------------------------
+# $Id: sqlt-dumper,v 1.1 2003-08-26 03:56:43 kycl4rk Exp $
+# -------------------------------------------------------------------
+# Copyright (C) 2003 Ken Y. Clark <kclark@cpan.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307  USA
+# -------------------------------------------------------------------
+
+=head1 NAME
+
+sqlt-dumper - create a dumper script from a schema
+
+=head1 SYNOPSIS
+
+  sqlt-dumper -d Oracle [options] schema.sql > dumper.pl
+
+  ./dumper.pl > data.sql
+
+  Options:
+
+    -h|--help       Show help and exit
+    --add-truncate  Add "TRUNCATE TABLE" statements for each table
+    --skip=t1[,t2]  Skip tables in comma-separated list
+    -u|--user       Database username
+    -p|--password   Database password
+    --dsn           DSN for DBI
+
+=head1 DESCRIPTION
+
+This script uses SQL::Translator to parse the SQL schema and create a
+Perl script that can connect to the database and dump the data as
+INSERT statements a la mysqldump.  If you enable "add-truncate" or
+specify tables to "skip," then the generated dumper script will have
+those hardcoded.  However, these will also be options in the generated
+dumper, so you can wait to specify these options when you dump your
+database.  The database username, password, and DSN can be hardcoded
+into the generated script, or part of the DSN can be intuited from the
+"database" argument.
+
+=cut
+
+# -------------------------------------------------------------------
+
+use strict;
+use Pod::Usage;
+use Getopt::Long;
+use SQL::Translator;
+
+use vars '$VERSION';
+$VERSION = sprintf "%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/;
+
+my ( $help, $db, $add_truncate, $skip, $db_user, $db_pass, $dsn );
+GetOptions(
+    'h|help'        => \$help,
+    'd|f|from|db=s' => \$db,
+    'add-truncate'  => \$add_truncate,
+    'skip:s'        => \$skip,
+    'u|user:s'      => \$db_user,
+    'p|password:s'  => \$db_pass,
+    'dsn:s'         => \$dsn,
+) or pod2usage;
+
+pod2usage(0) if $help;
+pod2usage( 'No database driver specified' ) unless $db;
+$db_user ||= 'username';
+$db_pass ||= 'password';
+$dsn     ||= "dbi:$db:_";
+
+my $file = shift @ARGV or pod2usage( -msg => 'No input file' );
+
+my $t = SQL::Translator->new;
+$t->parser( $db ) or die $t->error, "\n";
+$t->filename( $file ) or die $t->error, "\n";
+
+my %skip = map { $_, 1 } map { s/^\s+|\s+$//; $_ } split (/,/, $skip);
+my $parser = $t->parser or die $t->error;
+$parser->($t, $t->data);
+my $schema = $t->schema;
+my $now    = localtime;
+
+my $out = <<"EOF";
+#!/usr/bin/perl
+
+#
+# Generated $now
+# By sqlt-dumper.pl, part of the SQLFairy project
+# For more info, see http://sqlfairy.sourceforge.net/
+#
+
+use strict;
+use DBI;
+use Getopt::Long;
+
+my ( \$help, \$add_truncate, \$skip );
+GetOptions(
+    'h|help'        => \\\$help,
+    'add-truncate'  => \\\$add_truncate,
+    'skip:s'        => \\\$skip,
+);
+
+if ( \$help ) {
+    print <<"USAGE";
+Usage:
+  \$0 [options]
+
+  Options:
+    -h|--help       Show help and exit
+    --add-truncate  Add "TRUNCATE TABLE" statements
+    --skip=t1[,t2]  Comma-separated list of tables to skip
+
+USAGE
+    exit(0);
+}
+
+my \%skip = map { \$_, 1 } map { s/^\\s+|\\s+\$//; \$_ } split (/,/, \$skip);
+my \$db = DBI->connect('$dsn', '$db_user', '$db_pass');
+
+EOF
+
+for my $table ( $schema->get_tables ) {
+    my $table_name  = $table->name;
+    next if $skip{ $table_name };
+    my ( @field_names, %types );
+    for my $field ( $table->get_fields ) {
+        $types{ $field->name } = $field->data_type =~ m/(char|str|long|text)/
+            ? 'string' : 'number';
+        push @field_names, $field->name;
+    }
+
+    $out .= join('',
+        "#\n# Table: $table_name\n#\n{\n",
+        "    next if \$skip{'$table_name'};\n",
+        "    print \"--\\n-- Data for table '$table_name'\\n--\\n\";\n\n",
+        "    if ( \$add_truncate ) {\n",
+        "        print \"TRUNCATE TABLE $table_name;\\n\";\n",
+        "    }\n\n",
+    );
+
+    my $insert = "INSERT INTO $table_name (". join(', ', @field_names).
+            ') VALUES (';
+
+    if ( $add_truncate ) {
+        $out .= "    print \"TRUNCATE TABLE $table_name;\\n\";\n";
+    }
+
+    $out .= join('',
+        "    my \%types = (\n",
+        join("\n", map { "        $_ => '$types{ $_ }'," } @field_names), 
+        "\n    );\n\n",
+        "    my \$data  = \$db->selectall_arrayref(\n",
+        "        'select ", join(', ', @field_names), " from $table_name',\n",
+        "        { Columns => {} },\n",
+        "    );\n\n",
+        "    for my \$rec ( \@{ \$data } ) {\n",
+        "        my \@vals;\n",
+        "        for my \$fld ( qw[", join(' ', @field_names), "] ) {\n",
+        "            my \$val = \$rec->{ \$fld };\n",
+        "            if ( \$types{ \$fld } eq 'string' ) {\n",
+        "                \$val =~ s/'/\\'/g;\n",
+        "                \$val = defined \$val ? qq['\$val'] : qq[''];\n",
+        "            }\n",
+        "            else {\n",
+        "                \$val = defined \$val ? \$val : 'NULL';\n",
+        "            }\n",
+        "            push \@vals, \$val;\n",
+        "        }\n",
+        "        print \"$insert\", join(', ', \@vals), \");\\n\";\n",
+        "    }\n",
+        "    print \"\\n\";\n",
+        "}\n\n",
+    );
+}
+
+print $out;
+exit(0);
+
+# -------------------------------------------------------------------
+
+=pod
+
+=head1 AUTHOR
+
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>.
+
+=head1 SEE ALSO
+
+perl, SQL::Translator.
+
+=cut
diff --git a/bin/sqlt-graph b/bin/sqlt-graph
new file mode 100755 (executable)
index 0000000..fca4aba
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/perl
+
+# -------------------------------------------------------------------
+# $Id: sqlt-graph,v 1.1 2003-08-26 03:56:43 kycl4rk Exp $
+# -------------------------------------------------------------------
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307  USA
+# -------------------------------------------------------------------
+
+=head1 NAME 
+
+sqlt-graph - Automatically create a graph from a database schema
+
+=head1 SYNOPSIS
+
+  ./sqlt-graph -d|--db|-f|--from=db_parser [options] schema.sql
+
+  Options:
+
+    -l|--layout        Layout schema for GraphViz
+                       ("dot," "neato," "twopi"; default "dot")
+    -n|--node-shape    Shape of the nodes ("record," "plaintext," 
+                       "ellipse," "circle," "egg," "triangle," "box," 
+                       "diamond," "trapezium," "parallelogram," "house," 
+                       "hexagon," "octagon," default "record")
+    -o|--output        Output file name (default STDOUT)
+    -t|--output-type   Output file type ("canon", "text," "ps," "hpgl,"
+                       "pcl," "mif," "pic," "gd," "gd2," "gif," "jpeg,"
+                       "png," "wbmp," "cmap," "ismap," "imap," "vrml,"
+                       "vtx," "mp," "fig," "svg," "plain," default "png")
+    -c|--color         Add colors
+    --no-fields        Don't show field names
+    --height           Image height (in inches, default "11",
+                       set to "0" to undefine)
+    --width            Image width (in inches, default "8.5", 
+                       set to "0" to undefine)
+    --natural-join     Perform natural joins
+    --natural-join-pk  Perform natural joins from primary keys only
+    -s|--skip          Fields to skip in natural joins
+    --debug            Print debugging information
+
+=head1 DESCRIPTION
+
+This script will create a graph of your schema.  Only the database
+driver argument (for SQL::Translator) is required.  If no output file
+name is given, then image will be printed to STDOUT, so you should
+redirect the output into a file.
+
+The default action is to assume the presence of foreign key
+relationships defined via "REFERNCES" or "FOREIGN KEY" constraints on
+the tables.  If you are parsing the schema of a file that does not
+have these, you will find the natural join options helpful.  With
+natural joins, like-named fields will be considered foreign keys.
+This can prove too permissive, however, as you probably don't want a
+field called "name" to be considered a foreign key, so you could
+include it in the "skip" option, and all fields called "name" will be
+excluded from natural joins.  A more efficient method, however, might
+be to simply deduce the foriegn keys from primary keys to other fields
+named the same in other tables.  Use the "natural-join-pk" option
+to acheive this.
+
+If the schema defines foreign keys, then the graph produced will be
+directed showing the direction of the relationship.  If the foreign
+keys are intuited via natural joins, the graph will be undirected.
+
+=cut
+
+# -------------------------------------------------------------------
+
+use strict;
+use Data::Dumper;
+use Getopt::Long;
+use GraphViz;
+use Pod::Usage;
+use SQL::Translator;
+
+use vars '$VERSION';
+$VERSION = sprintf "%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/;
+
+#
+# Get arguments.
+#
+my ( 
+    $layout, $node_shape, $out_file, $output_type, $db_driver, $add_color, 
+    $natural_join, $join_pk_only, $skip_fields, $debug, $help, $height, 
+    $width, $no_fields
+);
+
+GetOptions(
+    'd|db|f|from=s'    => \$db_driver,
+    'o|output:s'       => \$out_file,
+    'l|layout:s'       => \$layout,
+    'n|node-shape:s'   => \$node_shape,
+    't|output-type:s'  => \$output_type,
+    'height:i'         => \$height,
+    'width:i'          => \$width,
+    'c|color'          => \$add_color,
+    'no-fields'        => \$no_fields,
+    'natural-join'     => \$natural_join,
+    'natural-join-pk'  => \$join_pk_only,
+    's|skip:s'         => \$skip_fields,
+    'debug'            => \$debug,
+    'h|help'           => \$help,
+) or die pod2usage;
+my @files = @ARGV; # the create script(s) for the original db
+
+pod2usage(1) if $help;
+pod2usage( -message => "No db driver specified" ) unless $db_driver;
+pod2usage( -message => 'No input file'          ) unless @files;
+
+my $translator          =  SQL::Translator->new( 
+    from                => $db_driver,
+    to                  => 'GraphViz',
+    debug               => $debug || 0,
+    producer_args       => {
+        out_file        => $out_file,
+        layout          => $layout,
+        node_shape      => $node_shape,
+        output_type     => $output_type,
+        add_color       => $add_color,
+        natural_join    => $natural_join,
+        natural_join_pk => $join_pk_only,
+        skip_fields     => $skip_fields,
+        height          => $height || 0,
+        width           => $width  || 0,
+        show_fields     => $no_fields ? 0 : 1,
+    },
+) or die SQL::Translator->error;
+
+for my $file (@files) {
+    my $output = $translator->translate( $file ) or die
+                 "Error: " . $translator->error;
+    if ( $out_file ) {
+        print "Image written to '$out_file'.  Done.\n";
+    }
+    else {
+        print $output;
+    }
+}
+
+# -------------------------------------------------------------------
+
+=pod
+
+=head1 AUTHOR
+
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>.
+
+=head1 SEE ALSO
+
+perl, SQL::Translator.
+
+=cut