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