Added "enum" to string types.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Producer / Dumper.pm
CommitLineData
fa44993c 1package SQL::Translator::Producer::Dumper;
2
3# -------------------------------------------------------------------
e42b8ca2 4# $Id: Dumper.pm,v 1.6 2004-07-14 19:37:41 kycl4rk Exp $
fa44993c 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
25SQL::Translator::Producer::Dumper - SQL Dumper producer for SQL::Translator
26
27=head1 SYNOPSIS
28
29 use SQL::Translator::Producer::Dumper;
30
31 Options:
32
fa44993c 33 db_user Database username
34 db_password Database password
35 dsn DSN for DBI
36 mysql_loadfile Create MySQL's LOAD FILE syntax instead of INSERTs
37 skip=t1[,t2] Skip tables in comma-separated list
38 skiplike=regex Skip tables in comma-separated list
39
40=head1 DESCRIPTION
41
42This producer creates a Perl script that can connect to a database and
43dump the data as INSERT statements (a la mysqldump) or as a file
44suitable for MySQL's LOAD DATA command. If you enable "add-truncate"
45or specify tables to "skip" (also using the "skiplike" regular
46expression) then the generated dumper script will leave out those
47tables. However, these will also be options in the generated dumper,
48so you can wait to specify these options when you dump your database.
49The database username, password, and DSN can be hardcoded into the
50generated script, or part of the DSN can be intuited from the
51"database" argument.
52
53=cut
54
55use strict;
56use Config;
57use SQL::Translator;
58use File::Temp 'tempfile';
59use Template;
60use vars qw($VERSION);
61
62use Data::Dumper;
63
e42b8ca2 64$VERSION = sprintf "%d.%02d", q$Revision: 1.6 $ =~ /(\d+)\.(\d+)/;
fa44993c 65
66sub produce {
67 my $t = shift;
68 my $args = $t->producer_args;
69 my $schema = $t->schema;
70 my $add_truncate = $args->{'add_truncate'} || 0;
71 my $skip = $args->{'skip'} || '';
72 my $skiplike = $args->{'skiplike'} || '';
73 my $db_user = $args->{'db_user'} || 'db_user';
74 my $db_pass = $args->{'db_password'} || 'db_pass';
75 my $parser_name = $t->parser_type;
76 my %skip = map { $_, 1 } map { s/^\s+|\s+$//; $_ }
77 split (/,/, $skip);
78 my $sqlt_version = $t->version;
79
80 if ( $parser_name =~ /Parser::(\w+)$/ ) {
81 $parser_name = $1
82 }
83
84 my %type_to_dbd = (
85 MySQL => 'mysql',
86 Oracle => 'Oracle',
87 PostgreSQL => 'Pg',
88 SQLite => 'SQLite',
89 Sybase => 'Sybase',
90 );
91 my $dbd = $type_to_dbd{ $parser_name } || 'DBD';
92 my $dsn = $args->{'dsn'} || "dbi:$dbd:";
93 if ( $dbd eq 'Pg' && ! $args->{'dsn'} ) {
94 $dsn .= 'dbname=dbname;host=hostname';
95 }
96 elsif ( $dbd eq 'Oracle' && ! $args->{'dsn'} ) {
97 $db_user = "$db_user/$db_pass@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)" .
98 "(HOST=hostname)(PORT=1521))(CONNECT_DATA=(SID=sid)))";
99 $db_pass = '';
100 }
101 elsif ( $dbd eq 'mysql' && ! $args->{'dsn'} ) {
102 $dsn .= 'dbname';
103 }
104
105 my $template = Template->new;
106 my $template_text = template();
107 my $out;
108 $template->process(
109 \$template_text,
110 {
111 translator => $t,
112 schema => $schema,
113 db_user => $db_user,
114 db_pass => $db_pass,
115 dsn => $dsn,
116 perl => $Config{'startperl'},
117 skip => \%skip,
118 skiplike => $skiplike,
119 },
120 \$out
121 ) or die $template->error;
122
123 return $out;
124}
125
126# -------------------------------------------------------------------
127sub template {
128#
129# Returns the template to be processed by Template Toolkit
130#
131 return <<'EOF';
132[% perl || '#!/usr/bin/perl' %]
133[% USE date %]
134#
135# Generated by SQL::Translator [% translator.version %]
136# [% date.format( date.now, "%Y-%m-%d" ) %]
137# For more info, see http://sqlfairy.sourceforge.net/
138#
139
140use strict;
141use Cwd;
142use DBI;
143use Getopt::Long;
144use File::Spec::Functions 'catfile';
145
b7df9b7e 146my ( $help, $add_truncate, $skip, $skiplike, $no_comments,
147 $takelike, $mysql_loadfile );
fa44993c 148GetOptions(
149 'add-truncate' => \$add_truncate,
150 'h|help' => \$help,
151 'no-comments' => \$no_comments,
152 'mysql-loadfile' => \$mysql_loadfile,
153 'skip:s' => \$skip,
154 'skiplike:s' => \$skiplike,
b7df9b7e 155 'takelike:s' => \$takelike,
fa44993c 156);
157
158if ( $help ) {
159 print <<"USAGE";
160Usage:
25afc779 161 $0 [options] > dump.sql
fa44993c 162
163 Options:
164 -h|--help Show help and exit
165 --add-truncate Add "TRUNCATE TABLE" statements
166 --mysql-loadfile Create MySQL's LOAD FILE syntax, not INSERTs
167 --no-comments Suppress comments
168 --skip=t1[,t2] Comma-separated list of tables to skip
169 --skiplike=regex Comma-separated list of tables to skip
170
171USAGE
172 exit(0);
173}
174
175$no_comments = 1 if $mysql_loadfile;
176
177[%-
178SET table_defs = [];
179SET max_field = 0;
180
181FOREACH table IN schema.get_tables;
182 SET table_name = table.name;
183 NEXT IF skip.$table_name;
184 NEXT IF skiplike AND table_name.match("(?:$skiplike)");
185
186 SET field_names = [];
187 SET types = {};
188 FOR field IN table.get_fields;
189 field_name = field.name;
190 fname_len = field.name.length;
191 max_field = fname_len > max_field ? fname_len : max_field;
e42b8ca2 192 types.$field_name = field.data_type.match( '(char|str|long|text|enum)' )
fa44993c 193 ? 'string' : 'number';
194 field_names.push( field_name );
195 END;
196
197 table_defs.push({
198 name => table_name,
199 types => types,
200 fields => field_names,
201 });
202END
203-%]
204
e96fe4d3 205my $db = DBI->connect(
206 '[% dsn %]',
207 '[% db_user %]',
208 '[% db_pass %]',
209 { RaiseError => 1 }
210);
fa44993c 211my %skip = map { $_, 1 } map { s/^\s+|\s+$//; $_ } split (/,/, $skip);
212my @tables = (
213[%- FOREACH t IN table_defs %]
214 {
215 table_name => '[% t.name %]',
216 fields => [ qw/ [% t.fields.join(' ') %] / ],
217 types => {
218 [%- FOREACH fname IN t.types.keys %]
219 '[% fname %]' => '[% t.types.$fname %]',
220 [%- END %]
221 },
222 },
223[%- END %]
224);
225
226for my $table ( @tables ) {
227 my $table_name = $table->{'table_name'};
228 next if $skip{ $table_name };
229 next if $skiplike && $table_name =~ qr/$skiplike/;
b7df9b7e 230 next if $takelike && $table_name !~ qr/$takelike/;
fa44993c 231
232 my ( $out_fh, $outfile );
233 if ( $mysql_loadfile ) {
234 $outfile = catfile( cwd(), "$table_name.txt" );
235 open $out_fh, ">$outfile" or
236 die "Can't write LOAD FILE to '$table_name': $!\n";
237 }
238
239 print "--\n-- Data for table '$table_name'\n--\n" unless $no_comments;
240
241 if ( $add_truncate ) {
242 print "TRUNCATE TABLE $table_name;\n";
243 }
244
245 my $data = $db->selectall_arrayref(
246 'select ' . join(', ', @{ $table->{'fields'} } ) . " from $table_name",
247 { Columns => {} }
248 );
249
250 for my $rec ( @{ $data } ) {
251 my @vals;
252 for my $fld ( @{ $table->{'fields'} } ) {
253 my $val = $rec->{ $fld };
254 if ( $table->{'types'}{ $fld } eq 'string' ) {
255 if ( defined $val ) {
256 $val =~ s/'/\\'/g;
257 $val = qq['$val']
258 }
259 else {
260 $val = qq[''];
261 }
262 }
263 else {
264 $val = defined $val ? $val : $mysql_loadfile ? '\N' : 'NULL';
265 }
266 push @vals, $val;
267 }
268
269 if ( $mysql_loadfile ) {
270 print $out_fh join("\t", @vals), "\n";
271 }
272 else {
273 print "INSERT INTO $table_name (".
274 join(', ', @{ $table->{'fields'} }) .
afff8ac6 275 ') VALUES (', join(', ', @vals), ");\n";
fa44993c 276 }
277 }
278
279 if ( $out_fh ) {
280 print "LOAD DATA INFILE '$outfile' INTO TABLE $table_name ",
281 "FIELDS OPTIONALLY ENCLOSED BY '\\'';\n";
282 close $out_fh or die "Can't close filehandle: $!\n";
283 }
284 else {
285 print "\n";
286 }
287}
288EOF
289}
290
2911;
292
293# -------------------------------------------------------------------
294# To create a little flower is the labour of ages.
295# William Blake
296# -------------------------------------------------------------------
297
298=pod
299
300=head1 AUTHOR
301
302Ken Y. Clark E<lt>kclark@cpan.orgE<gt>.
303
304=cut