Just the fairy.
[dbsrgits/SQL-Translator.git] / bin / sqlt-dumper
1 #!/usr/bin/perl
2
3 # -------------------------------------------------------------------
4 # $Id: sqlt-dumper,v 1.3 2004-02-06 17:48:16 kycl4rk Exp $
5 # -------------------------------------------------------------------
6 # Copyright (C) 2002-4 SQLFairy Authors
7 #
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License as
10 # published by the Free Software Foundation; version 2.
11 #
12 # This program is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 # General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 # 02111-1307  USA
21 # -------------------------------------------------------------------
22
23 =head1 NAME
24
25 sqlt-dumper - create a dumper script from a schema
26
27 =head1 SYNOPSIS
28
29   sqlt-dumper -d Oracle [options] schema.sql > dumper.pl
30
31   ./dumper.pl > data.sql
32
33   Options:
34
35     -h|--help       Show help and exit
36     --add-truncate  Add "TRUNCATE TABLE" statements for each table
37     --skip=t1[,t2]  Skip tables in comma-separated list
38     -u|--user       Database username
39     -p|--password   Database password
40     --dsn           DSN for DBI
41
42 =head1 DESCRIPTION
43
44 This script uses SQL::Translator to parse the SQL schema and create a
45 Perl script that can connect to the database and dump the data as
46 INSERT statements a la mysqldump.  If you enable "add-truncate" or
47 specify tables to "skip," then the generated dumper script will have
48 those hardcoded.  However, these will also be options in the generated
49 dumper, so you can wait to specify these options when you dump your
50 database.  The database username, password, and DSN can be hardcoded
51 into the generated script, or part of the DSN can be intuited from the
52 "database" argument.
53
54 =cut
55
56 # -------------------------------------------------------------------
57
58 use strict;
59 use Pod::Usage;
60 use Getopt::Long;
61 use SQL::Translator;
62 use File::Basename qw(basename);
63
64 use vars '$VERSION';
65 $VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/;
66
67 my ( $help, $db, $add_truncate, $skip, $db_user, $db_pass, $dsn );
68 GetOptions(
69     'h|help'        => \$help,
70     'd|f|from|db=s' => \$db,
71     'add-truncate'  => \$add_truncate,
72     'skip:s'        => \$skip,
73     'u|user:s'      => \$db_user,
74     'p|password:s'  => \$db_pass,
75     'dsn:s'         => \$dsn,
76 ) or pod2usage;
77
78 pod2usage(0) if $help;
79 pod2usage( 'No database driver specified' ) unless $db;
80 $db_user ||= 'username';
81 $db_pass ||= 'password';
82 $dsn     ||= "dbi:$db:_";
83
84 my $file = shift @ARGV or pod2usage( -msg => 'No input file' );
85
86 my $t = SQL::Translator->new;
87 $t->parser( $db ) or die $t->error, "\n";
88 $t->filename( $file ) or die $t->error, "\n";
89
90 my %skip = map { $_, 1 } map { s/^\s+|\s+$//; $_ } split (/,/, $skip);
91 my $parser = $t->parser or die $t->error;
92 $parser->($t, $t->data);
93 my $schema = $t->schema;
94 my $now    = localtime;
95 my $prog   = basename $0;
96
97 my $out = <<"EOF";
98 #!/usr/bin/perl
99
100 #
101 # Generated $now
102 # By $prog, part of the SQLFairy project
103 # For more info, see http://sqlfairy.sourceforge.net/
104 #
105
106 use strict;
107 use DBI;
108 use Getopt::Long;
109
110 my ( \$help, \$add_truncate, \$skip );
111 GetOptions(
112     'h|help'        => \\\$help,
113     'add-truncate'  => \\\$add_truncate,
114     'skip:s'        => \\\$skip,
115 );
116
117 if ( \$help ) {
118     print <<"USAGE";
119 Usage:
120   \$0 [options]
121
122   Options:
123     -h|--help       Show help and exit
124     --add-truncate  Add "TRUNCATE TABLE" statements
125     --skip=t1[,t2]  Comma-separated list of tables to skip
126
127 USAGE
128     exit(0);
129 }
130
131 my \%skip = map { \$_, 1 } map { s/^\\s+|\\s+\$//; \$_ } split (/,/, \$skip);
132 my \$db = DBI->connect('$dsn', '$db_user', '$db_pass');
133
134 EOF
135
136 for my $table ( $schema->get_tables ) {
137     my $table_name  = $table->name;
138     next if $skip{ $table_name };
139     my ( @field_names, %types );
140     for my $field ( $table->get_fields ) {
141         $types{ $field->name } = $field->data_type =~ m/(char|str|long|text)/
142             ? 'string' : 'number';
143         push @field_names, $field->name;
144     }
145
146     $out .= join('',
147         "#\n# Table: $table_name\n#\n{\n",
148         "    next if \$skip{'$table_name'};\n",
149         "    print \"--\\n-- Data for table '$table_name'\\n--\\n\";\n\n",
150         "    if ( \$add_truncate ) {\n",
151         "        print \"TRUNCATE TABLE $table_name;\\n\";\n",
152         "    }\n\n",
153     );
154
155     my $insert = "INSERT INTO $table_name (". join(', ', @field_names).
156             ') VALUES (';
157
158     if ( $add_truncate ) {
159         $out .= "    print \"TRUNCATE TABLE $table_name;\\n\";\n";
160     }
161
162     $out .= join('',
163         "    my \%types = (\n",
164         join("\n", map { "        $_ => '$types{ $_ }'," } @field_names), 
165         "\n    );\n\n",
166         "    my \$data  = \$db->selectall_arrayref(\n",
167         "        'select ", join(', ', @field_names), " from $table_name',\n",
168         "        { Columns => {} },\n",
169         "    );\n\n",
170         "    for my \$rec ( \@{ \$data } ) {\n",
171         "        my \@vals;\n",
172         "        for my \$fld ( qw[", join(' ', @field_names), "] ) {\n",
173         "            my \$val = \$rec->{ \$fld };\n",
174         "            if ( \$types{ \$fld } eq 'string' ) {\n",
175         "                \$val =~ s/'/\\'/g;\n",
176         "                \$val = defined \$val ? qq['\$val'] : qq[''];\n",
177         "            }\n",
178         "            else {\n",
179         "                \$val = defined \$val ? \$val : 'NULL';\n",
180         "            }\n",
181         "            push \@vals, \$val;\n",
182         "        }\n",
183         "        print \"$insert\", join(', ', \@vals), \");\\n\";\n",
184         "    }\n",
185         "    print \"\\n\";\n",
186         "}\n\n",
187     );
188 }
189
190 print $out;
191 exit(0);
192
193 # -------------------------------------------------------------------
194
195 =pod
196
197 =head1 AUTHOR
198
199 Ken Y. Clark E<lt>kclark@cpan.orgE<gt>.
200
201 =head1 SEE ALSO
202
203 perl, SQL::Translator.
204
205 =cut