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