Rolled in Darren's new list_[producers|parsers], lots of cosmetic changes,
Ken Youens-Clark [Fri, 22 Nov 2002 03:03:40 +0000 (03:03 +0000)]
working in Tim Bunce's "mysql2ora" grammar and producer logic into
appropriate modules, making output of producers more consistent, added
table order to MySQL parser, using it in Oracle producer to maintain
original ordering.

12 files changed:
bin/sql_translator.pl
lib/SQL/Translator.pm
lib/SQL/Translator/Parser.pm
lib/SQL/Translator/Parser/MySQL.pm
lib/SQL/Translator/Parser/Sybase.pm
lib/SQL/Translator/Parser/xSV.pm
lib/SQL/Translator/Producer.pm
lib/SQL/Translator/Producer/MySQL.pm
lib/SQL/Translator/Producer/Oracle.pm
lib/SQL/Translator/Producer/PostgreSQL.pm
lib/SQL/Translator/Producer/XML.pm
lib/SQL/Translator/Validator.pm

index 34d89c8..2465737 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -w
 
 # -------------------------------------------------------------------
-# $Id: sql_translator.pl,v 1.4 2002-11-20 04:03:02 kycl4rk Exp $
+# $Id: sql_translator.pl,v 1.5 2002-11-22 03:03:40 kycl4rk Exp $
 # -------------------------------------------------------------------
 # Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
 #                    darren chamberlain <darren@cpan.org>
@@ -29,35 +29,73 @@ use SQL::Translator;
 use Data::Dumper;
 
 use vars qw( $VERSION );
-$VERSION = sprintf "%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.5 $ =~ /(\d+)\.(\d+)/;
 
 my $from;        # the original database
 my $to;          # the destination database 
 my $help;        # show POD and bail
 my $stdin;       # whether to read STDIN for create script
 my $no_comments; # whether to put comments in out file
-my $verbose;     # whether to print progress/debug
+my $xlate;       # user overrides for field translation
+my $debug;       # whether to print debug info
+my $trace;       # whether to print parser trace
+my $list;        # list all parsers and producers
 
 #
 # Get options, explain how to use the script if necessary.
 #
 GetOptions(
-    'f|from|parser=s' => \$from,
-    't|to|producer=s' => \$to,
+    'f|from|parser:s' => \$from,
+    't|to|producer:s' => \$to,
     'h|help'          => \$help,
-    'v|verbose'       => \$verbose,
-    'no_comments'     => \$no_comments,
+    'l|list'          => \$list,
+    'd|debug'         => \$debug,
+    'trace'           => \$trace,
+    'no-comments'     => \$no_comments,
+    'xlate=s'         => \$xlate,
 ) or pod2usage(2);
 
-my @files = @ARGV; # the create script for the original db
+my @files = @ARGV; # the create script(s) for the original db
 
 pod2usage(1) if $help;
-pod2usage(2) unless $from && $to && @files;
+
+if ( $xlate ) {
+    my @fields = split /,/, $xlate;
+    $xlate     = {}; 
+    for my $field ( @fields ) {
+        my ( $from, $to ) = split(/\//, $field);
+        $xlate->{$from} = $to;
+    }
+}
 
 #
 # If everything is OK, translate file(s).
 #
-my $translator = SQL::Translator->new( debug => $verbose );
+my $translator  =  SQL::Translator->new( 
+    xlate       => $xlate || {},
+    debug       => $debug,
+    trace       => $trace,
+    no_comments => $no_comments,
+);
+
+if ( $list ) {
+    my @parsers   = $translator->list_parsers;
+    my @producers = $translator->list_producers;
+
+    for ( @parsers, @producers ) {
+        if ( $_ =~ m/.+::(\w+)\.pm/ ) {
+            $_ = $1;
+        }
+    }
+    
+    print "\nParsers:\n",   map { "\t$_\n" } sort @parsers;
+    print "\nProducers:\n", map { "\t$_\n" } sort @producers;
+    print "\n";
+    exit(0);
+}
+
+pod2usage(2) unless $from && $to && @files;
+
 $translator->parser($from);
 $translator->producer($to);
 
@@ -65,7 +103,6 @@ for my $file (@files) {
     my $output = $translator->translate( $file ) or die
         "Error: " . $translator->error;
     print $output;
-    warn "parser = ", Dumper( $translator->parser );
 }
 
 # ----------------------------------------------------
@@ -79,27 +116,42 @@ sql_translator.pl - convert an SQL database schema
 
 =head1 SYNOPSIS
 
+For help:
+
   ./sql_translator.pl -h|--help
 
-  ./sql_translator.pl -f|--from MySQL -t|--to Oracle [options] file
+For a list of all parsers and producers: 
+
+  ./sql_translator.pl -l|--list
+
+To translate a schema:
+
+  ./sql_translator.pl 
+        -f|--from|--parser MySQL 
+        -t|--to|--producer Oracle 
+        [options] 
+        file
 
   Options:
 
-    -v|--verbose   Print debug info to STDERR
-    --no-comments  Don't include comments in SQL output
+    -d|--debug                Print debug info
+    --trace                   Print parser trace info
+    --no-comments             Don't include comments in SQL output
+    --xlate=foo/bar,baz/blech Overrides for field translation
 
 =head1 DESCRIPTION
 
-Part of the SQL Fairy project (sqlfairy.sourceforge.net), this script
-will try to convert any database syntax for which it has a grammar
-into some other format it knows about.
+This script is part of the SQL Fairy project
+(http://sqlfairy.sourceforge.net/).  It will try to convert any
+database syntax for which it has a grammar into some other format it
+knows about.
 
 =head1 AUTHOR
 
-Ken Y. Clark, E<lt>kclark@logsoft.comE<gt>
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
 
 =head1 SEE ALSO
 
-perl(1), SQL::Translator.
+SQL::Translator.
 
 =cut
index 504544f..8994839 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator;
 
 # ----------------------------------------------------------------------
-# $Id: Translator.pm,v 1.11 2002-11-21 17:45:17 dlc Exp $
+# $Id: Translator.pm,v 1.12 2002-11-22 03:03:40 kycl4rk Exp $
 # ----------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -28,12 +28,20 @@ SQL::Translator - convert schema from one database to another
 =head1 SYNOPSIS
 
   use SQL::Translator;
-  my $translator = SQL::Translator->new;
+
+  my $translator = SQL::Translator->new(
+      xlate       => $xlate || {}, # Overrides for field translation
+      debug       => $debug,       # Print debug info
+      trace       => $trace,       # Print Parse::RecDescent trace
+      no_comments => $no_comments, # Don't include comments in output
+  );
+
   my $output     = $translator->translate(
       from       => "MySQL",
       to         => "Oracle",
       filename   => $file,
   ) or die $translator->error;
+
   print $output;
 
 =head1 DESCRIPTION
@@ -48,12 +56,13 @@ would use the PostgreSQL parser and the Oracle producer.
 =cut
 
 use strict;
-use vars qw( $VERSION $DEFAULT_SUB $DEBUG $ERROR );
+use vars qw( $VERSION $REVISION $DEFAULT_SUB $DEBUG $ERROR );
 use base 'Class::Base';
 
-$VERSION = sprintf "%d.%02d", q$Revision: 1.11 $ =~ /(\d+)\.(\d+)/;
-$DEBUG   = 0 unless defined $DEBUG;
-$ERROR   = "";
+$VERSION  = '0.01';
+$REVISION = sprintf "%d.%02d", q$Revision: 1.12 $ =~ /(\d+)\.(\d+)/;
+$DEBUG    = 0 unless defined $DEBUG;
+$ERROR    = "";
 
 use Carp qw(carp);
 
@@ -144,13 +153,57 @@ sub init {
         $self->data( $data );
     }
 
+    #
+    # Set various other options.
+    #
     $self->{'debug'} = defined $config->{'debug'} ? $config->{'debug'} : $DEBUG;
 
+    $self->trace( $config->{'trace'} );
+    
+    $self->custom_translate( $config->{'xlate'} );
+
+    $self->no_comments( $config->{'no_comments'} );
+
     return $self;
 }
 
 =head1 METHODS
 
+# ----------------------------------------------------------------------
+=head2 B<custom_translate>
+
+Allows the user to override default translation of fields.  For example,
+if a MySQL "text" field would normally be converted to a "long" for Oracle,
+the user could specify to change it to a "CLOB."  Accepts a hashref where
+keys are the "from" value and values are the "to," returns the current
+value of the field.
+
+=cut
+
+sub custom_translate {
+    my $self = shift;
+    $self->{'custom_translate'} = shift if @_;
+    return $self->{'custom_translate'} || {};
+}
+
+# ----------------------------------------------------------------------
+=head2 B<no_comments>
+
+Toggles whether to print comments in the output.  Accepts a true or false
+value, returns the current value.
+
+=cut
+
+sub no_comments {
+    my $self = shift;
+    my $arg  = shift;
+    if ( defined $arg ) {
+        $self->{'no_comments'} = $arg ? 1 : 0;
+    }
+    return $self->{'no_comments'} || 0;
+}
+
+# ----------------------------------------------------------------------
 =head2 B<producer>
 
 The B<producer> method is an accessor/mutator, used to retrieve or
@@ -310,6 +363,7 @@ sub producer_args {
     $self->{'producer_args'};
 }
 
+# ----------------------------------------------------------------------
 =head2 B<parser>
 
 The B<parser> method defines or retrieves a subroutine that will be
@@ -400,9 +454,10 @@ sub parser {
     return $self->{'parser'};
 }
 
+# ----------------------------------------------------------------------
 sub parser_type { $_[0]->{'parser_type'} }
 
-# parser_args
+# ----------------------------------------------------------------------
 sub parser_args {
     my $self = shift;
     if (@_) {
@@ -412,6 +467,7 @@ sub parser_args {
     $self->{'parser_args'};
 } 
 
+# ----------------------------------------------------------------------
 =head2 B<translate>
 
 The B<translate> method calls the subroutines referenced by the
@@ -458,6 +514,7 @@ You get the idea.
 
 =back
 
+# ----------------------------------------------------------------------
 =head2 B<filename>, B<data>
 
 Using the B<filename> method, the filename of the data to be parsed
@@ -505,6 +562,7 @@ sub filename {
     $self->{'filename'};
 }
 
+# ----------------------------------------------------------------------
 # data - get or set the data
 # if $self->{'data'} is not set, but $self->{'filename'} is, then
 # $self->{'filename'} is opened and read, whith the results put into
@@ -545,7 +603,25 @@ sub data {
     return $self->{'data'};
 }
 
-# translate
+# ----------------------------------------------------------------------
+=pod
+
+=head2 B<trace>
+
+Turns on/off the tracing option of Parse::RecDescent.
+
+=cut
+
+sub trace {
+    my $self = shift;
+    my $arg  = shift;
+    if ( defined $arg ) {
+        $self->{'trace'} = $arg ? 1 : 0;
+    }
+    return $self->{'trace'} || 0;
+}
+
+# ----------------------------------------------------------------------
 sub translate {
     my $self = shift;
     my ($args, $parser, $parser_type, $producer, $producer_type);
@@ -655,6 +731,7 @@ sub translate {
     return $producer_output;
 }
 
+# ----------------------------------------------------------------------
 sub list_producers {
     require SQL::Translator::Producer;
     my $path = catfile(dirname($INC{'SQL/Translator/Producer.pm'}), "Producer");
@@ -666,7 +743,7 @@ sub list_producers {
     return @available;
 }
 
-
+# ----------------------------------------------------------------------
 sub list_parsers {
     require SQL::Translator::Parser;
     my $path = catfile(dirname($INC{'SQL/Translator/Parser.pm'}), "Parser");
@@ -678,7 +755,7 @@ sub list_parsers {
     return @available;
 }
 
-
+# ----------------------------------------------------------------------
 sub load {
     my $module = do { my $m = shift; $m =~ s[::][/]g; "$m.pm" };
     return 1 if $INC{$module};
@@ -689,6 +766,7 @@ sub load {
     return 1;
 }
 
+# ----------------------------------------------------------------------
 sub isa { UNIVERSAL::isa($_[0], $_[1]) }
 
 1;
@@ -702,8 +780,9 @@ sub isa { UNIVERSAL::isa($_[0], $_[1]) }
 
 =head1 AUTHORS
 
-Ken Y. Clark, E<lt>kclark@logsoft.comE<gt>,
-darren chamberlain E<lt>darren@cpan.orgE<gt>
+Ken Y. Clark, E<lt>kclark@cpan.org<gt>,
+darren chamberlain E<lt>darren@cpan.orgE<gt>,
+Chris Mungall E<lt>cjm@fruitfly.orgE<gt>
 
 =head1 COPYRIGHT
 
index e86fe37..f3f3af6 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator::Parser;
 
 # ----------------------------------------------------------------------
-# $Id: Parser.pm,v 1.4 2002-11-20 04:03:03 kycl4rk Exp $
+# $Id: Parser.pm,v 1.5 2002-11-22 03:03:40 kycl4rk Exp $
 # ----------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -23,7 +23,7 @@ package SQL::Translator::Parser;
 
 use strict;
 use vars qw( $VERSION );
-$VERSION = sprintf "%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.5 $ =~ /(\d+)\.(\d+)/;
 
 sub parse { "" }
 
@@ -168,7 +168,7 @@ would be represented as:
 
 =head1 AUTHORS
 
-Ken Y. Clark, E<lt>kclark@logsoft.comE<gt>, 
+Ken Y. Clark, E<lt>kclark@cpan.org<gt>, 
 darren chamberlain E<lt>darren@cpan.orgE<gt>.
 
 =head1 SEE ALSO
index 547934b..62c3fd4 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator::Parser::MySQL;
 
 # -------------------------------------------------------------------
-# $Id: MySQL.pm,v 1.5 2002-11-20 04:03:04 kycl4rk Exp $
+# $Id: MySQL.pm,v 1.6 2002-11-22 03:03:40 kycl4rk Exp $
 # -------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -21,256 +21,325 @@ package SQL::Translator::Parser::MySQL;
 # 02111-1307  USA
 # -------------------------------------------------------------------
 
+=head1 NAME
+
+SQL::Translator::Parser::MySQL - parser for MySQL
+
+=head1 SYNOPSIS
+
+  use SQL::Translator;
+  use SQL::Translator::Parser::MySQL;
+
+  my $translator = SQL::Translator->new;
+  $translator->parser("SQL::Translator::Parser::MySQL");
+
+=head1 DESCRIPTION
+
+The grammar is influenced heavily by Tim Bunce's "mysql2ora" grammar.
+
+=cut
+
 use strict;
-use vars qw($VERSION $GRAMMAR @EXPORT_OK);
-$VERSION = sprintf "%d.%02d", q$Revision: 1.5 $ =~ /(\d+)\.(\d+)/;
+use vars qw[ $DEBUG $VERSION $GRAMMAR @EXPORT_OK ];
+$VERSION = sprintf "%d.%02d", q$Revision: 1.6 $ =~ /(\d+)\.(\d+)/;
+$DEBUG   = 1 unless defined $DEBUG;
 
-#use SQL::Translator::Parser;  # This is not necessary!
+use Data::Dumper;
 use Parse::RecDescent;
 use Exporter;
 use base qw(Exporter);
 
 @EXPORT_OK = qw(parse);
 
+# Enable warnings within the Parse::RecDescent module.
+$::RD_ERRORS = 1; # Make sure the parser dies when it encounters an error
+$::RD_WARN   = 1; # Enable warnings. This will warn on unused rules &c.
+$::RD_HINT   = 1; # Give out hints to help fix problems.
+
 my $parser; # should we do this?  There's no programmic way to 
             # change the grammar, so I think this is safe.
-sub parse {
-    my ( $translator, $data ) = @_;
-    $parser ||= Parse::RecDescent->new($GRAMMAR);
-
-    unless (defined $parser) {
-        return $translator->error("Error instantiating Parse::RecDescent ".
-            "instance: Bad grammer");
-    }
-
-    # Is this right?  It was $parser->parse before, but that didn't
-    # work; Parse::RecDescent appears to need the name of a rule
-    # with which to begin, so I chose the first rule in the grammar.
-    return $parser->file($data);
-}
 
-$GRAMMAR =
-    q!
-        { our ( %tables ) }
-
-        file         : statement(s) { \%tables }
-
-        statement    : comment
-                       | create
-                       | <error>
-
-        create       : create_table table_name '(' line(s /,/) ')' table_type(?) ';'
-                    { 
-                        my $i = 0;
-                        for my $line ( @{ $item[4] } ) {
-                            if ( $line->{'type'} eq 'field' ) {
-                                my $field_name = $line->{'name'};
-                                $tables{ $item{'table_name'} }
-                                    {'fields'}{$field_name} = 
-                                    { %$line, order => $i };
-                                $i++;
-                        
-                                if ( $line->{'is_primary_key'} ) {
-                                    push
-                                    @{ $tables{ $item{'table_name'} }{'indices'} },
-                                    {
-                                        type   => 'primary_key',
-                                        fields => [ $field_name ],
-                                    };
-                                }
-                            }
-                            else {
-                                push @{ $tables{ $item{'table_name'} }{'indices'} },
-                                    $line;
-                            }
-                            $tables{ $item{'table_name'} }{'type'} = 
-                                $item{'table_type'}[0];
+$GRAMMAR = q!
+
+{ our ( %tables, $table_order ) }
+
+startrule : statement(s) { \%tables }
+
+statement : comment
+    | create
+    | <error>
+
+create : create_table table_name '(' create_definition(s /,/) ')' table_option(s?) ';'
+    { 
+        my $table_name                       = $item{'table_name'};
+        $tables{ $table_name }{'order'}      = ++$table_order;
+        $tables{ $table_name }{'table_name'} = $table_name;
+
+        my $i = 0;
+        for my $definition ( @{ $item[4] } ) {
+            if ( $definition->{'type'} eq 'field' ) {
+                my $field_name = $definition->{'name'};
+                $tables{ $table_name }{'fields'}{ $field_name } = 
+                    { %$definition, order => $i };
+                $i++;
+        
+                if ( $definition->{'is_primary_key'} ) {
+                    push @{ $tables{ $table_name }{'indices'} },
+                        {
+                            type   => 'primary_key',
+                            fields => [ $field_name ],
                         }
-                    }
-                       | <error>
-
-        create       : create_index index_name /on/i table_name '(' field_name(s /,/) ')' ';'
-#        create       : create_index index_name keyword_on table_name '(' field_name ')' ';'
-                       {
-                         # do nothing just now
-                         my $dummy = 0;
-                       }
-                        | <error>
-
-        keyword_on   : /on/i
-
-        line         : index
-                       | field
-                       | <error>
-
-        comment      : /^\s*[#-]+.*\n/
-
-        blank        : /\s*/
-
-
-        field        : field_name data_type field_qualifier(s?)
-                       { 
-                          my %qualifier_h =  
-                            map {%$_} @{$item{'field_qualifier'} || []};
-                          my $null = defined $item{'not_null'}
-                            ? $item{'not_null'} : 1 ;
-                          delete $qualifier_h{'not_null'};
-                          $return = { 
-                                type           => 'field',
-                                name           => $item{'field_name'}, 
-                                data_type      => $item{'data_type'}{'type'},
-                               null           => $null,
-                               %qualifier_h,
-                           } 
-                       }
-                    | <error>
-
-        field_qualifier : not_null
-            { 
-                $return = { 
-                     null => $item{'not_null'},
-                } 
+                    ;
+                }
             }
-
-        field_qualifier : default_val
-            { 
-                $return = { 
-                     default => $item{default_val},
-                } 
+            else {
+                push @{ $tables{ $table_name }{'indices'} },
+                    $definition;
             }
+        }
 
-        field_qualifier : auto_inc
-            { 
-                $return = { 
-                     is_auto_inc => $item{auto_inc},
-                } 
+        for my $opt ( @{ $item{'table_option'} } ) {
+            if ( my ( $key, $val ) = each %$opt ) {
+                $tables{ $table_name }{'table_options'}{ $key } = $val;
             }
+        }
+    }
 
-        field_qualifier : primary_key
-            { 
-                $return = { 
-                     is_primary_key => $item{primary_key},
-                } 
+create : /CREATE/i unique(?) /(INDEX|KEY)/i index_name /on/i table_name '(' field_name(s /,/) ')' ';'
+    {
+        push @{ $tables{ $item{'table_name'} }{'indices'} },
+            {
+                name   => $item[4],
+                type   => $item[2] ? 'unique' : 'normal',
+                fields => $item[8],
             }
+        ;
+    }
 
-        field_qualifier : unsigned
-            { 
-                $return = { 
-                     is_unsigned => $item{unsigned},
-                } 
-            }
+create_definition : index
+    | field
+    | <error>
+
+comment : /^\s*(?:#|-{2}).*\n/
+
+blank : /\s*/
+
+field : field_name data_type field_qualifier(s?)
+    { 
+        my %qualifiers = map { %$_ } @{ $item{'field_qualifier'} || [] };
+        my $null = defined $item{'not_null'} ? $item{'not_null'} : 1;
+        delete $qualifiers{'not_null'};
+        if ( my @type_quals = @{ $item{'data_type'}{'qualifiers'} || [] } ) {
+            $qualifiers{ $_ } = 1 for @type_quals;
+        }
+
+        $return = { 
+            type           => 'field',
+            name           => $item{'field_name'}, 
+            data_type      => $item{'data_type'}{'type'},
+            size           => $item{'data_type'}{'size'},
+            list           => $item{'data_type'}{'list'},
+            null           => $null,
+            %qualifiers,
+        } 
+    }
+    | <error>
 
-        index        : primary_key_index
-                       | unique_index
-                       | normal_index
+field_qualifier : not_null
+    { 
+        $return = { 
+             null => $item{'not_null'},
+        } 
+    }
 
-        table_name   : WORD
+field_qualifier : default_val
+    { 
+        $return = { 
+             default => $item{'default_val'},
+        } 
+    }
 
-        field_name   : WORD
+field_qualifier : auto_inc
+    { 
+        $return = { 
+             is_auto_inc => $item{'auto_inc'},
+        } 
+    }
 
-        index_name   : WORD
+field_qualifier : primary_key
+    { 
+        $return = { 
+             is_primary_key => $item{'primary_key'},
+        } 
+    }
 
-        data_type    : WORD field_size(?) 
-            { 
-                $return = { 
-                    type => $item[1], 
-                    size => $item[2][0]
-                } 
-            }
+field_qualifier : unsigned
+    { 
+        $return = { 
+             is_unsigned => $item{'unsigned'},
+        } 
+    }
 
-        field_type   : WORD
+index : primary_key_index
+    | unique_index
+    | normal_index
+
+table_name   : WORD
+
+field_name   : WORD
+
+index_name   : WORD
+
+data_type    : WORD parens_value_list(s?) type_qualifier(s?)
+    { 
+        my $type = $item[1];
+        my $size; # field size, applicable only to non-set fields
+        my $list; # set list, applicable only to sets (duh)
+
+        if ( uc $type eq 'SET' ) {
+            $size = undef;
+            $list = $item[2][0];
+        }
+        else {
+            $size = $item[2][0];
+            $list = [];
+        }
+
+        $return        = { 
+            type       => $type,
+            size       => $size,
+            list       => $list,
+            qualifiers => $item[3],
+        } 
+    }
 
-        field_size   : '(' num_range ')' { $item{'num_range'} }
+parens_value_list : '(' VALUE(s /,/) ')'
+    { $item[2] }
 
-        num_range    : DIGITS ',' DIGITS
-            { $return = $item[1].','.$item[3] }
-                       | DIGITS
-            { $return = $item[1] }
+type_qualifier : /(BINARY|UNSIGNED|ZEROFILL)/i
+    { lc $item[1] }
 
+field_type   : WORD
 
-        create_table : /create/i /table/i
+field_size   : '(' num_range ')' { $item{'num_range'} }
 
-        create_index : /create/i /index/i
+num_range    : DIGITS ',' DIGITS
+    { $return = $item[1].','.$item[3] }
+    | DIGITS
+    { $return = $item[1] }
 
-        not_null     : /not/i /null/i { $return = 0 }
+create_table : /create/i /table/i
 
-        unsigned     : /unsigned/i { $return = 0 }
+create_index : /create/i /index/i
 
-        default_val  : /default/i /(?:')?[\w\d.-]*(?:')?/ { $item[2]=~s/'//g; $return=$item[2] }
+not_null     : /not/i /null/i { $return = 0 }
 
-        auto_inc     : /auto_increment/i { 1 }
+unsigned     : /unsigned/i { $return = 0 }
 
-        primary_key  : /primary/i /key/i { 1 }
+default_val  : /default/i /(?:')?[\w\d.-]*(?:')?/ 
+    { 
+        $item[2] =~ s/'//g; 
+        $return  =  $item[2];
+    }
 
-        primary_key_index : primary_key index_name(?) '(' field_name(s /,/) ')'
-            { 
-                $return = { 
-                    name   => $item{'index_name'}[0],
-                    type   => 'primary_key',
-                    fields => $item[4],
-                } 
-            }
+auto_inc : /auto_increment/i { 1 }
 
-        normal_index      : key index_name(?) '(' field_name(s /,/) ')'
-            { 
-                $return = { 
-                    name   => $item{'index_name'}[0],
-                    type   => 'normal',
-                    fields => $item[4],
-                } 
-            }
+primary_key : /primary/i /key/i { 1 }
 
-        unique_index      : /unique/i key(?) index_name(?) '(' field_name(s /,/) ')'
-            { 
-                $return = { 
-                    name   => $item{'index_name'}[0],
-                    type   => 'unique',
-                    fields => $item[5],
-                } 
-            }
+primary_key_index : primary_key index_name(?) '(' field_name(s /,/) ')'
+    { 
+        $return    = { 
+            name   => $item{'index_name'}[0],
+            type   => 'primary_key',
+            fields => $item[4],
+        } 
+    }
 
-        key          : /key/i 
-                       | /index/i
+normal_index : key index_name(?) '(' name_with_opt_paren(s /,/) ')'
+    { 
+        $return    = { 
+            name   => $item{'index_name'}[0],
+            type   => 'normal',
+            fields => $item[4],
+        } 
+    }
 
-        table_type   : /TYPE=/i /\w+/ { $item[2] }
+unique_index : unique key(?) index_name(?) '(' name_with_opt_paren(s /,/) ')'
+    { 
+        $return    = { 
+            name   => $item{'index_name'}[0],
+            type   => 'unique',
+            fields => $item[5],
+        } 
+    }
 
-        WORD         : /\w+/
+name_with_opt_paren : NAME parens_value_list(s?)
+    { $item[2][0] ? "$item[1]($item[2][0][0])" : $item[1] }
 
-        DIGITS       : /\d+/
+unique : /unique/i { 1 }
 
-        COMMA        : ','
+key : /key/i | /index/i
 
-    !;
+table_option : /[^\s;]+/ 
+    { 
+        $return = { split /=/, $item[1] }
+    }
 
-1;
+WORD : /\w+/
 
-#-----------------------------------------------------
-# Where man is not nature is barren.
-# William Blake
-#-----------------------------------------------------
+DIGITS : /\d+/
 
-=head1 NAME
+COMMA : ','
 
-SQL::Translator::Parser::MySQL - parser for MySQL
+NAME    : "`" /\w+/ "`"
+    { $item[2] }
+    | /\w+/
+    { $item[1] }
 
-=head1 SYNOPSIS
+VALUE   : /[-+]?\.?\d+(?:[eE]\d+)?/
+    { $item[1] }
+    | /'.*?'/   # XXX doesn't handle embedded quotes
+    { $item[1] }
+    | /NULL/
+    { 'NULL' }
 
-  use SQL::Translator;
-  use SQL::Translator::Parser::MySQL;
+!;
 
-  my $translator = SQL::Translator->new;
-  $translator->parser("SQL::Translator::Parser::MySQL");
+# -------------------------------------------------------------------
+sub parse {
+    my ( $translator, $data ) = @_;
+    $parser ||= Parse::RecDescent->new($GRAMMAR);
 
-=head1 DESCRIPTION
+    $::RD_TRACE  = $translator->trace ? 1 : undef;
+    $DEBUG       = $translator->debug;
+
+    unless (defined $parser) {
+        return $translator->error("Error instantiating Parse::RecDescent ".
+            "instance: Bad grammer");
+    }
+
+    my $result = $parser->startrule($data);
+    die "Parse failed.\n" unless defined $result;
+    warn Dumper($result) if $DEBUG;
+    return $result;
+}
+
+1;
+
+#-----------------------------------------------------
+# Where man is not nature is barren.
+# William Blake
+#-----------------------------------------------------
 
-Blah blah blah.
+=pod
 
 =head1 AUTHOR
 
-Ken Y. Clark, kclark@logsoft.com
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>,
+Chris Mungall
 
 =head1 SEE ALSO
 
-perl(1).
+perl(1), Parse::RecDescent.
 
 =cut
index 0407c8f..f0369a8 100644 (file)
@@ -1,13 +1,40 @@
 package SQL::Translator::Parser::Sybase;
 
-#-----------------------------------------------------
-# $Id: Sybase.pm,v 1.2 2002-11-20 04:03:04 kycl4rk Exp $
+# -------------------------------------------------------------------
+# $Id: Sybase.pm,v 1.3 2002-11-22 03:03:40 kycl4rk Exp $
+# -------------------------------------------------------------------
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
+#                    darren chamberlain <darren@cpan.org>
 #
-# File       : SQL/Translator/Parser/Sybase.pm
-# Programmer : Ken Y. Clark, kclark@logsoft.com
-# Created    : 2002/02/27
-# Purpose    : parser for Sybase (dbschema.pl)
-#-----------------------------------------------------
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307  USA
+# -------------------------------------------------------------------
+
+=head1 NAME
+
+SQL::Translator::Parser::Sybase - parser for Sybase
+
+=head1 SYNOPSIS
+
+  use SQL::Translator::Parser::Sybase;
+
+=head1 DESCRIPTION
+
+Parses the output of "dbschema.pl," a Perl script freely available from
+www.midsomer.org.
+
+=cut
 
 my $grammar = q{
 
@@ -212,21 +239,11 @@ my $grammar = q{
 # Ralph Waldo Emerson
 #-----------------------------------------------------
 
-=head1 NAME
-
-SQL::Translator::Parser::Sybase - parser for Sybase
-
-=head1 SYNOPSIS
-
-  use SQL::Translator::Parser::Sybase;
-
-=head1 DESCRIPTION
-
-Blah blah blah.
+=pod
 
 =head1 AUTHOR
 
-Ken Y. Clark, kclark@logsoft.com
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
 
 =head1 SEE ALSO
 
index 4488b70..f93d61b 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator::Parser::xSV;
 
 # -------------------------------------------------------------------
-# $Id: xSV.pm,v 1.2 2002-11-20 04:03:04 kycl4rk Exp $
+# $Id: xSV.pm,v 1.3 2002-11-22 03:03:40 kycl4rk Exp $
 # -------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -23,7 +23,7 @@ package SQL::Translator::Parser::xSV;
 
 use strict;
 use vars qw($VERSION @EXPORT);
-$VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/;
 
 use Exporter;
 use Text::ParseWords qw(quotewords);
index 2ef06f5..25cac69 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator::Producer;
 
 # -------------------------------------------------------------------
-# $Id: Producer.pm,v 1.3 2002-11-20 04:03:03 kycl4rk Exp $
+# $Id: Producer.pm,v 1.4 2002-11-22 03:03:40 kycl4rk Exp $
 # -------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -23,7 +23,7 @@ package SQL::Translator::Producer;
 
 use strict;
 use vars qw($VERSION);
-$VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/;
 
 sub produce { "" }
 
@@ -52,7 +52,7 @@ create statement.
 
 =head1 AUTHOR
 
-Ken Y. Clark, kclark@logsoft.com
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
 
 =head1 SEE ALSO
 
index ad308ea..a6b2e4f 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator::Producer::MySQL;
 
 # -------------------------------------------------------------------
-# $Id: MySQL.pm,v 1.3 2002-11-20 04:03:04 kycl4rk Exp $
+# $Id: MySQL.pm,v 1.4 2002-11-22 03:03:40 kycl4rk Exp $
 # -------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -22,9 +22,9 @@ package SQL::Translator::Producer::MySQL;
 # -------------------------------------------------------------------
 
 use strict;
-use vars qw($VERSION $DEBUG);
-$VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/;
-$DEBUG = 1 unless defined $DEBUG;
+use vars qw[ $VERSION $DEBUG ];
+$VERSION = sprintf "%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/;
+$DEBUG   = 1 unless defined $DEBUG;
 
 use Data::Dumper;
 
@@ -34,33 +34,35 @@ sub import {
 
 sub produce {
     my ($translator, $data) = @_;
+    $DEBUG                  = $translator->debug;
+    my $no_comments         = $translator->no_comments;
+
     debug("Beginning production\n");
-    my $create = sprintf 
-"# ----------------------------------------------------------------------
-# Created by %s
-# Created on %s
-# ----------------------------------------------------------------------\n\n",
-        __PACKAGE__, scalar localtime;
+
+    my $create; 
+    unless ( $no_comments ) {
+        $create .= sprintf "--\n-- Created by %s\n-- Created on %s\n--\n\n",
+            __PACKAGE__, scalar localtime;
+    }
 
     for my $table (keys %{$data}) {
         debug("Looking at table '$table'\n");
         my $table_data = $data->{$table};
-        my @fields = sort { $table_data->{'fields'}->{$a}->{'order'} <=>
-                            $table_data->{'fields'}->{$b}->{'order'}
-                          } keys %{$table_data->{'fields'}};
+        my @fields = sort { 
+            $table_data->{'fields'}->{$a}->{'order'} 
+            <=>
+            $table_data->{'fields'}->{$b}->{'order'}
+        } keys %{$table_data->{'fields'}};
 
-        # --------------------------------------------------------------
+        #
         # Header.  Should this look like what mysqldump produces?
-        # --------------------------------------------------------------
-        $create .=
-"# ----------------------------------------------------------------------
-# Table: $table
-# ----------------------------------------------------------------------\n";
+        #
+        $create .= "--\n-- Table: $table\n--\n" unless $no_comments;
         $create .= "CREATE TABLE $table (";
 
-        # --------------------------------------------------------------
+        #
         # Fields
-        # --------------------------------------------------------------
+        #
         for (my $i = 0; $i <= $#fields; $i++) {
             my $field = $fields[$i];
             debug("Looking at field '$field'\n");
@@ -69,17 +71,29 @@ sub produce {
             $create .= "\n";
 
             # data type and size
-            push @fdata, sprintf "%s%s", $field_data->{'data_type'},
-                                         ($field_data->{'size'}) ?
-                                        "($field_data->{'size'})" : "";
+            my $attr = uc $field_data->{'data_type'} eq 'SET' ? 'list' : 'size';
+            my @values = @{ $field_data->{ $attr } || [] };
+            push @fdata, sprintf "%s%s", 
+                $field_data->{'data_type'},
+                ( @values )
+                    ? '('.join(', ', @values).')'
+                    : '';
+
+            # MySQL qualifiers
+            for my $qual ( qw[ binary unsigned zerofill ] ) {
+                push @fdata, $qual 
+                    if $field_data->{ $qual } ||
+                       $field_data->{ uc $qual };
+            }
 
             # Null?
             push @fdata, "NOT NULL" unless $field_data->{'null'};
 
             # Default?  XXX Need better quoting!
-            if (my $default = $field_data->{'default'}) {
-                if (int $default eq "$default") {
-                    push @fdata, "DEFAULT $default";
+            my $default = $field_data->{'default'};
+            if ( defined $default ) {
+                if ( uc $default eq 'NULL') {
+                    push @fdata, "DEFAULT NULL";
                 } else {
                     push @fdata, "DEFAULT '$default'";
                 }
@@ -89,51 +103,50 @@ sub produce {
             push @fdata, "auto_increment" if $field_data->{'is_auto_inc'};
 
             # primary key?
-            push @fdata, "PRIMARY KEY" if $field_data->{'is_primary_key'};
+            # This is taken care of in the indices, could be duplicated here
+            # push @fdata, "PRIMARY KEY" if $field_data->{'is_primary_key'};
 
 
-            $create .= (join " ", @fdata);
+            $create .= (join " ", '', @fdata);
             $create .= "," unless ($i == $#fields);
         }
 
-        # --------------------------------------------------------------
-        # Other keys
-        # --------------------------------------------------------------
-        my @indices = @{$table_data->{'indices'}};
+        #
+        # Indices
+        #
+        my @index_creates;
+        my @indices = @{ $table_data->{'indices'} || [] };
         for (my $i = 0; $i <= $#indices; $i++) {
-            $create .= ",\n";
-            my $key = $indices[$i];
-            my ($name, $type, $fields) = @{$key}{qw(name type fields)};
-            if ($type eq "primary_key") {
-                $create .= " PRIMARY KEY (@{$fields})"
-            } else {
-                local $" = ", ";
-                $create .= " KEY $name (@{$fields})"
-            }
+            my $key  = $indices[$i];
+            my ($name, $type, $fields) = @{ $key }{ qw[ name type fields ] };
+            $name ||= '';
+            my $index_type = 
+                $type eq 'primary_key' ? 'PRIMARY KEY' :
+                $type eq 'unique'      ? 'UNIQUE KEY'  : 'KEY';
+            push @index_creates, 
+                "  $index_type $name (" . join( ', ', @$fields ) . ')';
         }
 
-        # --------------------------------------------------------------
+        if ( @index_creates ) {
+            $create .= join(",\n", '', @index_creates);
+        }
+
+        #
         # Footer
-        # --------------------------------------------------------------
+        #
         $create .= "\n)";
-        $create .= " TYPE=$table_data->{'type'}"
-            if defined $table_data->{'type'};
+        while ( my ( $key, $val ) = each %{ $table_data->{'table_options'} } ) {
+            $create .= " $key=$val" 
+        }
         $create .= ";\n\n";
     }
 
-    # Global footer (with a vim plug)
-    $create .= "#
-#
-# vim: set ft=sql:
-";
-
     return $create;
 }
 
-use Carp;
 sub debug {
     if ($DEBUG) {
-        map { carp "[" . __PACKAGE__ . "] $_" } @_;
+        map { warn "[" . __PACKAGE__ . "] $_" } @_;
     }
 }
 
index 1f0e586..30d0e42 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator::Producer::Oracle;
 
 # -------------------------------------------------------------------
-# $Id: Oracle.pm,v 1.3 2002-11-20 04:03:04 kycl4rk Exp $
+# $Id: Oracle.pm,v 1.4 2002-11-22 03:03:40 kycl4rk Exp $
 # -------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -23,13 +23,17 @@ package SQL::Translator::Producer::Oracle;
 
 
 use strict;
-use vars qw( $VERSION );
-$VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/;
+use vars qw[ $VERSION $DEBUG ];
+$VERSION = sprintf "%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/;
+$DEBUG   = 0 unless defined $DEBUG;
 
-my $max_identifier_length = 30;
+my $max_id_length = 30;
 my %used_identifiers = ();
 
 my %translate  = (
+    #
+    # MySQL types
+    #
     bigint     => 'number',
     double     => 'number',
     decimal    => 'number',
@@ -38,63 +42,133 @@ my %translate  = (
     mediumint  => 'number',
     smallint   => 'number',
     tinyint    => 'number',
-
     char       => 'char',
-
     varchar    => 'varchar2',
-
     tinyblob   => 'CLOB',
     blob       => 'CLOB',
     mediumblob => 'CLOB',
     longblob   => 'CLOB',
-
     longtext   => 'long',
     mediumtext => 'long',
     text       => 'long',
     tinytext   => 'long',
-
     enum       => 'varchar2',
     set        => 'varchar2',
-
     date       => 'date',
     datetime   => 'date',
     time       => 'date',
     timestamp  => 'date',
     year       => 'date',
+
+    #
+    # PostgreSQL types
+    #
+    smallint            => '',
+    integer             => '',
+    bigint              => '',
+    decimal             => '',
+    numeric             => '',
+    real                => '',
+    'double precision'  => '',
+    serial              => '',
+    bigserial           => '',
+    money               => '',
+    character           => '',
+    'character varying' => '',
+    bytea               => '',
+    interval            => '',
+    boolean             => '',
+    point               => '',
+    line                => '',
+    lseg                => '',
+    box                 => '',
+    path                => '',
+    polygon             => '',
+    circle              => '',
+    cidr                => '',
+    inet                => '',
+    macaddr             => '',
+    bit                 => '',
+    'bit varying'       => '',
+);
+
+#
+# Oracle reserved words from:
+# http://technet.oracle.com/docs/products/oracle8i/doc_library/\
+# 817_doc/server.817/a85397/ap_keywd.htm
+#
+my @ora_reserved = qw(
+    ACCESS ADD ALL ALTER AND ANY AS ASC AUDIT 
+    BETWEEN BY
+    CHAR CHECK CLUSTER COLUMN COMMENT COMPRESS CONNECT CREATE CURRENT
+    DATE DECIMAL DEFAULT DELETE DESC DISTINCT DROP
+    ELSE EXCLUSIVE EXISTS 
+    FILE FLOAT FOR FROM
+    GRANT GROUP 
+    HAVING
+    IDENTIFIED IMMEDIATE IN INCREMENT INDEX INITIAL INSERT
+    INTEGER INTERSECT INTO IS
+    LEVEL LIKE LOCK LONG 
+    MAXEXTENTS MINUS MLSLABEL MODE MODIFY 
+    NOAUDIT NOCOMPRESS NOT NOWAIT NULL NUMBER 
+    OF OFFLINE ON ONLINE OPTION OR ORDER
+    PCTFREE PRIOR PRIVILEGES PUBLIC
+    RAW RENAME RESOURCE REVOKE ROW ROWID ROWNUM ROWS
+    SELECT SESSION SET SHARE SIZE SMALLINT START 
+    SUCCESSFUL SYNONYM SYSDATE 
+    TABLE THEN TO TRIGGER 
+    UID UNION UNIQUE UPDATE USER
+    VALIDATE VALUES VARCHAR VARCHAR2 VIEW
+    WHENEVER WHERE WITH
 );
 
-# This is for testing only, and probably needs to be removed
-*translate = *produce;
+my %ora_reserved = map { $_ => 1 } @ora_reserved;
+my %global_names;
+my %unreserve;
+my %truncated;
 
 sub produce {
     my ( $translator, $data ) = @_;
+    $DEBUG                    = $translator->debug;
+    my $no_comments           = $translator->no_comments;
 
     #print "got ", scalar keys %$data, " tables:\n";
     #print join(', ', keys %$data), "\n";
     #print Dumper( $data );
 
-    #
-    # Output
-    #
-    my $output = sprintf "
-#
-# Created by %s, version %s
-# Datasource: %s
-#
+    my $output;
+    unless ( $no_comments ) {
+        $output .=  sprintf 
+            "--\n-- Created by %s\n-- Created on %s\n--\n\n",
+            __PACKAGE__, scalar localtime;
+    }
 
-", __PACKAGE__, $VERSION, $translator->parser_type;
+    if ( $translator->parser_type =~ /mysql/i ) {
+        $output .= 
+        "-- We assume that default NLS_DATE_FORMAT has been changed\n".
+        "-- but we set it here anyway to be self-consistent.\n".
+        "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';\n\n";
+    }
 
     #
     # Print create for each table
     #
     my ( $index_i, $trigger_i ) = ( 1, 1 );
-    for my $table_name ( sort keys %$data ) { 
-        check_identifier( $table_name );
+    for my $table ( 
+        # sort keys %$data 
+        map  { $_->[1] }
+        sort { $a->[0] <=> $b->[0] }
+        map  { [ $_->{'order'}, $_ ] }
+        values %{ $data }
+    ) { 
+        my $table_name = $table->{'table_name'};
+#        check_identifier( $table_name );
+        $table_name = mk_name( $table_name, '', undef, 1 );
+#        my $tablename_ur = unreserve($table_name);
 
         my ( @comments, @field_decs, @trigger_decs );
 
-        my $table = $data->{ $table_name };
-        push @comments, "#\n# Table: $table_name\n#";
+        push @comments, "--\n-- Table: $table_name\n--" unless $no_comments;
 
         for my $field ( 
             map  { $_->[1] }
@@ -115,7 +189,8 @@ sub produce {
                              $translate{ $data_type } :
                              die "Unknown datatype: $data_type\n";
                $field_str .= ' '.$data_type;
-               $field_str .= '('.$field->{'size'}.')' if defined $field->{'size'};
+               $field_str .= '('.join(',', @{ $field->{'size'} }).')' 
+                if @{ $field->{'size'} || [] };
 
             #
             # Default value
@@ -148,15 +223,15 @@ sub produce {
                     join( '_', 'autoinc', $field->{'name'}, $trigger_no );
 
                 push @trigger_decs, 
-                    'CREATE SEQUENCE ' . $trigger_sequence . ";" .
-                    'CREATE OR REPLACE TRIGGER ' . $trigger_name .
-                    ' BEFORE INSERT ON ' . $table_name .
-                    ' FOR EACH ROW WHEN (new.' . $field->{'name'} . ' is null) ' .
-                    ' BEGIN ' .
-                        ' SELECT ' . $trigger_sequence . '.nextval ' .
-                        ' INTO :new.' . $field->{'name'} .
+                    "CREATE SEQUENCE $trigger_sequence;\n" .
+                    "CREATE OR REPLACE TRIGGER $trigger_name\n" .
+                    "BEFORE INSERT ON $table_name\n" .
+                    "FOR EACH ROW WHEN (new.".$field->{'name'}." is null)\n".
+                    "BEGIN\n" .
+                        " SELECT $trigger_sequence.nextval\n" .
+                        " INTO :new." . $field->{'name'}."\n" .
                         " FROM dual;\n" .
-                    ' END ' . $trigger_name . ";/"
+                    " END  $trigger_name;/"
                 ;
             }
 
@@ -242,7 +317,7 @@ sub produce {
         );
     }
 
-    $output .= "#\n# End\n#\n";
+    return $output;
 }
 
 #
@@ -257,12 +332,12 @@ sub make_identifier {
 
     if ( 
         length( $identifier ) + $length_of_mutations >
-        $max_identifier_length
+        $max_id_length
     ) {
         $identifier = substr( 
             $identifier, 
             0, 
-            $max_identifier_length - $length_of_mutations
+            $max_id_length - $length_of_mutations
         );
     }
 
@@ -300,16 +375,63 @@ sub make_identifier {
 sub check_identifier {
     my $identifier = shift;
     die "Identifier '$identifier' is too long, unrecoverable error.\n"
-        if length( $identifier ) > $max_identifier_length;
+        if length( $identifier ) > $max_id_length;
     return $identifier;
 }
 
+# -------------------------------------------------------------------
+sub mk_name {
+    my ($basename, $type, $scope, $critical) = @_;
+    my $basename_orig = $basename;
+    my $max_name      = $max_id_length - (length($type) + 1);
+    $basename         = substr($basename, 0, $max_name) 
+                        if length($basename) > $max_name;
+    my $name          = $type ? "${type}_$basename" : $basename;
+
+    if ( $basename ne $basename_orig and $critical ) {
+        my $show_type = $type ? "+'$type'" : "";
+        warn "Truncating '$basename_orig'$show_type to $max_id_length ",
+            "character limit to make '$name'\n" if $DEBUG;
+        $truncated{$basename_orig} = $name;
+    }
+
+    $scope ||= \%global_names;
+    return $name unless $scope->{$name}++;
+    my $name_orig = $name;
+    $name .= "02";
+    substr($name, $max_id_length - 3) = "00" if length($name) > $max_id_length;
+    ++$name while $scope->{$name};
+    warn "The name '$name_orig' has been changed to ",
+         "'$name' to make it unique\n" if $DEBUG;
+    return $name;
+}
+
+# -------------------------------------------------------------------
+sub unreserve {
+    my ($name, $schema_obj_name) = @_;
+    my ($suffix) = ($name =~ s/(\W.*)$//) ? $1 : '';
+
+    # also trap fields that don't begin with a letter
+    return $_[0] if !$ora_reserved{uc $name}
+        && $name =~ /^[a-z]/i; 
+
+    if ( $schema_obj_name ) {
+        ++$unreserve{"$schema_obj_name.$name"};
+    }
+    else {
+        ++$unreserve{"$name (table name)"};
+    }
+
+    my $unreserve = sprintf '%s_', $name;
+    return $unreserve.$suffix;
+}
+
 1;
 
-#-----------------------------------------------------
+# -------------------------------------------------------------------
 # All bad art is the result of good intentions.
 # Oscar Wilde
-#-----------------------------------------------------
+# -------------------------------------------------------------------
 
 =head1 NAME
 
@@ -347,10 +469,14 @@ creating multiple constraint lines, that look like:
 This is a very preliminary finding, and needs to be investigated more
 thoroughly, of course.
 
+=head1 CREDITS
+
+A hearty "thank-you" to Tim Bunce for much of the logic stolen from 
+his "mysql2ora" script.
 
 =head1 AUTHOR
 
-Ken Y. Clark, kclark@logsoft.com
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
 
 =head1 SEE ALSO
 
index 792f0c5..4d147a5 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator::Producer::PostgreSQL;
 
 # -------------------------------------------------------------------
-# $Id: PostgreSQL.pm,v 1.1 2002-11-20 04:03:56 kycl4rk Exp $
+# $Id: PostgreSQL.pm,v 1.2 2002-11-22 03:03:40 kycl4rk Exp $
 # -------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -23,11 +23,47 @@ package SQL::Translator::Producer::PostgreSQL;
 
 use strict;
 use vars qw($VERSION $DEBUG);
-$VERSION = sprintf "%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;
 $DEBUG = 1 unless defined $DEBUG;
 
 use Data::Dumper;
 
+my %translate  = (
+    #
+    # MySQL types
+    #
+    bigint     => 'bigint',
+    double     => 'double precision',
+    decimal    => 'decimal',
+    float      => 'double precision',
+    int        => 'integer',
+    mediumint  => 'integer',
+    smallint   => 'smallint',
+    tinyint    => 'smallint',
+    char       => 'char',
+    varchar    => 'varchar',
+    longtext   => 'text',
+    mediumtext => 'text',
+    text       => 'text',
+    tinytext   => 'text',
+    tinyblob   => 'bytea',
+    blob       => 'bytea',
+    mediumblob => 'bytea',
+    longblob   => 'bytea',
+    enum       => 'varchar',
+    set        => 'varchar',
+    date       => 'date',
+    datetime   => 'timestamp',
+    time       => 'date',
+    timestamp  => 'timestamp',
+    year       => 'date',
+
+    #
+    # Oracle types
+    #
+);
+
+
 sub import {
     warn "loading " . __PACKAGE__ . "...\n";
 }
@@ -54,8 +90,7 @@ sub produce {
         # Fields
         #
         my @field_statements;
-        for ( my $i = 0; $i <= $#fields; $i++ ) {
-            my $field = $fields[$i];
+        for my $field ( @fields ) {
             debug("Looking at field '$field'\n");
             my $field_data = $table_data->{'fields'}->{ $field };
             my @fdata      = ("", $field);
@@ -70,7 +105,8 @@ sub produce {
             push @fdata, "NOT NULL" unless $field_data->{'null'};
 
             # Default?  XXX Need better quoting!
-            if (my $default = $field_data->{'default'}) {
+            my $default = $field_data->{'default'};
+            if ( defined $default ) {
                 push @fdata, "DEFAULT '$default'";
 #                if (int $default eq "$default") {
 #                    push @fdata, "DEFAULT $default";
@@ -93,7 +129,7 @@ sub produce {
         #
         # Other keys
         #
-        my @indices = @{ $table_data->{'indices'} };
+        my @indices = @{ $table_data->{'indices'} || [] };
         for ( my $i = 0; $i <= $#indices; $i++ ) {
             $create .= ",\n";
             my $key = $indices[$i];
@@ -118,7 +154,7 @@ sub produce {
 
 use Carp;
 sub debug {
-    if ($DEBUG) {
+    if ( $DEBUG ) {
         map { carp "[" . __PACKAGE__ . "] $_" } @_;
     }
 }
index c6645d9..07767e9 100644 (file)
@@ -1,17 +1,43 @@
 package SQL::Translator::Producer::XML;
 
-#-----------------------------------------------------
-# $Id: XML.pm,v 1.2 2002-03-21 18:50:53 dlc Exp $
+# -------------------------------------------------------------------
+# $Id: XML.pm,v 1.3 2002-11-22 03:03:40 kycl4rk Exp $
+# -------------------------------------------------------------------
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
+#                    darren chamberlain <darren@cpan.org>
 #
-# File       : SQL/Translator/Producer/XML.pm
-# Programmer : Ken Y. Clark, kclark@logsoft.com
-# Created    : 2002/02/27
-# Purpose    : XML output
-#-----------------------------------------------------
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+# 02111-1307  USA
+# -------------------------------------------------------------------
+
+=head1 NAME
+
+SQL::Translator::Producer::XML - XML output
+
+=head1 SYNOPSIS
+
+  use SQL::Translator::Producer::XML;
+
+=head1 DESCRIPTION
+
+Meant to create some sort of usable XML output.
+
+=cut
 
 use strict;
 use vars qw( $VERSION );
-$VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.3 $ =~ /(\d+)\.(\d+)/;
 
 use XML::Dumper;
 
@@ -22,32 +48,21 @@ sub produce {
 }
 
 1;
-#-----------------------------------------------------
+
+# -------------------------------------------------------------------
 # The eyes of fire, the nostrils of air,
 # The mouth of water, the beard of earth.
 # William Blake
-#-----------------------------------------------------
-__END__
-
-
-=head1 NAME
-
-SQL::Translator::Producer::XML - XML output
-
-=head1 SYNOPSIS
-
-  use SQL::Translator::Producer::XML;
-
-=head1 DESCRIPTION
+# -------------------------------------------------------------------
 
-Blah blah blah.
+=pod
 
 =head1 AUTHOR
 
-Ken Y. Clark, kclark@logsoft.com
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
 
 =head1 SEE ALSO
 
-perl(1).
+XML::Dumper;
 
 =cut
index 41a5935..75edb55 100644 (file)
@@ -1,9 +1,9 @@
 package SQL::Translator::Validator;
 
 # ----------------------------------------------------------------------
-# $Id: Validator.pm,v 1.4 2002-11-20 04:03:03 kycl4rk Exp $
+# $Id: Validator.pm,v 1.5 2002-11-22 03:03:40 kycl4rk Exp $
 # ----------------------------------------------------------------------
-# Copyright (C) 2002 Ken Y. Clark <kycl4rk@users.sourceforge.net>,
+# Copyright (C) 2002 Ken Y. Clark <kclark@cpan.org>,
 #                    darren chamberlain <darren@cpan.org>
 #
 # This program is free software; you can redistribute it and/or
@@ -23,7 +23,7 @@ package SQL::Translator::Validator;
 
 use strict;
 use vars qw($VERSION @EXPORT);
-$VERSION = sprintf "%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/;
+$VERSION = sprintf "%d.%02d", q$Revision: 1.5 $ =~ /(\d+)\.(\d+)/;
 
 use Exporter;
 use base qw(Exporter);