Upped version numbers, cleaned up code, fixed my name.
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / Sybase.pm
CommitLineData
16dc9970 1package SQL::Translator::Parser::Sybase;
2
d529894e 3# -------------------------------------------------------------------
478f608d 4# Copyright (C) 2002-2009 SQLFairy Authors
16dc9970 5#
d529894e 6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; version 2.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18# 02111-1307 USA
19# -------------------------------------------------------------------
20
21=head1 NAME
22
23SQL::Translator::Parser::Sybase - parser for Sybase
24
25=head1 SYNOPSIS
26
27 use SQL::Translator::Parser::Sybase;
28
29=head1 DESCRIPTION
30
0839c8a4 31Mostly parses the output of "dbschema.pl," a Perl script freely
32available from http://www.midsomer.org. The parsing is not complete,
33however, and you would probably have much better luck using the
34DBI-Sybase parser included with SQL::Translator.
d529894e 35
36=cut
16dc9970 37
d9656bd5 38use strict;
16dc9970 39
da06ac74 40use vars qw[ $DEBUG $VERSION $GRAMMAR @EXPORT_OK ];
ba506e52 41$VERSION = '1.60';
d9656bd5 42$DEBUG = 0 unless defined $DEBUG;
16dc9970 43
d9656bd5 44use Data::Dumper;
45use Parse::RecDescent;
46use Exporter;
47use base qw(Exporter);
16dc9970 48
d9656bd5 49@EXPORT_OK = qw(parse);
16dc9970 50
0839c8a4 51$::RD_ERRORS = 1;
52$::RD_WARN = 1;
53$::RD_HINT = 1;
16dc9970 54
d9656bd5 55$GRAMMAR = q{
16dc9970 56
d9656bd5 57{
56746e2a 58 my ( %tables, @table_comments, $table_order );
d9656bd5 59}
16dc9970 60
d9656bd5 61startrule : statement(s) eofile { \%tables }
16dc9970 62
d9656bd5 63eofile : /^\Z/
16dc9970 64
d9656bd5 65statement : create_table
66 | create_procedure
67 | create_index
68 | create_constraint
69 | comment
70 | use
71 | setuser
72 | if
73 | print
74 | grant
75 | exec
76 | <error>
16dc9970 77
d9656bd5 78use : /use/i WORD GO
79 { @table_comments = () }
16dc9970 80
d9656bd5 81setuser : /setuser/i NAME GO
82
83if : /if/i object_not_null begin if_command end GO
84
85if_command : grant
86 | create_index
87 | create_constraint
88
89object_not_null : /object_id/i '(' ident ')' /is not null/i
90
91print : /\s*/ /print/i /.*/
92
93else : /else/i /.*/
94
95begin : /begin/i
96
97end : /end/i
98
99grant : /grant/i /[^\n]*/
100
101exec : exec_statement(s) GO
102
103exec_statement : /exec/i /[^\n]+/
104
105comment : comment_start comment_middle comment_end
106 {
107 my $comment = $item[2];
108 $comment =~ s/^\s*|\s*$//mg;
109 $comment =~ s/^\**\s*//mg;
110 push @table_comments, $comment;
111 }
16dc9970 112
d9656bd5 113comment_start : /^\s*\/\*/
114
115comment_end : /\s*\*\//
116
117comment_middle : m{([^*]+|\*(?!/))*}
118
119#
120# Create table.
121#
122create_table : /create/i /table/i ident '(' create_def(s /,/) ')' lock(?) on_system(?) GO
123 {
124 my $table_owner = $item[3]{'owner'};
125 my $table_name = $item[3]{'name'};
126
127 if ( @table_comments ) {
128 $tables{ $table_name }{'comments'} = [ @table_comments ];
129 @table_comments = ();
16dc9970 130 }
131
56746e2a 132 $tables{ $table_name }{'order'} = ++$table_order;
d9656bd5 133 $tables{ $table_name }{'name'} = $table_name;
134 $tables{ $table_name }{'owner'} = $table_owner;
135 $tables{ $table_name }{'system'} = $item[7];
136
137 my $i = 0;
138 for my $def ( @{ $item[5] } ) {
139 if ( $def->{'supertype'} eq 'field' ) {
140 my $field_name = $def->{'name'};
141 $tables{ $table_name }{'fields'}{ $field_name } =
142 { %$def, order => $i };
143 $i++;
144
145 if ( $def->{'is_primary_key'} ) {
146 push @{ $tables{ $table_name }{'constraints'} }, {
147 type => 'primary_key',
148 fields => [ $field_name ],
149 };
150 }
151 }
152 elsif ( $def->{'supertype'} eq 'constraint' ) {
153 push @{ $tables{ $table_name }{'constraints'} }, $def;
154 }
155 else {
156 push @{ $tables{ $table_name }{'indices'} }, $def;
157 }
16dc9970 158 }
d9656bd5 159 }
160
161create_constraint : /create/i constraint
162 {
163 @table_comments = ();
164 push @{ $tables{ $item[2]{'table'} }{'constraints'} }, $item[2];
165 }
166
167create_index : /create/i index
168 {
169 @table_comments = ();
170 push @{ $tables{ $item[2]{'table'} }{'indices'} }, $item[2];
171 }
172
173create_procedure : /create/i /procedure/i procedure_body GO
174 {
175 @table_comments = ();
176 }
177
178procedure_body : not_go(s)
179
180not_go : /((?!go).)*/
181
182create_def : field
183 | index
184 | constraint
185
186blank : /\s*/
187
188field : field_name data_type nullable(?)
189 {
190 $return = {
191 supertype => 'field',
192 name => $item{'field_name'},
193 data_type => $item{'data_type'}{'type'},
194 size => $item{'data_type'}{'size'},
195 nullable => $item[3][0],
196# default => $item{'default_val'}[0],
197# is_auto_inc => $item{'auto_inc'}[0],
198# is_primary_key => $item{'primary_key'}[0],
199 }
200 }
201
202constraint : primary_key_constraint
203 | unique_constraint
204
205field_name : WORD
206
207index_name : WORD
208
209table_name : WORD
210
211data_type : WORD field_size(?)
212 {
213 $return = {
214 type => $item[1],
215 size => $item[2][0]
216 }
217 }
218
219lock : /lock/i /datarows/i
220
221field_type : WORD
222
223field_size : '(' num_range ')' { $item{'num_range'} }
224
225num_range : DIGITS ',' DIGITS
226 { $return = $item[1].','.$item[3] }
227 | DIGITS
228 { $return = $item[1] }
229
16dc9970 230
d9656bd5 231nullable : /not/i /null/i
232 { $return = 0 }
233 | /null/i
234 { $return = 1 }
16dc9970 235
d9656bd5 236default_val : /default/i /(?:')?[\w\d.-]*(?:')?/
237 { $item[2]=~s/'//g; $return=$item[2] }
16dc9970 238
d9656bd5 239auto_inc : /auto_increment/i { 1 }
16dc9970 240
d9656bd5 241primary_key_constraint : /primary/i /key/i index_name(?) parens_field_list
242 {
243 $return = {
244 supertype => 'constraint',
245 name => $item{'index_name'}[0],
246 type => 'primary_key',
247 fields => $item[4],
248 }
249 }
16dc9970 250
d9656bd5 251unique_constraint : /unique/i clustered(?) INDEX(?) index_name(?) on_table(?) parens_field_list
252 {
253 $return = {
254 supertype => 'constraint',
255 type => 'unique',
256 clustered => $item[2][0],
257 name => $item[4][0],
258 table => $item[5][0],
259 fields => $item[6],
260 }
261 }
262
263clustered : /clustered/i
264 { $return = 1 }
265 | /nonclustered/i
266 { $return = 0 }
267
268INDEX : /index/i
269
270on_table : /on/i table_name
271 { $return = $item[2] }
272
273on_system : /on/i /system/i
274 { $return = 1 }
275
276index : clustered(?) INDEX index_name(?) on_table(?) parens_field_list
277 {
278 $return = {
279 supertype => 'index',
280 type => 'normal',
281 clustered => $item[1][0],
282 name => $item[3][0],
283 table => $item[4][0],
284 fields => $item[5],
285 }
286 }
287
288parens_field_list : '(' field_name(s /,/) ')'
289 { $item[2] }
290
291ident : QUOTE(?) WORD '.' WORD QUOTE(?)
292 { $return = { owner => $item[2], name => $item[4] } }
293 | WORD
294 { $return = { name => $item[2] } }
295
296GO : /^go/i
297
298NAME : QUOTE(?) /\w+/ QUOTE(?)
299 { $item[2] }
300
301WORD : /[\w#]+/
302
303DIGITS : /\d+/
304
305COMMA : ','
306
307QUOTE : /'/
16dc9970 308
309};
310
d9656bd5 311# -------------------------------------------------------------------
312sub parse {
313 my ( $translator, $data ) = @_;
314 my $parser = Parse::RecDescent->new($GRAMMAR);
315
316 local $::RD_TRACE = $translator->trace ? 1 : undef;
317 local $DEBUG = $translator->debug;
318
319 unless (defined $parser) {
320 return $translator->error("Error instantiating Parse::RecDescent ".
321 "instance: Bad grammer");
322 }
323
324 my $result = $parser->startrule($data);
325 return $translator->error( "Parse failed." ) unless defined $result;
326 warn Dumper( $result ) if $DEBUG;
327
328 my $schema = $translator->schema;
329 my @tables = sort {
330 $result->{ $a }->{'order'} <=> $result->{ $b }->{'order'}
331 } keys %{ $result };
332
333 for my $table_name ( @tables ) {
334 my $tdata = $result->{ $table_name };
335 my $table = $schema->add_table( name => $tdata->{'name'} )
336 or die "Can't create table '$table_name': ", $schema->error;
337
338 $table->comments( $tdata->{'comments'} );
339
340 my @fields = sort {
341 $tdata->{'fields'}->{$a}->{'order'}
342 <=>
343 $tdata->{'fields'}->{$b}->{'order'}
344 } keys %{ $tdata->{'fields'} };
345
346 for my $fname ( @fields ) {
347 my $fdata = $tdata->{'fields'}{ $fname };
348 my $field = $table->add_field(
349 name => $fdata->{'name'},
350 data_type => $fdata->{'data_type'},
351 size => $fdata->{'size'},
352 default_value => $fdata->{'default'},
353 is_auto_increment => $fdata->{'is_auto_inc'},
354 is_nullable => $fdata->{'nullable'},
355 comments => $fdata->{'comments'},
356 ) or die $table->error;
357
358 $table->primary_key( $field->name ) if $fdata->{'is_primary_key'};
359
360 for my $qual ( qw[ binary unsigned zerofill list ] ) {
361 if ( my $val = $fdata->{ $qual } || $fdata->{ uc $qual } ) {
362 next if ref $val eq 'ARRAY' && !@$val;
363 $field->extra( $qual, $val );
364 }
365 }
366
367 if ( $field->data_type =~ /(set|enum)/i && !$field->size ) {
368 my %extra = $field->extra;
369 my $longest = 0;
370 for my $len ( map { length } @{ $extra{'list'} || [] } ) {
371 $longest = $len if $len > $longest;
372 }
373 $field->size( $longest ) if $longest;
374 }
375
376 for my $cdata ( @{ $fdata->{'constraints'} } ) {
377 next unless $cdata->{'type'} eq 'foreign_key';
378 $cdata->{'fields'} ||= [ $field->name ];
379 push @{ $tdata->{'constraints'} }, $cdata;
380 }
381 }
382
383 for my $idata ( @{ $tdata->{'indices'} || [] } ) {
384 my $index = $table->add_index(
385 name => $idata->{'name'},
386 type => uc $idata->{'type'},
387 fields => $idata->{'fields'},
388 ) or die $table->error;
389 }
390
391 for my $cdata ( @{ $tdata->{'constraints'} || [] } ) {
392 my $constraint = $table->add_constraint(
393 name => $cdata->{'name'},
394 type => $cdata->{'type'},
395 fields => $cdata->{'fields'},
396 reference_table => $cdata->{'reference_table'},
397 reference_fields => $cdata->{'reference_fields'},
398 match_type => $cdata->{'match_type'} || '',
100684f3 399 on_delete => $cdata->{'on_delete'} || $cdata->{'on_delete_do'},
400 on_update => $cdata->{'on_update'} || $cdata->{'on_update_do'},
d9656bd5 401 ) or die $table->error;
402 }
403 }
404
405 return 1;
406}
407
16dc9970 4081;
409
d9656bd5 410# -------------------------------------------------------------------
16dc9970 411# Every hero becomes a bore at last.
412# Ralph Waldo Emerson
d9656bd5 413# -------------------------------------------------------------------
16dc9970 414
d529894e 415=pod
16dc9970 416
417=head1 AUTHOR
418
ba506e52 419Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
16dc9970 420
421=head1 SEE ALSO
422
0839c8a4 423SQL::Translator, SQL::Translator::Parser::DBI, L<http://www.midsomer.org/>.
16dc9970 424
425=cut