remove commented copyright
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / DBI / Sybase.pm
1 package SQL::Translator::Parser::DBI::Sybase;
2
3 =head1 NAME
4
5 SQL::Translator::Parser::DBI::Sybase - parser for DBD::Sybase
6
7 =head1 SYNOPSIS
8
9 See SQL::Translator::Parser::DBI.
10
11 =head1 DESCRIPTION
12
13 Uses DBI Catalog Methods.
14
15 =cut
16
17 use strict;
18 use DBI;
19 use SQL::Translator::Schema;
20 use Data::Dumper;
21
22 use vars qw[ $DEBUG $VERSION @EXPORT_OK ];
23 $VERSION = '1.59';
24 $DEBUG   = 0 unless defined $DEBUG;
25
26 no strict 'refs';
27
28 sub parse {
29     my ( $tr, $dbh ) = @_;
30
31     if ($dbh->{FetchHashKeyName} ne 'NAME_uc') {
32         warn "setting dbh attribute {FetchHashKeyName} to NAME_uc";
33         $dbh->{FetchHashKeyName} = 'NAME_uc';
34     }
35
36     if ($dbh->{ChopBlanks} != 1) {
37         warn "setting dbh attribute {ChopBlanks} to 1";
38         $dbh->{ChopBlanks} = 1;
39     }
40
41     my $schema = $tr->schema;
42
43     my ($sth, @tables, $columns);
44     my $stuff;
45
46     ### Columns
47
48     # it is much quicker to slurp back everything all at once rather
49     # than make repeated calls
50
51     $sth = $dbh->column_info(undef, undef, undef, undef);
52
53
54     foreach my $c (@{$sth->fetchall_arrayref({})}) {
55         $columns
56             ->{$c->{TABLE_CAT}}
57                 ->{$c->{TABLE_SCHEM}}
58                     ->{$c->{TABLE_NAME}}
59                         ->{columns}
60                             ->{$c->{COLUMN_NAME}}= $c;
61     }
62
63     ### Tables and views
64
65     # Get a list of the tables and views.
66     $sth = $dbh->table_info();
67     @tables   = @{$sth->fetchall_arrayref({})};
68
69     my $h = $dbh->selectall_arrayref(q{
70 SELECT o.name, colid,colid2,c.text
71   FROM syscomments c
72   JOIN sysobjects o
73     ON c.id = o.id
74  WHERE o.type ='V'
75 ORDER BY o.name,
76          c.colid,
77          c.colid2
78 }
79 );
80
81     # View text
82     # I had always thought there was something 'hard' about
83     # reconstructing text from syscomments ..
84     # this seems to work fine and is certainly not complicated!
85
86     foreach (@{$h}) {
87         $stuff->{view}->{$_->[0]}->{text} .= $_->[3];
88     }
89
90     #### objects with indexes.
91     map {
92         $stuff->{indexes}->{$_->[0]}++
93             if defined;
94     } @{$dbh->selectall_arrayref("SELECT DISTINCT object_name(id) AS name
95                                     FROM sysindexes
96                                    WHERE indid > 0")};
97
98     ## slurp objects
99     map {
100         $stuff->{$_->[1]}->{$_->[0]} = $_;
101     } @{$dbh->selectall_arrayref("SELECT name,type, id FROM sysobjects")};
102
103
104     ### Procedures
105
106     # This gets legitimate procedures by used the 'supported' API: sp_stored_procedures
107     map {
108         my $n = $_->{PROCEDURE_NAME};
109         $n =~ s/;\d+$//;        # Ignore versions for now
110         $_->{name} = $n;
111         $stuff->{procedures}->{$n} = $_;
112     } values %{$dbh->selectall_hashref("sp_stored_procedures", 'PROCEDURE_NAME')};
113
114
115     # And this blasts in the text of 'legit' stored procedures.  Do
116     # this rather than calling sp_helptext in a loop.
117
118     $h = $dbh->selectall_arrayref(q{
119 SELECT o.name, colid,colid2,c.text
120   FROM syscomments c
121   JOIN sysobjects o
122     ON c.id = o.id
123  WHERE o.type ='P'
124 ORDER BY o.name,
125          c.colid,
126          c.colid2
127 }
128 );
129
130     foreach (@{$h}) {
131         $stuff->{procedures}->{$_->[0]}->{text} .= $_->[3]
132             if (defined($stuff->{procedures}->{$_->[0]}));
133     }
134
135     ### Defaults
136     ### Rules
137     ### Bind Defaults
138     ### Bind Rules
139
140     ### Triggers
141     # Since the 'target' of the trigger is defined in the text, we will
142     # just create them independently for now rather than associating them
143     # with a table.
144
145     $h = $dbh->selectall_arrayref(q{
146 SELECT o.name, colid,colid2,c.text
147   FROM syscomments c
148   JOIN sysobjects o
149     ON c.id = o.id
150   JOIN sysobjects o1
151     ON (o.id = o1.instrig OR o.id = o1.deltrig or o.id = o1.updtrig)
152  WHERE o.type ='TR'
153 ORDER BY o.name,
154          c.colid,
155          c.colid2
156 }
157 );
158     foreach (@{$h}) {
159         $stuff->{triggers}->{$_->[0]}->{text} .= $_->[3];
160     }
161
162     ### References
163     ### Keys
164
165     ### Types
166     # Not sure what to do with these?
167     $stuff->{type_info_all} = $dbh->type_info_all;
168
169     ### Tables
170     # According to the DBI docs, these can be
171
172     # "TABLE"
173     # "VIEW"
174     # "SYSTEM TABLE"
175     # "GLOBAL TEMPORARY",
176     # "LOCAL TEMPORARY"
177     # "ALIAS"
178     # "SYNONYM"
179
180     foreach my $table_info (@tables) {
181         next
182             unless (defined($table_info->{TABLE_TYPE}));
183
184         if ($table_info->{TABLE_TYPE} =~ /TABLE/) {
185             my $table = $schema->add_table(
186                                            name =>
187 $table_info->{TABLE_NAME},
188                                            type =>
189 $table_info->{TABLE_TYPE},
190                                           ) || die $schema->error;
191
192             # find the associated columns
193
194             my $cols =
195                 $columns->{$table_info->{TABLE_QUALIFIER}}
196                     ->{$table_info->{TABLE_OWNER}}
197                         ->{$table_info->{TABLE_NAME}}
198                             ->{columns};
199
200             foreach my $c (values %{$cols}) {
201                 my $f = $table->add_field(
202                                           name        => $c->{COLUMN_NAME},
203                                           data_type   => $c->{TYPE_NAME},
204                                           order       => $c->{ORDINAL_POSITION},
205                                           size        => $c->{COLUMN_SIZE},
206                                          ) || die $table->error;
207
208                 $f->is_nullable(1)
209                     if ($c->{NULLABLE} == 1);
210             }
211
212             # add in primary key
213             my $h = $dbh->selectall_hashref("sp_pkeys
214 $table_info->{TABLE_NAME}", 'COLUMN_NAME');
215             if (scalar keys %{$h} > 1) {
216                 my @c = map {
217                     $_->{COLUMN_NAME}
218                 } sort {
219                     $a->{KEY_SEQ} <=> $b->{KEY_SEQ}
220                 } values %{$h};
221
222                 $table->primary_key(@c)
223                     if (scalar @c);
224             }
225
226             # add in any indexes ... how do we tell if the index has
227             # already been created as part of a primary key or other
228             # constraint?
229
230             if (defined($stuff->{indexes}->{$table_info->{TABLE_NAME}})){
231                 my $h = $dbh->selectall_hashref("sp_helpindex
232 $table_info->{TABLE_NAME}", 'INDEX_NAME');
233                 foreach (values %{$h}) {
234                     my $fields = $_->{'INDEX_KEYS'};
235                     $fields =~ s/\s*//g;
236                     my $i = $table->add_index(
237                                               name   =>
238 $_->{INDEX_NAME},
239                                               fields => $fields,
240                                              );
241                     if ($_->{'INDEX_DESCRIPTION'} =~ /unique/i) {
242                         $i->type('unique');
243
244                         # we could make this a primary key if there
245                         # isn't already one defined and if there
246                         # aren't any nullable columns in thisindex.
247
248                         if (!defined($table->primary_key())) {
249                             $table->primary_key($fields)
250                                 unless grep {
251                                     $table->get_field($_)->is_nullable()
252                                 } split(/,\s*/, $fields);
253                         }
254                     }
255                 }
256             }
257         } elsif ($table_info->{TABLE_TYPE} eq 'VIEW') {
258             my $view =  $schema->add_view(
259                                           name =>
260 $table_info->{TABLE_NAME},
261                                           );
262
263
264             my $cols =
265                 $columns->{$table_info->{TABLE_QUALIFIER}}
266                     ->{$table_info->{TABLE_OWNER}}
267                         ->{$table_info->{TABLE_NAME}}
268                             ->{columns};
269
270             $view->fields(map {
271                 $_->{COLUMN_NAME}
272             } sort {
273                 $a->{ORDINAL_POSITION} <=> $b->{ORDINAL_POSITION}
274                 } values %{$cols}
275                          );
276
277             $view->sql($stuff->{view}->{$table_info->{TABLE_NAME}}->{text})
278                 if (defined($stuff->{view}->{$table_info->{TABLE_NAME}}->{text}));
279         }
280     }
281
282     foreach my $p (values %{$stuff->{procedures}}) {
283         my $proc = $schema->add_procedure(
284                                name      => $p->{name},
285                                owner     => $p->{PROCEDURE_OWNER},
286                                comments  => $p->{REMARKS},
287                                sql       => $p->{text},
288                                );
289
290     }
291
292     ### Permissions
293     ### Groups
294     ### Users
295     ### Aliases
296     ### Logins
297     return 1;
298 }
299
300 1;
301
302 =pod
303
304 =head1 AUTHOR
305
306 Paul Harrington E<lt>harringp@deshaw.comE<gt>.
307
308 =head1 SEE ALSO
309
310 DBI, DBD::Sybase, SQL::Translator::Schema.
311
312 =cut