• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.9.5/CPANInternal-140/DBIx-Class-Schema-Loader-0.07033/lib/DBIx/Class/Schema/Loader/DBI/
1package DBIx::Class::Schema::Loader::DBI::Sybase;
2
3use strict;
4use warnings;
5use base 'DBIx::Class::Schema::Loader::DBI::Sybase::Common';
6use mro 'c3';
7use List::MoreUtils 'any';
8use namespace::clean;
9
10use DBIx::Class::Schema::Loader::Table::Sybase ();
11
12our $VERSION = '0.07033';
13
14=head1 NAME
15
16DBIx::Class::Schema::Loader::DBI::Sybase - DBIx::Class::Schema::Loader::DBI
17Sybase ASE Implementation.
18
19=head1 DESCRIPTION
20
21See L<DBIx::Class::Schema::Loader> and L<DBIx::Class::Schema::Loader::Base>.
22
23This class reblesses into the L<DBIx::Class::Schema::Loader::DBI::Sybase::Microsoft_SQL_Server> class for connections to MSSQL.
24
25=cut
26
27sub _rebless {
28    my $self = shift;
29
30    my $dbh = $self->schema->storage->dbh;
31    my $DBMS_VERSION = @{$dbh->selectrow_arrayref(qq{sp_server_info \@attribute_id=1})}[2];
32    if ($DBMS_VERSION =~ /^Microsoft /i) {
33        $DBMS_VERSION =~ s/\s/_/g;
34        my $subclass = "DBIx::Class::Schema::Loader::DBI::Sybase::$DBMS_VERSION";
35        if ($self->load_optional_class($subclass) && !$self->isa($subclass)) {
36            bless $self, $subclass;
37            $self->_rebless;
38      }
39    }
40}
41
42sub _system_databases {
43    return (qw/
44        master model sybsystemdb sybsystemprocs tempdb
45    /);
46}
47
48sub _system_tables {
49    return (qw/
50        sysquerymetrics
51    /);
52}
53
54sub _setup {
55    my $self = shift;
56
57    $self->next::method(@_);
58
59    $self->preserve_case(1);
60
61    my ($current_db) = $self->dbh->selectrow_array('SELECT db_name()');
62
63    if (ref $self->db_schema eq 'HASH') {
64        if (keys %{ $self->db_schema } < 2) {
65            my ($db) = keys %{ $self->db_schema };
66
67            $db ||= $current_db;
68
69            if ($db eq '%') {
70                my $owners = $self->db_schema->{$db};
71
72                my $db_names = $self->dbh->selectcol_arrayref(<<'EOF');
73SELECT name
74FROM master.dbo.sysdatabases
75EOF
76
77                my @dbs;
78
79                foreach my $db_name (@$db_names) {
80                    push @dbs, $db_name
81                        unless any { $_ eq $db_name } $self->_system_databases;
82                }
83
84                $self->db_schema({});
85
86                DB: foreach my $db (@dbs) {
87                    if (not ((ref $owners eq 'ARRAY' && $owners->[0] eq '%') || $owners eq '%')) {
88                        my @owners;
89
90                        foreach my $owner (@$owners) {
91                            push @owners, $owner
92                                if defined $self->_uid($db, $owner);
93                        }
94
95                        next DB unless @owners;
96
97                        $self->db_schema->{$db} = \@owners;
98                    }
99                    else {
100                        # for post-processing below
101                        $self->db_schema->{$db} = '%';
102                    }
103                }
104
105                $self->qualify_objects(1);
106            }
107            else {
108                if ($db ne $current_db) {
109                    $self->dbh->do("USE [$db]");
110
111                    $self->qualify_objects(1);
112                }
113            }
114        }
115        else {
116            $self->qualify_objects(1);
117        }
118    }
119    elsif (ref $self->db_schema eq 'ARRAY' || (not defined $self->db_schema)) {
120        my $owners = $self->db_schema;
121        $owners ||= [ $self->dbh->selectrow_array('SELECT user_name()') ];
122
123        $self->qualify_objects(1) if @$owners > 1;
124
125        $self->db_schema({ $current_db => $owners });
126    }
127
128    foreach my $db (keys %{ $self->db_schema }) {
129        if ($self->db_schema->{$db} eq '%') {
130            my $owners = $self->dbh->selectcol_arrayref(<<"EOF");
131SELECT name
132FROM [$db].dbo.sysusers
133WHERE uid <> gid
134EOF
135            $self->db_schema->{$db} = $owners;
136
137            $self->qualify_objects(1);
138        }
139    }
140}
141
142sub _tables_list {
143    my ($self, $opts) = @_;
144
145    my @tables;
146
147    while (my ($db, $owners) = each %{ $self->db_schema }) {
148        foreach my $owner (@$owners) {
149            my ($uid) = $self->_uid($db, $owner);
150
151            my $table_names = $self->dbh->selectcol_arrayref(<<"EOF");
152SELECT name
153FROM [$db].dbo.sysobjects
154WHERE uid = $uid
155    AND type IN ('U', 'V')
156EOF
157
158            TABLE: foreach my $table_name (@$table_names) {
159                next TABLE if any { $_ eq $table_name } $self->_system_tables;
160
161                push @tables, DBIx::Class::Schema::Loader::Table::Sybase->new(
162                    loader   => $self,
163                    name     => $table_name,
164                    database => $db,
165                    schema   => $owner,
166                );
167            }
168        }
169    }
170
171    return $self->_filter_tables(\@tables, $opts);
172}
173
174sub _uid {
175    my ($self, $db, $owner) = @_;
176
177    my ($uid) = $self->dbh->selectrow_array(<<"EOF");
178SELECT uid
179FROM [$db].dbo.sysusers
180WHERE name = @{[ $self->dbh->quote($owner) ]}
181EOF
182
183    return $uid;
184}
185
186sub _table_columns {
187    my ($self, $table) = @_;
188
189    my $db    = $table->database;
190    my $owner = $table->schema;
191
192    my $columns = $self->dbh->selectcol_arrayref(<<"EOF");
193SELECT c.name
194FROM [$db].dbo.syscolumns c
195JOIN [$db].dbo.sysobjects o
196    ON c.id = o.id
197WHERE o.name = @{[ $self->dbh->quote($table->name) ]}
198    AND o.type IN ('U', 'V')
199    AND o.uid  = @{[ $self->_uid($db, $owner) ]}
200ORDER BY c.colid ASC
201EOF
202
203    return $columns;
204}
205
206sub _table_pk_info {
207    my ($self, $table) = @_;
208
209    my ($current_db) = $self->dbh->selectrow_array('SELECT db_name()');
210
211    my $db = $table->database;
212
213    $self->dbh->do("USE [$db]");
214
215    local $self->dbh->{FetchHashKeyName} = 'NAME_lc';
216
217    my $sth = $self->dbh->prepare(<<"EOF");
218sp_pkeys @{[ $self->dbh->quote($table->name) ]},
219    @{[ $self->dbh->quote($table->schema) ]},
220    @{[ $self->dbh->quote($db) ]}
221EOF
222    $sth->execute;
223
224    my @keydata;
225
226    while (my $row = $sth->fetchrow_hashref) {
227        push @keydata, $row->{column_name};
228    }
229
230    $self->dbh->do("USE [$current_db]");
231
232    return \@keydata;
233}
234
235sub _table_fk_info {
236    my ($self, $table) = @_;
237
238    my $db    = $table->database;
239    my $owner = $table->schema;
240
241    my $sth = $self->dbh->prepare(<<"EOF");
242SELECT sr.reftabid, sd2.name, sr.keycnt,
243    fokey1,  fokey2,   fokey3,   fokey4,   fokey5,   fokey6,   fokey7,   fokey8,
244    fokey9,  fokey10,  fokey11,  fokey12,  fokey13,  fokey14,  fokey15,  fokey16,
245    refkey1, refkey2,  refkey3,  refkey4,  refkey5,  refkey6,  refkey7,  refkey8,
246    refkey9, refkey10, refkey11, refkey12, refkey13, refkey14, refkey15, refkey16
247FROM [$db].dbo.sysreferences sr
248JOIN [$db].dbo.sysobjects so1
249    ON sr.tableid = so1.id
250JOIN [$db].dbo.sysusers su1
251    ON so1.uid = su1.uid
252JOIN master.dbo.sysdatabases sd2
253    ON sr.pmrydbid = sd2.dbid
254WHERE so1.name = @{[ $self->dbh->quote($table->name) ]}
255    AND su1.name = @{[ $self->dbh->quote($table->schema) ]}
256EOF
257    $sth->execute;
258
259    my @rels;
260
261    REL: while (my @rel = $sth->fetchrow_array) {
262        my ($remote_tab_id, $remote_db, $key_cnt) = splice @rel, 0, 3;
263
264        my ($remote_tab_owner, $remote_tab_name) =
265            $self->dbh->selectrow_array(<<"EOF");
266SELECT su.name, so.name
267FROM [$remote_db].dbo.sysusers su
268JOIN [$remote_db].dbo.sysobjects so
269    ON su.uid = so.uid
270WHERE so.id = $remote_tab_id
271EOF
272
273        next REL
274            unless any { $_ eq $remote_tab_owner }
275                @{ $self->db_schema->{$remote_db} || [] };
276
277        my @local_col_ids  = splice @rel, 0, 16;
278        my @remote_col_ids = splice @rel, 0, 16;
279
280        @local_col_ids  = splice @local_col_ids,  0, $key_cnt;
281        @remote_col_ids = splice @remote_col_ids, 0, $key_cnt;
282
283        my $remote_table = DBIx::Class::Schema::Loader::Table::Sybase->new(
284            loader   => $self,
285            name     => $remote_tab_name,
286            database => $remote_db,
287            schema   => $remote_tab_owner,
288        );
289
290        my $all_local_cols  = $self->_table_columns($table);
291        my $all_remote_cols = $self->_table_columns($remote_table);
292
293        my @local_cols  = map $all_local_cols->[$_-1],  @local_col_ids;
294        my @remote_cols = map $all_remote_cols->[$_-1], @remote_col_ids;
295
296        next REL if    (any { not defined $_ } @local_cols)
297                    || (any { not defined $_ } @remote_cols);
298
299        push @rels, {
300            local_columns  => \@local_cols,
301            remote_table   => $remote_table,
302            remote_columns => \@remote_cols,
303        };
304    };
305
306    return \@rels;
307}
308
309sub _table_uniq_info {
310    my ($self, $table) = @_;
311
312    my $db    = $table->database;
313    my $owner = $table->schema;
314    my $uid   = $self->_uid($db, $owner);
315
316    my ($current_db) = $self->dbh->selectrow_array('SELECT db_name()');
317
318    $self->dbh->do("USE [$db]");
319
320    my $sth = $self->dbh->prepare(<<"EOF");
321SELECT si.name, si.indid, si.keycnt
322FROM [$db].dbo.sysindexes si
323JOIN [$db].dbo.sysobjects so
324    ON si.id = so.id
325WHERE so.name = @{[ $self->dbh->quote($table->name) ]}
326    AND so.uid = $uid
327    AND si.indid > 0
328    AND si.status & 2048 <> 2048
329    AND si.status2 & 2 = 2
330EOF
331    $sth->execute;
332
333    my %uniqs;
334
335    while (my ($ind_name, $ind_id, $key_cnt) = $sth->fetchrow_array) {
336        COLS: foreach my $col_idx (1 .. ($key_cnt+1)) {
337            my ($next_col) = $self->dbh->selectrow_array(<<"EOF");
338SELECT index_col(
339    @{[ $self->dbh->quote($table->name) ]},
340    $ind_id, $col_idx, $uid
341)
342EOF
343            last COLS unless defined $next_col;
344
345            push @{ $uniqs{$ind_name} }, $next_col;
346        }
347    }
348
349    my @uniqs = map { [ $_ => $uniqs{$_} ] } keys %uniqs;
350
351    $self->dbh->do("USE [$current_db]");
352
353    return \@uniqs;
354}
355
356sub _columns_info_for {
357    my $self    = shift;
358    my ($table) = @_;
359    my $result  = $self->next::method(@_);
360
361    my $db    = $table->database;
362    my $owner = $table->schema;
363    my $uid   = $self->_uid($db, $owner);
364
365    local $self->dbh->{FetchHashKeyName} = 'NAME_lc';
366    my $sth = $self->dbh->prepare(<<"EOF");
367SELECT c.name, bt.name base_type, ut.name user_type, c.prec prec, c.scale scale, c.length len, c.cdefault dflt_id, c.computedcol comp_id, (c.status & 0x80) is_id
368FROM [$db].dbo.syscolumns c
369LEFT JOIN [$db].dbo.sysobjects o  ON c.id       = o.id
370LEFT JOIN [$db].dbo.systypes   bt ON c.type     = bt.type
371LEFT JOIN [$db].dbo.systypes   ut ON c.usertype = ut.usertype
372WHERE o.name = @{[ $self->dbh->quote($table) ]}
373    AND o.uid = $uid
374    AND o.type IN ('U', 'V')
375EOF
376    $sth->execute;
377    my $info = $sth->fetchall_hashref('name');
378
379    while (my ($col, $res) = each %$result) {
380        $res->{data_type} = $info->{$col}{user_type} || $info->{$col}{base_type};
381
382        if ($info->{$col}{is_id}) {
383            $res->{is_auto_increment} = 1;
384        }
385        $sth->finish;
386
387        # column has default value
388        if (my $default_id = $info->{$col}{dflt_id}) {
389            my $sth = $self->dbh->prepare(<<"EOF");
390SELECT cm.id, cm.text
391FROM [$db].dbo.syscomments cm
392WHERE cm.id = $default_id
393EOF
394            $sth->execute;
395
396            if (my ($d_id, $default) = $sth->fetchrow_array) {
397                my $constant_default = ($default =~ /^DEFAULT \s+ (\S.*\S)/ix)
398                    ? $1
399                    : $default;
400
401                $constant_default = substr($constant_default, 1, length($constant_default) - 2)
402                    if (   substr($constant_default, 0, 1) =~ m{['"\[]}
403                        && substr($constant_default, -1)   =~ m{['"\]]});
404
405                $res->{default_value} = $constant_default;
406            }
407        }
408
409        # column is a computed value
410        if (my $comp_id = $info->{$col}{comp_id}) {
411            my $sth = $self->dbh->prepare(<<"EOF");
412SELECT cm.id, cm.text
413FROM [$db].dbo.syscomments cm
414WHERE cm.id = $comp_id
415EOF
416            $sth->execute;
417            if (my ($c_id, $comp) = $sth->fetchrow_array) {
418                my $function = ($comp =~ /^AS \s+ (\S+)/ix) ? $1 : $comp;
419                $res->{default_value} = \$function;
420
421                if ($function =~ /^getdate\b/) {
422                    $res->{inflate_datetime} = 1;
423                }
424
425                delete $res->{size};
426                $res->{data_type} = undef;
427            }
428        }
429
430        if (my $data_type = $res->{data_type}) {
431            if ($data_type eq 'int') {
432                $data_type = $res->{data_type} = 'integer';
433            }
434            elsif ($data_type eq 'decimal') {
435                $data_type = $res->{data_type} = 'numeric';
436            }
437            elsif ($data_type eq 'float') {
438                $data_type = $res->{data_type}
439                    = ($info->{$col}{len} <= 4 ? 'real' : 'double precision');
440            }
441
442            if ($data_type eq 'timestamp') {
443                $res->{inflate_datetime} = 0;
444            }
445
446            if ($data_type =~ /^(?:text|unitext|image|bigint|integer|smallint|tinyint|real|double|double precision|float|date|time|datetime|smalldatetime|money|smallmoney|timestamp|bit)\z/i) {
447                delete $res->{size};
448            }
449            elsif ($data_type eq 'numeric') {
450                my ($prec, $scale) = @{$info->{$col}}{qw/prec scale/};
451
452                if (!defined $prec && !defined $scale) {
453                    $data_type = $res->{data_type} = 'integer';
454                    delete $res->{size};
455                }
456                elsif ($prec == 18 && $scale == 0) {
457                    delete $res->{size};
458                }
459                else {
460                    $res->{size} = [ $prec, $scale ];
461                }
462            }
463            elsif ($data_type =~ /char/) {
464                $res->{size} = $info->{$col}{len};
465
466                if ($data_type =~ /^(?:unichar|univarchar)\z/i) {
467                    $res->{size} /= 2;
468                }
469                elsif ($data_type =~ /^n(?:var)?char\z/i) {
470                    my ($nchar_size) = $self->dbh->selectrow_array('SELECT @@ncharsize');
471
472                    $res->{size} /= $nchar_size;
473                }
474            }
475        }
476    }
477
478    return $result;
479}
480
481=head1 SEE ALSO
482
483L<DBIx::Class::Schema::Loader::DBI::Sybase::Common>,
484L<DBIx::Class::Schema::Loader>, L<DBIx::Class::Schema::Loader::Base>,
485L<DBIx::Class::Schema::Loader::DBI>
486
487=head1 AUTHOR
488
489See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
490
491=head1 LICENSE
492
493This library is free software; you can redistribute it and/or modify it under
494the same terms as Perl itself.
495
496=cut
497
4981;
499# vim:et sts=4 sw=4 tw=0:
500