Informix: write out highest precision datetime (until we can parse the datetime preci...
[dbsrgits/DBIx-Class-Schema-Loader.git] / lib / DBIx / Class / Schema / Loader / DBI / InterBase.pm
CommitLineData
4cbddf8d 1package DBIx::Class::Schema::Loader::DBI::InterBase;
2
3use strict;
4use warnings;
4145a6f3 5use namespace::autoclean;
4cbddf8d 6use Class::C3;
7use base qw/DBIx::Class::Schema::Loader::DBI/;
8use Carp::Clan qw/^DBIx::Class/;
4145a6f3 9use List::Util 'first';
4cbddf8d 10
9990e58f 11our $VERSION = '0.07000';
4cbddf8d 12
13=head1 NAME
14
15DBIx::Class::Schema::Loader::DBI::InterBase - DBIx::Class::Schema::Loader::DBI
16Firebird Implementation.
17
18=head1 DESCRIPTION
19
bc1cb85e 20See L<DBIx::Class::Schema::Loader> and L<DBIx::Class::Schema::Loader::Base>.
21
22=head1 COLUMN NAME CASE ISSUES
4cbddf8d 23
18e84656 24By default column names from unquoted DDL will be generated in uppercase, as
25that is the only way they will work with quoting on.
26
bc1cb85e 27See the L<preserve_case|DBIx::Class::Schema::Loader::Base/preserve_case> option
28to false if you would like to have lowercase column names.
18e84656 29
bc1cb85e 30Setting this option is a good idea if your DDL uses unquoted identifiers and
31you will not use quoting (the
32L<quote_char|DBIx::Class::Storage::DBI/quote_char> option in
18e84656 33L<connect_info|DBIx::Class::Storage::DBI/connect_info>.)
34
bc1cb85e 35Be careful to also not use any SQL reserved words in your DDL.
36
18e84656 37This will generate lowercase column names (as opposed to the actual uppercase
38names) in your Result classes that will only work with quoting off.
39
40Mixed-case table and column names will be ignored when this option is on and
41will not work with quoting turned off.
42
bc1cb85e 43B<NOTE:> This option used to be called C<unquoted_ddl> but has been removed in
44favor of the more generic option.
18e84656 45
bc1cb85e 46=cut
243c6ebc 47
ffb03c96 48sub _setup {
49 my $self = shift;
50
bc1cb85e 51 $self->next::method(@_);
18e84656 52
ffb03c96 53 $self->schema->storage->sql_maker->name_sep('.');
18e84656 54
bc1cb85e 55 if (not defined $self->preserve_case) {
ec957051 56 warn <<'EOF';
57
bc1cb85e 58WARNING: Assuming mixed-case Firebird DDL, see
ec957051 59perldoc DBIx::Class::Schema::Loader::DBI::InterBase
bc1cb85e 60and the 'preserve_case' option in
61perldoc DBIx::Class::Schema::Loader::Base
ec957051 62for more information.
63
64EOF
bc1cb85e 65 $self->preserve_case(1);
ec957051 66 }
67
bc1cb85e 68 if ($self->preserve_case) {
18e84656 69 $self->schema->storage->sql_maker->quote_char('"');
70 }
71 else {
72 $self->schema->storage->sql_maker->quote_char(undef);
73 }
74}
75
4cbddf8d 76sub _table_pk_info {
77 my ($self, $table) = @_;
78
79 my $dbh = $self->schema->storage->dbh;
80 my $sth = $dbh->prepare(<<'EOF');
81SELECT iseg.rdb$field_name
82FROM rdb$relation_constraints rc
83JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
84WHERE rc.rdb$constraint_type = 'PRIMARY KEY' and rc.rdb$relation_name = ?
85ORDER BY iseg.rdb$field_position
86EOF
87 $sth->execute($table);
88
89 my @keydata;
90
91 while (my ($col) = $sth->fetchrow_array) {
92 s/^\s+//, s/\s+\z// for $col;
93
18e84656 94 push @keydata, $self->_lc($col);
4cbddf8d 95 }
96
97 return \@keydata;
98}
99
100sub _table_fk_info {
101 my ($self, $table) = @_;
102
103 my ($local_cols, $remote_cols, $remote_table, @rels);
104 my $dbh = $self->schema->storage->dbh;
105 my $sth = $dbh->prepare(<<'EOF');
106SELECT rc.rdb$constraint_name fk, iseg.rdb$field_name local_col, ri.rdb$relation_name remote_tab, riseg.rdb$field_name remote_col
107FROM rdb$relation_constraints rc
108JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
109JOIN rdb$indices li ON rc.rdb$index_name = li.rdb$index_name
110JOIN rdb$indices ri ON li.rdb$foreign_key = ri.rdb$index_name
111JOIN rdb$index_segments riseg ON iseg.rdb$field_position = riseg.rdb$field_position and ri.rdb$index_name = riseg.rdb$index_name
112WHERE rc.rdb$constraint_type = 'FOREIGN KEY' and rc.rdb$relation_name = ?
113ORDER BY iseg.rdb$field_position
114EOF
115 $sth->execute($table);
116
117 while (my ($fk, $local_col, $remote_tab, $remote_col) = $sth->fetchrow_array) {
118 s/^\s+//, s/\s+\z// for $fk, $local_col, $remote_tab, $remote_col;
119
18e84656 120 push @{$local_cols->{$fk}}, $self->_lc($local_col);
121 push @{$remote_cols->{$fk}}, $self->_lc($remote_col);
4cbddf8d 122 $remote_table->{$fk} = $remote_tab;
123 }
124
125 foreach my $fk (keys %$remote_table) {
126 push @rels, {
127 local_columns => $local_cols->{$fk},
128 remote_columns => $remote_cols->{$fk},
129 remote_table => $remote_table->{$fk},
130 };
131 }
132 return \@rels;
133}
134
135sub _table_uniq_info {
136 my ($self, $table) = @_;
137
138 my $dbh = $self->schema->storage->dbh;
139 my $sth = $dbh->prepare(<<'EOF');
140SELECT rc.rdb$constraint_name, iseg.rdb$field_name
141FROM rdb$relation_constraints rc
142JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
143WHERE rc.rdb$constraint_type = 'UNIQUE' and rc.rdb$relation_name = ?
144ORDER BY iseg.rdb$field_position
145EOF
146 $sth->execute($table);
147
148 my $constraints;
149 while (my ($constraint_name, $column) = $sth->fetchrow_array) {
150 s/^\s+//, s/\s+\z// for $constraint_name, $column;
151
18e84656 152 push @{$constraints->{$constraint_name}}, $self->_lc($column);
4cbddf8d 153 }
154
155 my @uniqs = map { [ $_ => $constraints->{$_} ] } keys %$constraints;
156 return \@uniqs;
157}
158
cf0ba25b 159sub _columns_info_for {
160 my $self = shift;
161 my ($table) = @_;
162
163 my $result = $self->next::method(@_);
45be2ce7 164
165 my $dbh = $self->schema->storage->dbh;
166
167 local $dbh->{LongReadLen} = 100000;
168 local $dbh->{LongTruncOk} = 1;
169
cf0ba25b 170 while (my ($column, $info) = each %$result) {
171 my $sth = $dbh->prepare(<<'EOF');
45be2ce7 172SELECT t.rdb$trigger_source
173FROM rdb$triggers t
174WHERE t.rdb$relation_name = ?
93e8c513 175AND t.rdb$system_flag = 0 -- user defined
176AND t.rdb$trigger_type = 1 -- BEFORE INSERT
45be2ce7 177EOF
cf0ba25b 178 $sth->execute($table);
45be2ce7 179
cf0ba25b 180 while (my ($trigger) = $sth->fetchrow_array) {
181 my @trig_cols = map { /^"([^"]+)/ ? $1 : uc($_) } $trigger =~ /new\.("?\w+"?)/ig;
243c6ebc 182
cf0ba25b 183 my ($quoted, $generator) = $trigger =~ /(?:gen_id\s* \( \s* |next \s* value \s* for \s*)(")?(\w+)/ix;
4145a6f3 184
cf0ba25b 185 if ($generator) {
186 $generator = uc $generator unless $quoted;
0e0a4941 187
cf0ba25b 188 if (first { $self->_uc($_) eq $self->_uc($column) } @trig_cols) {
189 $info->{is_auto_increment} = 1;
190 $info->{sequence} = $generator;
191 last;
192 }
0e0a4941 193 }
45be2ce7 194 }
45be2ce7 195
cf0ba25b 196# fix up types
197 $sth = $dbh->prepare(<<'EOF');
198SELECT f.rdb$field_precision, f.rdb$field_scale, f.rdb$field_type, f.rdb$field_sub_type, t.rdb$type_name, st.rdb$type_name
199FROM rdb$fields f
200JOIN rdb$relation_fields rf ON rf.rdb$field_source = f.rdb$field_name
9dc968df 201LEFT JOIN rdb$types t ON f.rdb$field_type = t.rdb$type AND t.rdb$field_name = 'RDB$FIELD_TYPE'
202LEFT JOIN rdb$types st ON f.rdb$field_sub_type = st.rdb$type AND st.rdb$field_name = 'RDB$FIELD_SUB_TYPE'
cf0ba25b 203WHERE rf.rdb$relation_name = ?
204 AND rf.rdb$field_name = ?
205EOF
206 $sth->execute($table, $self->_uc($column));
207 my ($precision, $scale, $type_num, $sub_type_num, $type_name, $sub_type_name) = $sth->fetchrow_array;
208 $scale = -$scale if $scale && $scale < 0;
209
210 if ($type_name && $sub_type_name) {
211 s/\s+\z// for $type_name, $sub_type_name;
212
213 # fixups primarily for DBD::InterBase
9dc968df 214 if ($info->{data_type} =~ /^(?:integer|int|smallint|bigint|-9581)\z/) {
215 if ($precision && $type_name =~ /^(?:LONG|INT64)\z/ && $sub_type_name eq 'BLR') {
cf0ba25b 216 $info->{data_type} = 'decimal';
217 }
9dc968df 218 elsif ($precision && $type_name =~ /^(?:LONG|SHORT|INT64)\z/ && $sub_type_name eq 'TEXT') {
cf0ba25b 219 $info->{data_type} = 'numeric';
220 }
221 elsif ((not $precision) && $type_name eq 'INT64' && $sub_type_name eq 'BINARY') {
222 $info->{data_type} = 'bigint';
223 }
224 }
225 # ODBC makes regular blobs sub_type blr
226 elsif ($type_name eq 'BLOB') {
227 if ($sub_type_name eq 'BINARY') {
228 $info->{data_type} = 'blob';
229 }
230 elsif ($sub_type_name eq 'TEXT') {
231 $info->{data_type} = 'blob sub_type text';
232 }
233 }
234 }
235
9dc968df 236 if ($info->{data_type} =~ /^(?:decimal|numeric)\z/ && defined $precision && defined $scale) {
cf0ba25b 237 if ($precision == 9 && $scale == 0) {
238 delete $info->{size};
239 }
240 else {
241 $info->{size} = [$precision, $scale];
242 }
243 }
244
245 if ($info->{data_type} eq '11') {
246 $info->{data_type} = 'timestamp';
247 }
248 elsif ($info->{data_type} eq '10') {
249 $info->{data_type} = 'time';
250 }
251 elsif ($info->{data_type} eq '9') {
252 $info->{data_type} = 'date';
253 }
254 elsif ($info->{data_type} eq 'character varying') {
255 $info->{data_type} = 'varchar';
256 }
257 elsif ($info->{data_type} eq 'character') {
258 $info->{data_type} = 'char';
259 }
260 elsif ($info->{data_type} eq 'real') {
261 $info->{data_type} = 'float';
262 }
263 elsif ($info->{data_type} eq 'int64' || $info->{data_type} eq '-9581') {
264 # the constant is just in case, the query should pick up the type
265 $info->{data_type} = 'bigint';
266 }
267
268 # DBD::InterBase sets scale to '0' for some reason for char types
9dc968df 269 if ($info->{data_type} =~ /^(?:char|varchar)\z/ && ref($info->{size}) eq 'ARRAY') {
cf0ba25b 270 $info->{size} = $info->{size}[0];
271 }
9dc968df 272 elsif ($info->{data_type} !~ /^(?:char|varchar|numeric|decimal)\z/) {
cf0ba25b 273 delete $info->{size};
274 }
4145a6f3 275
276# get default
cf0ba25b 277 delete $info->{default_value} if $info->{default_value} && $info->{default_value} eq 'NULL';
278
279 $sth = $dbh->prepare(<<'EOF');
4145a6f3 280SELECT rf.rdb$default_source
281FROM rdb$relation_fields rf
282WHERE rf.rdb$relation_name = ?
283AND rf.rdb$field_name = ?
284EOF
cf0ba25b 285 $sth->execute($table, $self->_uc($column));
286 my ($default_src) = $sth->fetchrow_array;
4145a6f3 287
cf0ba25b 288 if ($default_src && (my ($def) = $default_src =~ /^DEFAULT \s+ (\S+)/ix)) {
289 if (my ($quoted) = $def =~ /^'(.*?)'\z/) {
290 $info->{default_value} = $quoted;
291 }
292 else {
293 $info->{default_value} = $def =~ /^\d/ ? $def : \$def;
294 }
4145a6f3 295 }
296 }
297
cf0ba25b 298 return $result;
45be2ce7 299}
300
4cbddf8d 301=head1 SEE ALSO
302
303L<DBIx::Class::Schema::Loader>, L<DBIx::Class::Schema::Loader::Base>,
304L<DBIx::Class::Schema::Loader::DBI>
305
306=head1 AUTHOR
307
308See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
309
310=head1 LICENSE
311
312This library is free software; you can redistribute it and/or modify it under
313the same terms as Perl itself.
314
315=cut
316
3171;
cf0ba25b 318# vim:et sw=4 sts=4 tw=0: