#!/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>
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);
my $output = $translator->translate( $file ) or die
"Error: " . $translator->error;
print $output;
- warn "parser = ", Dumper( $translator->parser );
}
# ----------------------------------------------------
=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
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
=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
=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);
$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
$self->{'producer_args'};
}
+# ----------------------------------------------------------------------
=head2 B<parser>
The B<parser> method defines or retrieves a subroutine that will be
return $self->{'parser'};
}
+# ----------------------------------------------------------------------
sub parser_type { $_[0]->{'parser_type'} }
-# parser_args
+# ----------------------------------------------------------------------
sub parser_args {
my $self = shift;
if (@_) {
$self->{'parser_args'};
}
+# ----------------------------------------------------------------------
=head2 B<translate>
The B<translate> method calls the subroutines referenced by the
=back
+# ----------------------------------------------------------------------
=head2 B<filename>, B<data>
Using the B<filename> method, the filename of the data to be parsed
$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
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);
return $producer_output;
}
+# ----------------------------------------------------------------------
sub list_producers {
require SQL::Translator::Producer;
my $path = catfile(dirname($INC{'SQL/Translator/Producer.pm'}), "Producer");
return @available;
}
-
+# ----------------------------------------------------------------------
sub list_parsers {
require SQL::Translator::Parser;
my $path = catfile(dirname($INC{'SQL/Translator/Parser.pm'}), "Parser");
return @available;
}
-
+# ----------------------------------------------------------------------
sub load {
my $module = do { my $m = shift; $m =~ s[::][/]g; "$m.pm" };
return 1 if $INC{$module};
return 1;
}
+# ----------------------------------------------------------------------
sub isa { UNIVERSAL::isa($_[0], $_[1]) }
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
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
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 { "" }
=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
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
# 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
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{
# 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
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
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);
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
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 { "" }
=head1 AUTHOR
-Ken Y. Clark, kclark@logsoft.com
+Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
=head1 SEE ALSO
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
# -------------------------------------------------------------------
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;
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");
$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'";
}
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__ . "] $_" } @_;
}
}
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
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',
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] }
$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
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;/"
;
}
);
}
- $output .= "#\n# End\n#\n";
+ return $output;
}
#
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
);
}
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
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
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
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";
}
# 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);
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";
#
# 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];
use Carp;
sub debug {
- if ($DEBUG) {
+ if ( $DEBUG ) {
map { carp "[" . __PACKAGE__ . "] $_" } @_;
}
}
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;
}
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
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
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);