Commit | Line | Data |
16dc9970 |
1 | package SQL::Translator; |
2 | |
3 | #----------------------------------------------------- |
dfb4c915 |
4 | # $Id: Translator.pm,v 1.3 2002-03-07 14:11:40 dlc Exp $ |
1fd8c91f |
5 | # |
dfb4c915 |
6 | # File : SQL/Translator.pm |
7 | # Programmer : Ken Y. Clark, kclark@logsoft.com |
8 | # Created : 2002/02/27 |
9 | # Purpose : convert schema from one database to another |
10 | #----------------------------------------------------- |
16dc9970 |
11 | |
12 | use strict; |
dfb4c915 |
13 | use vars qw( $VERSION ); |
14 | $VERSION = (qw$Revision: 1.3 $)[-1]; |
16dc9970 |
15 | |
dfb4c915 |
16 | use Data::Dumper; |
16dc9970 |
17 | |
dfb4c915 |
18 | use SQL::Translator::Parser::MySQL; |
19 | use SQL::Translator::Parser::Sybase; |
20 | use SQL::Translator::Producer::Oracle; |
21 | use SQL::Translator::Producer::XML; |
16dc9970 |
22 | |
dfb4c915 |
23 | # |
24 | # These are the inputs we can parse. |
25 | # |
26 | my %parsers = ( |
27 | mysql => 'MySQL', |
28 | sybase => 'Sybase', |
29 | ); |
1fd8c91f |
30 | |
dfb4c915 |
31 | # |
32 | # These are the formats we can produce. |
33 | # |
34 | my %producers = ( |
35 | oracle => 'Oracle', |
36 | xml => 'XML', |
37 | ); |
16dc9970 |
38 | |
dfb4c915 |
39 | #----------------------------------------------------- |
16dc9970 |
40 | sub new { |
dfb4c915 |
41 | # |
42 | # Makes a new object. Intentionally made very bare as |
43 | # it is used by all subclasses (unless they override, |
44 | # of course). |
45 | # |
16dc9970 |
46 | my $class = shift; |
dfb4c915 |
47 | my %args = @_; |
48 | my $self = { %args }; |
49 | return bless $self, $class; |
16dc9970 |
50 | } |
1fd8c91f |
51 | |
dfb4c915 |
52 | #----------------------------------------------------- |
53 | sub error { |
54 | # |
55 | # Return the last error. |
56 | # |
57 | return shift()->{'error'} || ''; |
58 | } |
1fd8c91f |
59 | |
dfb4c915 |
60 | #----------------------------------------------------- |
61 | sub error_out { |
62 | # |
63 | # Record the error and return undef. |
64 | # |
1fd8c91f |
65 | my $self = shift; |
dfb4c915 |
66 | if ( my $error = shift ) { |
67 | $self->{'error'} = $error; |
1fd8c91f |
68 | } |
dfb4c915 |
69 | return; |
16dc9970 |
70 | } |
1fd8c91f |
71 | |
dfb4c915 |
72 | #----------------------------------------------------- |
16dc9970 |
73 | sub translate { |
dfb4c915 |
74 | # |
75 | # Translates any number of given files. |
76 | # |
77 | my ( $self, %args ) = @_; |
78 | my $from = $args{'from'} || ''; |
79 | my $to = $args{'to'} || ''; |
80 | my $input = $args{'input'} || []; |
81 | my $verbose = $args{'verbose'} || 0; |
82 | my $no_comments = $args{'no_comments'} || 0; |
83 | |
84 | if ( exists $parsers{ $from } ) { |
85 | $self->{'from'} = $from; |
86 | warn "Using parser '$from.'\n" if $verbose; |
16dc9970 |
87 | } |
88 | else { |
dfb4c915 |
89 | my $msg = "The parsers '$from' is not valid.\n" . |
90 | "Please choose from the following list:\n"; |
91 | $msg .= " $_\n" for sort keys %parsers; |
92 | return $self->error_out( $msg ); |
16dc9970 |
93 | } |
94 | |
dfb4c915 |
95 | if ( exists $producers{ $to } ) { |
96 | $self->{'to'} = $to; |
97 | warn "Using producer '$to.'\n" if $verbose; |
16dc9970 |
98 | } |
dfb4c915 |
99 | else { |
100 | my $msg = "The producer '$to' is not valid.\n" . |
101 | "Please choose from the following list:\n"; |
102 | $msg .= " $_\n" for sort keys %producers; |
103 | return $self->error_out( $msg ); |
16dc9970 |
104 | } |
105 | |
106 | # |
dfb4c915 |
107 | # Slurp the entire text file we're parsing. |
16dc9970 |
108 | # |
dfb4c915 |
109 | my $parser = $self->parser; |
110 | my $producer = $self->producer; |
111 | my $data; |
112 | for my $file ( @$input ) { |
113 | warn "Parsing file '$file.'\n" if $verbose; |
114 | open my $fh, $file or return $self->error_out( "Can't read $file: $!" ); |
115 | local $/; |
116 | $data = $parser->parse( <$fh> ); |
16dc9970 |
117 | } |
118 | |
dfb4c915 |
119 | warn "Data =\n", Dumper( $data ) if $verbose; |
120 | my $output = $producer->translate( $data ); |
16dc9970 |
121 | } |
122 | |
123 | #----------------------------------------------------- |
dfb4c915 |
124 | sub parser { |
16dc9970 |
125 | # |
dfb4c915 |
126 | # Figures out which module to load based on the "from" argument |
16dc9970 |
127 | # |
128 | my $self = shift; |
dfb4c915 |
129 | unless ( $self->{'parser'} ) { |
130 | my $parser_module = |
131 | 'SQL::Translator::Parser::'.$parsers{ $self->{'from'} }; |
132 | $self->{'parser'} = $parser_module->new; |
16dc9970 |
133 | } |
dfb4c915 |
134 | return $self->{'parser'}; |
16dc9970 |
135 | } |
1fd8c91f |
136 | |
dfb4c915 |
137 | #----------------------------------------------------- |
138 | sub producer { |
139 | # |
140 | # Figures out which module to load based on the "to" argument |
141 | # |
16dc9970 |
142 | my $self = shift; |
dfb4c915 |
143 | unless ( $self->{'producer'} ) { |
144 | my $from = $parsers{ $self->{'from'} }; |
145 | my $producer_module = |
146 | 'SQL::Translator::Producer::'.$producers{ $self->{'to'} }; |
147 | $self->{'producer'} = $producer_module->new( from => $from ); |
148 | } |
149 | return $self->{'producer'}; |
1fd8c91f |
150 | } |
16dc9970 |
151 | |
152 | 1; |
153 | |
154 | #----------------------------------------------------- |
155 | # Rescue the drowning and tie your shoestrings. |
156 | # Henry David Thoreau |
157 | #----------------------------------------------------- |
158 | |
dfb4c915 |
159 | =head1 NAME |
16dc9970 |
160 | |
dfb4c915 |
161 | SQL::Translator - convert schema from one database to another |
16dc9970 |
162 | |
dfb4c915 |
163 | =head1 SYNOPSIS |
16dc9970 |
164 | |
dfb4c915 |
165 | use SQL::Translator; |
166 | my $translator = SQL::Translator->new; |
167 | my $output = $translator->translate( |
168 | from => 'mysql', |
169 | to => 'oracle', |
170 | file => $file, |
171 | ) or die $translator->error; |
172 | print $output; |
173 | |
174 | =head1 DESCRIPTION |
16dc9970 |
175 | |
dfb4c915 |
176 | This module attempts to simplify the task of converting one database |
177 | create syntax to another through the use of Parsers and Producers. |
178 | The idea is that any Parser can be used with any Producer in the |
179 | conversion process. So, if you wanted PostgreSQL-to-Oracle, you could |
180 | just write the PostgreSQL parser and use an existing Oracle producer. |
181 | |
182 | Currently, the existing parsers use Parse::RecDescent, and the |
183 | producers are just printing formatted output of the parsed data |
184 | structure. New parsers don't necessarily have to use |
185 | Parse::RecDescent, however, as long as the data structure conforms to |
186 | what the producers are expecting. With this separation of code, it is |
187 | hoped that developers will find it easy to add more database dialects |
188 | by using what's written, writing only what they need, and then |
189 | contributing their parsers or producers back to the project. |
190 | |
191 | =head1 AUTHOR |
16dc9970 |
192 | |
dfb4c915 |
193 | Ken Y. Clark, kclark@logsoft.com |
16dc9970 |
194 | |
195 | =head1 SEE ALSO |
196 | |
dfb4c915 |
197 | perl(1). |
16dc9970 |
198 | |
199 | =cut |