PageRenderTime 60ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/DBIx-Class-Schema-Loader-0.07025/lib/DBIx/Class/Schema/Loader/DBI/Sybase.pm

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