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