PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

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

#
Perl | 513 lines | 388 code | 110 blank | 15 comment | 59 complexity | 73a89970203a3a62854168c4d5c9ac17 MD5 | raw file
  1. package DBIx::Class::Schema::Loader::DBI::Informix;
  2. use strict;
  3. use warnings;
  4. use base qw/DBIx::Class::Schema::Loader::DBI/;
  5. use mro 'c3';
  6. use Scalar::Util 'looks_like_number';
  7. use List::MoreUtils 'any';
  8. use Try::Tiny;
  9. use namespace::clean;
  10. use DBIx::Class::Schema::Loader::Table::Informix ();
  11. our $VERSION = '0.07025';
  12. =head1 NAME
  13. DBIx::Class::Schema::Loader::DBI::Informix - DBIx::Class::Schema::Loader::DBI
  14. Informix Implementation.
  15. =head1 DESCRIPTION
  16. See L<DBIx::Class::Schema::Loader> and L<DBIx::Class::Schema::Loader::Base>.
  17. =cut
  18. sub _build_name_sep { '.' }
  19. sub _system_databases {
  20. return (qw/
  21. sysmaster sysutils sysuser sysadmin
  22. /);
  23. }
  24. sub _current_db {
  25. my $self = shift;
  26. my ($current_db) = $self->dbh->selectrow_array(<<'EOF');
  27. SELECT rtrim(ODB_DBName)
  28. FROM sysmaster:informix.SysOpenDB
  29. WHERE ODB_SessionID = (
  30. SELECT DBINFO('sessionid')
  31. FROM informix.SysTables
  32. WHERE TabID = 1
  33. ) and ODB_IsCurrent = 'Y'
  34. EOF
  35. return $current_db;
  36. }
  37. sub _owners {
  38. my ($self, $db) = @_;
  39. my ($owners) = $self->dbh->selectcol_arrayref(<<"EOF");
  40. SELECT distinct(rtrim(owner))
  41. FROM ${db}:informix.systables
  42. EOF
  43. my @owners = grep $_ && $_ ne 'informix' && !/^\d/, @$owners;
  44. return @owners;
  45. }
  46. sub _setup {
  47. my $self = shift;
  48. $self->next::method(@_);
  49. if (not defined $self->preserve_case) {
  50. $self->preserve_case(0);
  51. }
  52. elsif ($self->preserve_case) {
  53. $self->schema->storage->sql_maker->quote_char('"');
  54. $self->schema->storage->sql_maker->name_sep('.');
  55. }
  56. my $current_db = $self->_current_db;
  57. if (ref $self->db_schema eq 'HASH') {
  58. if (keys %{ $self->db_schema } < 2) {
  59. my ($db) = keys %{ $self->db_schema };
  60. $db ||= $current_db;
  61. if ($db eq '%') {
  62. my $owners = $self->db_schema->{$db};
  63. my $db_names = $self->dbh->selectcol_arrayref(<<'EOF');
  64. SELECT rtrim(name)
  65. FROM sysmaster:sysdatabases
  66. EOF
  67. my @dbs;
  68. foreach my $db_name (@$db_names) {
  69. push @dbs, $db_name
  70. unless any { $_ eq $db_name } $self->_system_databases;
  71. }
  72. $self->db_schema({});
  73. DB: foreach my $db (@dbs) {
  74. if (not ((ref $owners eq 'ARRAY' && $owners->[0] eq '%') || $owners eq '%')) {
  75. my @owners;
  76. my @db_owners = try {
  77. $self->_owners($db);
  78. }
  79. catch {
  80. if (/without logging/) {
  81. warn
  82. "Database '$db' is unreferencable due to lack of logging.\n";
  83. }
  84. return ();
  85. };
  86. foreach my $owner (@$owners) {
  87. push @owners, $owner
  88. if any { $_ eq $owner } @db_owners;
  89. }
  90. next DB unless @owners;
  91. $self->db_schema->{$db} = \@owners;
  92. }
  93. else {
  94. # for post-processing below
  95. $self->db_schema->{$db} = '%';
  96. }
  97. }
  98. $self->qualify_objects(1);
  99. }
  100. else {
  101. if ($db ne $current_db) {
  102. $self->qualify_objects(1);
  103. }
  104. }
  105. }
  106. else {
  107. $self->qualify_objects(1);
  108. }
  109. }
  110. elsif (ref $self->db_schema eq 'ARRAY' || (not defined $self->db_schema)) {
  111. my $owners = $self->db_schema;
  112. $owners ||= [ $self->dbh->selectrow_array(<<'EOF') ];
  113. SELECT rtrim(username)
  114. FROM sysmaster:syssessions
  115. WHERE sid = DBINFO('sessionid')
  116. EOF
  117. $self->qualify_objects(1) if @$owners > 1;
  118. $self->db_schema({ $current_db => $owners });
  119. }
  120. DB: foreach my $db (keys %{ $self->db_schema }) {
  121. if ($self->db_schema->{$db} eq '%') {
  122. my @db_owners = try {
  123. $self->_owners($db);
  124. }
  125. catch {
  126. if (/without logging/) {
  127. warn
  128. "Database '$db' is unreferencable due to lack of logging.\n";
  129. }
  130. return ();
  131. };
  132. if (not @db_owners) {
  133. delete $self->db_schema->{$db};
  134. next DB;
  135. }
  136. $self->db_schema->{$db} = \@db_owners;
  137. $self->qualify_objects(1);
  138. }
  139. }
  140. }
  141. sub _tables_list {
  142. my ($self, $opts) = @_;
  143. my @tables;
  144. while (my ($db, $owners) = each %{ $self->db_schema }) {
  145. foreach my $owner (@$owners) {
  146. my $table_names = $self->dbh->selectcol_arrayref(<<"EOF", {}, $owner);
  147. select tabname
  148. FROM ${db}:informix.systables
  149. WHERE rtrim(owner) = ?
  150. EOF
  151. TABLE: foreach my $table_name (@$table_names) {
  152. next if $table_name =~ /^\s/;
  153. push @tables, DBIx::Class::Schema::Loader::Table::Informix->new(
  154. loader => $self,
  155. name => $table_name,
  156. database => $db,
  157. schema => $owner,
  158. );
  159. }
  160. }
  161. }
  162. return $self->_filter_tables(\@tables, $opts);
  163. }
  164. sub _constraints_for {
  165. my ($self, $table, $type) = @_;
  166. local $self->dbh->{FetchHashKeyName} = 'NAME_lc';
  167. my $db = $table->database;
  168. my $sth = $self->dbh->prepare(<<"EOF");
  169. SELECT c.constrname, i.*
  170. FROM ${db}:informix.sysconstraints c
  171. JOIN ${db}:informix.systables t
  172. ON t.tabid = c.tabid
  173. JOIN ${db}:informix.sysindexes i
  174. ON c.idxname = i.idxname
  175. WHERE t.tabname = ? and c.constrtype = ?
  176. EOF
  177. $sth->execute($table, $type);
  178. my $indexes = $sth->fetchall_hashref('constrname');
  179. $sth->finish;
  180. my $cols = $self->_colnames_by_colno($table);
  181. my $constraints;
  182. while (my ($constr_name, $idx_def) = each %$indexes) {
  183. $constraints->{$constr_name} = $self->_idx_colnames($idx_def, $cols);
  184. }
  185. return $constraints;
  186. }
  187. sub _idx_colnames {
  188. my ($self, $idx_info, $table_cols_by_colno) = @_;
  189. return [ map $table_cols_by_colno->{$_}, grep $_, map $idx_info->{$_}, map "part$_", (1..16) ];
  190. }
  191. sub _colnames_by_colno {
  192. my ($self, $table) = @_;
  193. local $self->dbh->{FetchHashKeyName} = 'NAME_lc';
  194. my $db = $table->database;
  195. my $sth = $self->dbh->prepare(<<"EOF");
  196. SELECT c.colname, c.colno
  197. FROM ${db}:informix.syscolumns c
  198. JOIN ${db}:informix.systables t
  199. ON c.tabid = t.tabid
  200. WHERE t.tabname = ?
  201. EOF
  202. $sth->execute($table);
  203. my $cols = $sth->fetchall_hashref('colno');
  204. $cols = { map +($_, $self->_lc($cols->{$_}{colname})), keys %$cols };
  205. return $cols;
  206. }
  207. sub _table_pk_info {
  208. my ($self, $table) = @_;
  209. my $pk = (values %{ $self->_constraints_for($table, 'P') || {} })[0];
  210. return $pk;
  211. }
  212. sub _table_uniq_info {
  213. my ($self, $table) = @_;
  214. my $constraints = $self->_constraints_for($table, 'U');
  215. my @uniqs = map { [ $_ => $constraints->{$_} ] } keys %$constraints;
  216. return \@uniqs;
  217. }
  218. sub _table_fk_info {
  219. my ($self, $table) = @_;
  220. my $local_columns = $self->_constraints_for($table, 'R');
  221. local $self->dbh->{FetchHashKeyName} = 'NAME_lc';
  222. my $db = $table->database;
  223. my $sth = $self->dbh->prepare(<<"EOF");
  224. SELECT c.constrname local_constraint, rt.tabname remote_table, rtrim(rt.owner) remote_owner, rc.constrname remote_constraint, ri.*
  225. FROM ${db}:informix.sysconstraints c
  226. JOIN ${db}:informix.systables t
  227. ON c.tabid = t.tabid
  228. JOIN ${db}:informix.sysreferences r
  229. ON c.constrid = r.constrid
  230. JOIN ${db}:informix.sysconstraints rc
  231. ON rc.constrid = r.primary
  232. JOIN ${db}:informix.systables rt
  233. ON r.ptabid = rt.tabid
  234. JOIN ${db}:informix.sysindexes ri
  235. ON rc.idxname = ri.idxname
  236. WHERE t.tabname = ? and c.constrtype = 'R'
  237. EOF
  238. $sth->execute($table);
  239. my $remotes = $sth->fetchall_hashref('local_constraint');
  240. $sth->finish;
  241. my @rels;
  242. while (my ($local_constraint, $remote_info) = each %$remotes) {
  243. my $remote_table = DBIx::Class::Schema::Loader::Table::Informix->new(
  244. loader => $self,
  245. name => $remote_info->{remote_table},
  246. database => $db,
  247. schema => $remote_info->{remote_owner},
  248. );
  249. push @rels, {
  250. local_columns => $local_columns->{$local_constraint},
  251. remote_columns => $self->_idx_colnames($remote_info, $self->_colnames_by_colno($remote_table)),
  252. remote_table => $remote_table,
  253. };
  254. }
  255. return \@rels;
  256. }
  257. # This is directly from http://www.ibm.com/developerworks/data/zones/informix/library/techarticle/0305parker/0305parker.html
  258. # it doesn't work at all
  259. sub _informix_datetime_precision {
  260. my @date_type = qw/DUMMY year month day hour minute second fraction(1) fraction(2) fraction(3) fraction(4) fraction(5)/;
  261. my @start_end = ( [], [1,5],[5,7],[7,9],[9,11],[11,13],[13,15],[15,16], [16,17], [17,18], [18,19], [19,20] );
  262. my ($self, $collength) = @_;
  263. my $i = ($collength % 16) + 1;
  264. my $j = int(($collength % 256) / 16) + 1;
  265. my $k = int($collength / 256);
  266. my $len = $start_end[$i][1] - $start_end[$j][0];
  267. $len = $k - $len;
  268. if ($len == 0 || $j > 11) {
  269. return $date_type[$j] . ' to ' . $date_type[$i];
  270. }
  271. $k = $start_end[$j][1] - $start_end[$j][0];
  272. $k += $len;
  273. return $date_type[$j] . "($k) to " . $date_type[$i];
  274. }
  275. sub _columns_info_for {
  276. my $self = shift;
  277. my ($table) = @_;
  278. my $result = $self->next::method(@_);
  279. my $db = $table->database;
  280. my $sth = $self->dbh->prepare(<<"EOF");
  281. SELECT c.colname, c.coltype, c.collength, c.colmin, d.type deflt_type, d.default deflt
  282. FROM ${db}:informix.syscolumns c
  283. JOIN ${db}:informix.systables t
  284. ON c.tabid = t.tabid
  285. LEFT JOIN ${db}:informix.sysdefaults d
  286. ON t.tabid = d.tabid AND c.colno = d.colno
  287. WHERE t.tabname = ?
  288. EOF
  289. $sth->execute($table);
  290. my $cols = $sth->fetchall_hashref('colname');
  291. $sth->finish;
  292. while (my ($col, $info) = each %$cols) {
  293. $col = $self->_lc($col);
  294. my $type = $info->{coltype} % 256;
  295. if ($type == 6) { # SERIAL
  296. $result->{$col}{is_auto_increment} = 1;
  297. }
  298. elsif ($type == 7) {
  299. $result->{$col}{data_type} = 'date';
  300. }
  301. elsif ($type == 10) {
  302. $result->{$col}{data_type} = 'datetime year to fraction(5)';
  303. # this doesn't work yet
  304. # $result->{$col}{data_type} = 'datetime ' . $self->_informix_datetime_precision($info->{collength});
  305. }
  306. elsif ($type == 17 || $type == 52) {
  307. $result->{$col}{data_type} = 'bigint';
  308. }
  309. elsif ($type == 40) {
  310. $result->{$col}{data_type} = 'lvarchar';
  311. $result->{$col}{size} = $info->{collength};
  312. }
  313. elsif ($type == 12) {
  314. $result->{$col}{data_type} = 'text';
  315. }
  316. elsif ($type == 11) {
  317. $result->{$col}{data_type} = 'bytea';
  318. $result->{$col}{original}{data_type} = 'byte';
  319. }
  320. elsif ($type == 41) {
  321. # XXX no way to distinguish opaque types boolean, blob and clob
  322. $result->{$col}{data_type} = 'blob' unless $result->{$col}{data_type} eq 'smallint';
  323. }
  324. elsif ($type == 21) {
  325. $result->{$col}{data_type} = 'list';
  326. }
  327. elsif ($type == 20) {
  328. $result->{$col}{data_type} = 'multiset';
  329. }
  330. elsif ($type == 19) {
  331. $result->{$col}{data_type} = 'set';
  332. }
  333. elsif ($type == 15) {
  334. $result->{$col}{data_type} = 'nchar';
  335. }
  336. elsif ($type == 16) {
  337. $result->{$col}{data_type} = 'nvarchar';
  338. }
  339. # XXX untested!
  340. elsif ($info->{coltype} == 2061) {
  341. $result->{$col}{data_type} = 'idssecuritylabel';
  342. }
  343. my $data_type = $result->{$col}{data_type};
  344. if ($data_type !~ /^(?:[nl]?(?:var)?char|decimal)\z/i) {
  345. delete $result->{$col}{size};
  346. }
  347. if (lc($data_type) eq 'decimal') {
  348. no warnings 'uninitialized';
  349. $result->{$col}{data_type} = 'numeric';
  350. my @size = @{ $result->{$col}{size} || [] };
  351. if ($size[0] == 16 && $size[1] == -4) {
  352. delete $result->{$col}{size};
  353. }
  354. elsif ($size[0] == 16 && $size[1] == 2) {
  355. $result->{$col}{data_type} = 'money';
  356. delete $result->{$col}{size};
  357. }
  358. }
  359. elsif (lc($data_type) eq 'smallfloat') {
  360. $result->{$col}{data_type} = 'real';
  361. }
  362. elsif (lc($data_type) eq 'float') {
  363. $result->{$col}{data_type} = 'double precision';
  364. }
  365. elsif ($data_type =~ /^n?(?:var)?char\z/i) {
  366. $result->{$col}{size} = $result->{$col}{size}[0];
  367. }
  368. # XXX colmin doesn't work for min size of varchar columns, it's NULL
  369. # if (lc($data_type) eq 'varchar') {
  370. # $result->{$col}{size}[1] = $info->{colmin};
  371. # }
  372. my ($default_type, $default) = @{$info}{qw/deflt_type deflt/};
  373. next unless $default_type;
  374. if ($default_type eq 'C') {
  375. my $current = 'current year to fraction(5)';
  376. $result->{$col}{default_value} = \$current;
  377. }
  378. elsif ($default_type eq 'T') {
  379. my $today = 'today';
  380. $result->{$col}{default_value} = \$today;
  381. }
  382. else {
  383. $default = (split ' ', $default, 2)[-1];
  384. $default =~ s/\s+\z// if looks_like_number $default;
  385. # remove trailing 0s in floating point defaults
  386. # disabled, this is unsafe since it might be a varchar default
  387. #$default =~ s/0+\z// if $default =~ /^\d+\.\d+\z/;
  388. $result->{$col}{default_value} = $default;
  389. }
  390. }
  391. return $result;
  392. }
  393. =head1 SEE ALSO
  394. L<DBIx::Class::Schema::Loader>, L<DBIx::Class::Schema::Loader::Base>,
  395. L<DBIx::Class::Schema::Loader::DBI>
  396. =head1 AUTHOR
  397. See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
  398. =head1 LICENSE
  399. This library is free software; you can redistribute it and/or modify it under
  400. the same terms as Perl itself.
  401. =cut
  402. 1;
  403. # vim:et sw=4 sts=4 tw=0: