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