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