1 package SQL::Translator;
3 # ----------------------------------------------------------------------
4 # $Id: Translator.pm,v 1.19 2003-03-12 14:19:52 dlc Exp $
5 # ----------------------------------------------------------------------
6 # Copyright (C) 2003 Ken Y. Clark <kclark@cpan.org>,
7 # darren chamberlain <darren@cpan.org>,
8 # Chris Mungall <cjm@fruitfly.org>
10 # This program is free software; you can redistribute it and/or
11 # modify it under the terms of the GNU General Public License as
12 # published by the Free Software Foundation; version 2.
14 # This program is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 # General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 # -------------------------------------------------------------------
26 use vars qw( $VERSION $REVISION $DEFAULT_SUB $DEBUG $ERROR );
27 use base 'Class::Base';
30 $REVISION = sprintf "%d.%02d", q$Revision: 1.19 $ =~ /(\d+)\.(\d+)/;
31 $DEBUG = 0 unless defined $DEBUG;
36 use File::Spec::Functions qw(catfile);
37 use File::Basename qw(dirname);
40 # ----------------------------------------------------------------------
41 # The default behavior is to "pass through" values (note that the
42 # SQL::Translator instance is the first value ($_[0]), and the stuff
43 # to be parsed is the second value ($_[1])
44 # ----------------------------------------------------------------------
45 $DEFAULT_SUB = sub { $_[1] } unless defined $DEFAULT_SUB;
47 # ----------------------------------------------------------------------
51 # new takes an optional hash of arguments. These arguments may
52 # include a parser, specified with the keys "parser" or "from",
53 # and a producer, specified with the keys "producer" or "to".
55 # The values that can be passed as the parser or producer are
56 # given directly to the parser or producer methods, respectively.
57 # See the appropriate method description below for details about
58 # what each expects/accepts.
59 # ----------------------------------------------------------------------
61 my ( $self, $config ) = @_;
64 # Set the parser and producer.
66 # If a 'parser' or 'from' parameter is passed in, use that as the
67 # parser; if a 'producer' or 'to' parameter is passed in, use that
68 # as the producer; both default to $DEFAULT_SUB.
70 $self->parser ($config->{'parser'} || $config->{'from'} || $DEFAULT_SUB);
71 $self->producer($config->{'producer'} || $config->{'to'} || $DEFAULT_SUB);
74 # Set the parser_args and producer_args
76 for my $pargs ( qw[ parser_args producer_args ] ) {
77 $self->$pargs( $config->{$pargs} ) if defined $config->{ $pargs };
81 # Set the data source, if 'filename' or 'file' is provided.
83 $config->{'filename'} ||= $config->{'file'} || "";
84 $self->filename( $config->{'filename'} ) if $config->{'filename'};
87 # Finally, if there is a 'data' parameter, use that in
88 # preference to filename and file
90 if ( my $data = $config->{'data'} ) {
95 # Set various other options.
97 $self->{'debug'} = defined $config->{'debug'} ? $config->{'debug'} : $DEBUG;
100 $self->add_drop_table( $config->{'add_drop_table'} );
102 $self->custom_translate( $config->{'xlate'} );
104 $self->no_comments( $config->{'no_comments'} );
106 $self->show_warnings( $config->{'show_warnings'} );
108 $self->trace( $config->{'trace'} );
113 # ----------------------------------------------------------------------
114 # add_drop_table([$bool])
115 # ----------------------------------------------------------------------
118 if ( defined (my $arg = shift) ) {
119 $self->{'add_drop_table'} = $arg ? 1 : 0;
121 return $self->{'add_drop_table'} || 0;
125 # ----------------------------------------------------------------------
126 # custom_translate([$bool])
127 # ----------------------------------------------------------------------
128 sub custom_translate {
130 $self->{'custom_translate'} = shift if @_;
131 return $self->{'custom_translate'} || {};
134 # ----------------------------------------------------------------------
135 # no_comments([$bool])
136 # ----------------------------------------------------------------------
140 if ( defined $arg ) {
141 $self->{'no_comments'} = $arg ? 1 : 0;
143 return $self->{'no_comments'} || 0;
147 # ----------------------------------------------------------------------
148 # producer([$producer_spec])
150 # Get or set the producer for the current translator.
151 # ----------------------------------------------------------------------
155 # producer as a mutator
157 my $producer = shift;
159 # Passed a module name (string containing "::")
160 if ($producer =~ /::/) {
163 # Module name was passed directly
164 # We try to load the name; if it doesn't load, there's
165 # a possibility that it has a function name attached to
167 if (load($producer)) {
168 $func_name = "produce";
171 # Module::function was passed
173 # Passed Module::Name::function; try to recover
174 my @func_parts = split /::/, $producer;
175 $func_name = pop @func_parts;
176 $producer = join "::", @func_parts;
178 # If this doesn't work, then we have a legitimate
180 load($producer) or die "Can't load $producer: $@";
183 # get code reference and assign
184 $self->{'producer'} = \&{ "$producer\::$func_name" };
185 $self->{'producer_type'} = $producer;
186 $self->debug("Got producer: $producer\::$func_name\n");
189 # passed an anonymous subroutine reference
190 elsif (isa($producer, 'CODE')) {
191 $self->{'producer'} = $producer;
192 $self->{'producer_type'} = "CODE";
193 $self->debug("Got producer: code ref\n");
196 # passed a string containing no "::"; relative package name
198 my $Pp = sprintf "SQL::Translator::Producer::$producer";
199 load($Pp) or die "Can't load $Pp: $@";
200 $self->{'producer'} = \&{ "$Pp\::produce" };
201 $self->{'producer_type'} = $Pp;
202 $self->debug("Got producer: $Pp\n");
205 # At this point, $self->{'producer'} contains a subroutine
206 # reference that is ready to run
208 # Anything left? If so, it's producer_args
209 $self->producer_args(@_) if (@_);
212 return $self->{'producer'};
215 # ----------------------------------------------------------------------
218 # producer_type is an accessor that allows producer subs to get
219 # information about their origin. This is poptentially important;
220 # since all producer subs are called as subroutine references, there is
221 # no way for a producer to find out which package the sub lives in
222 # originally, for example.
223 # ----------------------------------------------------------------------
224 sub producer_type { $_[0]->{'producer_type'} }
226 # ----------------------------------------------------------------------
227 # producer_args([\%args])
229 # Arbitrary name => value pairs of paramters can be passed to a
230 # producer using this method.
232 # If the first argument passed in is undef, then the hash of arguments
233 # is cleared; all subsequent elements are added to the hash of name,
234 # value pairs stored as producer_args.
235 # ----------------------------------------------------------------------
238 return $self->_args("producer", @_);
241 # ----------------------------------------------------------------------
242 # parser([$parser_spec])
243 # ----------------------------------------------------------------------
247 # parser as a mutator
251 # Passed a module name (string containing "::")
252 if ($parser =~ /::/) {
255 # Module name was passed directly
256 # We try to load the name; if it doesn't load, there's
257 # a possibility that it has a function name attached to
260 $func_name = "parse";
263 # Module::function was passed
265 # Passed Module::Name::function; try to recover
266 my @func_parts = split /::/, $parser;
267 $func_name = pop @func_parts;
268 $parser = join "::", @func_parts;
270 # If this doesn't work, then we have a legitimate
272 load($parser) or die "Can't load $parser: $@";
275 # get code reference and assign
276 $self->{'parser'} = \&{ "$parser\::$func_name" };
277 $self->{'parser_type'} = $parser;
278 $self->debug("Got parser: $parser\::$func_name\n");
281 # passed an anonymous subroutine reference
282 elsif ( isa( $parser, 'CODE' ) ) {
283 $self->{'parser'} = $parser;
284 $self->{'parser_type'} = "CODE";
285 $self->debug("Got parser: code ref\n");
288 # passed a string containing no "::"; relative package name
290 my $Pp = "SQL::Translator::Parser::$parser";
291 load( $Pp ) or die "Can't load $Pp: $@";
292 $self->{'parser'} = \&{ "$Pp\::parse" };
293 $self->{'parser_type'} = $Pp;
294 $self->debug("Got parser: $Pp\n");
298 # At this point, $self->{'parser'} contains a subroutine
299 # reference that is ready to run
301 $self->parser_args( @_ ) if (@_);
304 return $self->{'parser'};
307 # ----------------------------------------------------------------------
308 sub parser_type { $_[0]->{'parser_type'} }
312 return $self->_args("parser", @_);
318 if ( defined $arg ) {
319 $self->{'show_warnings'} = $arg ? 1 : 0;
321 return $self->{'show_warnings'} || 0;
325 # filename - get or set the filename
329 my $filename = shift;
331 my $msg = "Cannot use directory '$filename' as input source";
332 return $self->error($msg);
333 } elsif (-f _ && -r _) {
334 $self->{'filename'} = $filename;
335 $self->debug("Got filename: '$self->{'filename'}'\n");
337 my $msg = "Cannot use '$filename' as input source: ".
338 "file does not exist or is not readable.";
339 return $self->error($msg);
346 # ----------------------------------------------------------------------
349 # if $self->{'data'} is not set, but $self->{'filename'} is, then
350 # $self->{'filename'} is opened and read, with the results put into
352 # ----------------------------------------------------------------------
356 # Set $self->{'data'} based on what was passed in. We will
357 # accept a number of things; do our best to get it right.
360 if (isa($data, "SCALAR")) {
361 $self->{'data'} = $data;
364 if (isa($data, 'ARRAY')) {
365 $data = join '', @$data;
367 elsif (isa($data, 'GLOB')) {
371 elsif (! ref $data && @_) {
372 $data = join '', $data, @_;
374 $self->{'data'} = \$data;
378 # If we have a filename but no data yet, populate.
379 if (not $self->{'data'} and my $filename = $self->filename) {
380 $self->debug("Opening '$filename' to get contents.\n");
385 unless (open FH, $filename) {
386 return $self->error("Can't read file '$filename': $!");
390 $self->{'data'} = \$data;
393 return $self->error("Can't close file '$filename': $!");
397 return $self->{'data'};
404 if ( defined $arg ) {
405 $self->{'trace'} = $arg ? 1 : 0;
407 return $self->{'trace'} || 0;
410 # ----------------------------------------------------------------------
411 # translate([source], [\%args])
413 # translate does the actual translation. The main argument is the
414 # source of the data to be translated, which can be a filename, scalar
415 # reference, or glob reference.
417 # Alternatively, translate takes optional arguements, which are passed
418 # to the appropriate places. Most notable of these arguments are
419 # parser and producer, which can be used to set the parser and
420 # producer, respectively. This is the applications last chance to set
423 # translate returns a string.
424 # ----------------------------------------------------------------------
427 my ($args, $parser, $parser_type, $producer, $producer_type);
428 my ($parser_output, $producer_output);
432 # Passed a reference to a hash?
433 if (isa($_[0], 'HASH')) {
435 $self->debug("translate: Got a hashref\n");
439 # Passed a GLOB reference, i.e., filehandle
440 elsif (isa($_[0], 'GLOB')) {
441 $self->debug("translate: Got a GLOB reference\n");
445 # Passed a reference to a string containing the data
446 elsif (isa($_[0], 'SCALAR')) {
447 # passed a ref to a string
448 $self->debug("translate: Got a SCALAR reference (string)\n");
452 # Not a reference; treat it as a filename
453 elsif (! ref $_[0]) {
454 # Not a ref, it's a filename
455 $self->debug("translate: Got a filename\n");
456 $self->filename($_[0]);
459 # Passed something else entirely.
461 # We're not impressed. Take your empty string and leave.
464 # Actually, if data, parser, and producer are set, then we
465 # can continue. Too bad, because I like my comment
467 return "" unless ($self->data &&
473 # You must pass in a hash, or you get nothing.
478 # ----------------------------------------------------------------------
479 # Can specify the data to be transformed using "filename", "file",
480 # "data", or "datasource".
481 # ----------------------------------------------------------------------
482 if (my $filename = ($args->{'filename'} || $args->{'file'})) {
483 $self->filename($filename);
486 if (my $data = ($self->{'data'} || $self->{'datasource'})) {
490 # ----------------------------------------------------------------
492 # ----------------------------------------------------------------
493 my $data = $self->data;
494 unless (length $$data) {
495 return $self->error("Empty data file!");
498 # ----------------------------------------------------------------
499 # Local reference to the parser subroutine
500 # ----------------------------------------------------------------
501 if ($parser = ($args->{'parser'} || $args->{'from'})) {
502 $self->parser($parser);
504 $parser = $self->parser;
505 $parser_type = $self->parser_type;
507 # ----------------------------------------------------------------
508 # Local reference to the producer subroutine
509 # ----------------------------------------------------------------
510 if ($producer = ($args->{'producer'} || $args->{'to'})) {
511 $self->producer($producer);
513 $producer = $self->producer;
514 $producer_type = $self->producer_type;
516 # ----------------------------------------------------------------
517 # Execute the parser, then execute the producer with that output.
518 # Allowances are made for each piece to die, or fail to compile,
519 # since the referenced subroutines could be almost anything. In
520 # the future, each of these might happen in a Safe environment,
521 # depending on how paranoid we want to be.
522 # ----------------------------------------------------------------
523 eval { $parser_output = $parser->($self, $$data) };
524 if ($@ || ! $parser_output) {
525 my $msg = sprintf "translate: Error with parser '%s': %s",
526 $parser_type, ($@) ? $@ : " no results";
527 return $self->error($msg);
530 eval { $producer_output = $producer->($self, $parser_output) };
531 if ($@ || ! $producer_output) {
532 my $msg = sprintf "translate: Error with producer '%s': %s",
533 $producer_type, ($@) ? $@ : " no results";
534 return $self->error($msg);
537 return $producer_output;
540 # ----------------------------------------------------------------------
543 # Hacky sort of method to list all available parsers. This has
546 # - Only finds things in the SQL::Translator::Parser namespace
548 # - Only finds things that are located in the same directory
549 # as SQL::Translator::Parser. Yeck.
551 # This method will fail in several very likely cases:
553 # - Parser modules in different namespaces
555 # - Parser modules in the SQL::Translator::Parser namespace that
556 # have any XS componenets will be installed in
557 # arch_lib/SQL/Translator.
559 # ----------------------------------------------------------------------
561 return shift->_list("parser");
564 # ----------------------------------------------------------------------
567 # See notes for list_parsers(), above; all the problems apply to
568 # list_producers as well.
569 # ----------------------------------------------------------------------
571 return shift->_list("producer");
575 # ======================================================================
577 # ======================================================================
579 # ----------------------------------------------------------------------
580 # _args($type, \%args);
582 # Gets or sets ${type}_args. Called by parser_args and producer_args.
583 # ----------------------------------------------------------------------
587 $type = "${type}_args" unless $type =~ /_args$/;
589 unless (defined $self->{$type} && isa($self->{$type}, 'HASH')) {
590 $self->{$type} = { };
594 # If the first argument is an explicit undef (remember, we
595 # don't get here unless there is stuff in @_), then we clear
596 # out the producer_args hash.
597 if (! defined $_[0]) {
599 %{$self->{$type}} = ();
602 my $args = isa($_[0], 'HASH') ? shift : { @_ };
603 %{$self->{$type}} = (%{$self->{$type}}, %$args);
610 # ----------------------------------------------------------------------
612 # ----------------------------------------------------------------------
615 my $type = shift || return ();
616 my $uctype = ucfirst lc $type;
619 load("SQL::Translator::$uctype") or return ();
620 my $path = catfile "SQL", "Translator", $uctype;
622 my $dir = catfile $_, $path;
623 $self->debug("_list_${type}s searching $dir");
626 my $dh = IO::Dir->new($dir);
627 for (grep /\.pm$/, $dh->read) {
629 $found{ join "::", "SQL::Translator::$uctype", $_ } = 1;
636 # ----------------------------------------------------------------------
639 # Loads a Perl module. Short circuits if a module is already loaded.
640 # ----------------------------------------------------------------------
642 my $module = do { my $m = shift; $m =~ s[::][/]g; "$m.pm" };
643 return 1 if $INC{$module};
645 eval { require $module };
647 return __PACKAGE__->error($@) if ($@);
651 # ----------------------------------------------------------------------
654 # Calls UNIVERSAL::isa($ref, $type). I think UNIVERSAL::isa is ugly,
655 # but I like function overhead.
656 # ----------------------------------------------------------------------
658 my ($ref, $type) = @_;
659 return UNIVERSAL::isa($ref, $type);
663 #-----------------------------------------------------
664 # Rescue the drowning and tie your shoestrings.
665 # Henry David Thoreau
666 #-----------------------------------------------------
672 SQL::Translator - convert schema from one database to another
678 my $translator = SQL::Translator->new(
679 debug => 1, # Print debug info
680 trace => 0, # Print Parse::RecDescent trace
681 no_comments => 0, # Don't include comments in output
682 show_warnings => 0, # Print name mutations, conflicts
683 add_drop_table => 1, # Add "drop table" statements
686 my $output = $translator->translate(
690 ) or die $translator->error;
696 This module attempts to simplify the task of converting one database
697 create syntax to another through the use of Parsers (which understand
698 the source format) and Producers (which understand the destination
699 format). The idea is that any Parser can be used with any Producer in
700 the conversion process. So, if you wanted Postgres-to-Oracle, you
701 would use the Postgres parser and the Oracle producer.
705 The constructor is called B<new>, and accepts a optional hash of options.
740 All options are, well, optional; these attributes can be set via
741 instance methods. Internally, they are; no (non-syntactical)
742 advantage is gained by passing options to the constructor.
746 =head2 B<add_drop_table>
748 Toggles whether or not to add "DROP TABLE" statements just before the
751 =head2 B<custom_translate>
753 Allows the user to override default translation of fields. For example,
754 if a MySQL "text" field would normally be converted to a "long" for Oracle,
755 the user could specify to change it to a "CLOB." Accepts a hashref where
756 keys are the "from" value and values are the "to," returns the current
759 =head2 B<no_comments>
761 Toggles whether to print comments in the output. Accepts a true or false
762 value, returns the current value.
766 The B<producer> method is an accessor/mutator, used to retrieve or
767 define what subroutine is called to produce the output. A subroutine
768 defined as a producer will be invoked as a function (I<not a method>)
769 and passed 2 parameters: its container C<SQL::Translator> instance and a
770 data structure. It is expected that the function transform the data
771 structure to a string. The C<SQL::Transformer> instance is provided for
772 informational purposes; for example, the type of the parser can be
773 retrieved using the B<parser_type> method, and the B<error> and
774 B<debug> methods can be called when needed.
776 When defining a producer, one of several things can be passed in: A
777 module name (e.g., C<My::Groovy::Producer>, a module name relative to
778 the C<SQL::Translator::Producer> namespace (e.g., MySQL), a module
779 name and function combination (C<My::Groovy::Producer::transmogrify>),
780 or a reference to an anonymous subroutine. If a full module name is
781 passed in (for the purposes of this method, a string containing "::"
782 is considered to be a module name), it is treated as a package, and a
783 function called "produce" will be invoked: C<$modulename::produce>.
784 If $modulename cannot be loaded, the final portion is stripped off and
785 treated as a function. In other words, if there is no file named
786 F<My/Groovy/Producer/transmogrify.pm>, C<SQL::Translator> will attempt
787 to load F<My/Groovy/Producer.pm> and use transmogrify as the name of
788 the function, instead of the default "produce".
790 my $tr = SQL::Translator->new;
792 # This will invoke My::Groovy::Producer::produce($tr, $data)
793 $tr->producer("My::Groovy::Producer");
795 # This will invoke SQL::Translator::Producer::Sybase::produce($tr, $data)
796 $tr->producer("Sybase");
798 # This will invoke My::Groovy::Producer::transmogrify($tr, $data),
799 # assuming that My::Groovy::Producer::transmogrify is not a module
801 $tr->producer("My::Groovy::Producer::transmogrify");
803 # This will invoke the referenced subroutine directly, as
804 # $subref->($tr, $data);
805 $tr->producer(\&my_producer);
807 There is also a method named B<producer_type>, which is a string
808 containing the classname to which the above B<produce> function
809 belongs. In the case of anonymous subroutines, this method returns
812 Finally, there is a method named B<producer_args>, which is both an
813 accessor and a mutator. Arbitrary data may be stored in name => value
814 pairs for the producer subroutine to access:
816 sub My::Random::producer {
817 my ($tr, $data) = @_;
818 my $pr_args = $tr->producer_args();
820 # $pr_args is a hashref.
822 Extra data passed to the B<producer> method is passed to
825 $tr->producer("xSV", delimiter => ',\s*');
827 # In SQL::Translator::Producer::xSV:
828 my $args = $tr->producer_args;
829 my $delimiter = $args->{'delimiter'}; # value is ,\s*
833 The B<parser> method defines or retrieves a subroutine that will be
834 called to perform the parsing. The basic idea is the same as that of
835 B<producer> (see above), except the default subroutine name is
836 "parse", and will be invoked as C<$module_name::parse($tr, $data)>.
837 Also, the parser subroutine will be passed a string containing the
838 entirety of the data to be parsed.
840 # Invokes SQL::Translator::Parser::MySQL::parse()
841 $tr->parser("MySQL");
843 # Invokes My::Groovy::Parser::parse()
844 $tr->parser("My::Groovy::Parser");
846 # Invoke an anonymous subroutine directly
848 my $dumper = Data::Dumper->new([ $_[1] ], [ "SQL" ]);
849 $dumper->Purity(1)->Terse(1)->Deepcopy(1);
850 return $dumper->Dump;
853 There is also B<parser_type> and B<parser_args>, which perform
854 analogously to B<producer_type> and B<producer_args>
856 =head2 B<show_warnings>
858 Toggles whether to print warnings of name conflicts, identifier
859 mutations, etc. Probably only generated by producers to let the user
860 know when something won't translate very smoothly (e.g., MySQL "enum"
861 fields into Oracle). Accepts a true or false value, returns the
866 The B<translate> method calls the subroutines referenced by the
867 B<parser> and B<producer> data members (described above). It accepts
868 as arguments a number of things, in key => value format, including
869 (potentially) a parser and a producer (they are passed directly to the
870 B<parser> and B<producer> methods).
872 Here is how the parameter list to B<translate> is parsed:
878 1 argument means it's the data to be parsed; which could be a string
879 (filename) or a reference to a scalar (a string stored in memory), or a
880 reference to a hash, which is parsed as being more than one argument
883 # Parse the file /path/to/datafile
884 my $output = $tr->translate("/path/to/datafile");
886 # Parse the data contained in the string $data
887 my $output = $tr->translate(\$data);
891 More than 1 argument means its a hash of things, and it might be
892 setting a parser, producer, or datasource (this key is named
893 "filename" or "file" if it's a file, or "data" for a SCALAR reference.
895 # As above, parse /path/to/datafile, but with different producers
896 for my $prod ("MySQL", "XML", "Sybase") {
897 print $tr->translate(
899 filename => "/path/to/datafile",
903 # The filename hash key could also be:
904 datasource => \$data,
910 =head2 B<filename>, B<data>
912 Using the B<filename> method, the filename of the data to be parsed
913 can be set. This method can be used in conjunction with the B<data>
914 method, below. If both the B<filename> and B<data> methods are
915 invoked as mutators, the data set in the B<data> method is used.
917 $tr->filename("/my/data/files/create.sql");
921 my $create_script = do {
923 open CREATE, "/my/data/files/create.sql" or die $!;
926 $tr->data(\$create_script);
928 B<filename> takes a string, which is interpreted as a filename.
929 B<data> takes a reference to a string, which is used as the data to be
930 parsed. If a filename is set, then that file is opened and read when
931 the B<translate> method is called, as long as the data instance
938 Turns on/off the tracing option of Parse::RecDescent.
944 Ken Y. Clark, E<lt>kclark@cpan.orgE<gt>,
945 darren chamberlain E<lt>darren@cpan.orgE<gt>,
946 Chris Mungall E<lt>cjm@fruitfly.orgE<gt>,
947 Allen Day E<lt>allenday@users.sourceforge.netE<gt>
951 This program is free software; you can redistribute it and/or modify
952 it under the terms of the GNU General Public License as published by
953 the Free Software Foundation; version 2.
955 This program is distributed in the hope that it will be useful, but
956 WITHOUT ANY WARRANTY; without even the implied warranty of
957 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
958 General Public License for more details.
960 You should have received a copy of the GNU General Public License
961 along with this program; if not, write to the Free Software
962 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
967 Please use http://rt.cpan.org/ for reporting bugs.
972 L<SQL::Translator::Parser>,
973 L<SQL::Translator::Producer>,