fix firebird data_type tests
[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;
4cbddf8d 5use Class::C3;
6use base qw/DBIx::Class::Schema::Loader::DBI/;
7use Carp::Clan qw/^DBIx::Class/;
4145a6f3 8use List::Util 'first';
2b74a06b 9use namespace::clean;
4cbddf8d 10
6b1d4f76 11our $VERSION = '0.07001';
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
b511f36e 24By default column names from unquoted DDL will be generated in lowercase, for
25consistency with other backends.
18e84656 26
b511f36e 27Set the L<preserve_case|DBIx::Class::Schema::Loader::Base/preserve_case> option
28to true if you would like to have column names in the internal case, which is
29uppercase for DDL that uses unquoted identifiers.
18e84656 30
b511f36e 31Do not use quoting (the L<quote_char|DBIx::Class::Storage::DBI/quote_char>
32option in L<connect_info|DBIx::Class::Storage::DBI/connect_info> when in the
33default C<< preserve_case => 0 >> mode.
18e84656 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 43=cut
243c6ebc 44
ffb03c96 45sub _setup {
46 my $self = shift;
47
bc1cb85e 48 $self->next::method(@_);
18e84656 49
bc1cb85e 50 if (not defined $self->preserve_case) {
ec957051 51 warn <<'EOF';
52
b511f36e 53WARNING: Assuming unquoted Firebird DDL, see
ec957051 54perldoc DBIx::Class::Schema::Loader::DBI::InterBase
bc1cb85e 55and the 'preserve_case' option in
56perldoc DBIx::Class::Schema::Loader::Base
ec957051 57for more information.
58
59EOF
b511f36e 60 $self->preserve_case(0);
ec957051 61 }
62
bc1cb85e 63 if ($self->preserve_case) {
18e84656 64 $self->schema->storage->sql_maker->quote_char('"');
c930f78b 65 $self->schema->storage->sql_maker->name_sep('.');
18e84656 66 }
67 else {
68 $self->schema->storage->sql_maker->quote_char(undef);
c930f78b 69 $self->schema->storage->sql_maker->name_sep(undef);
18e84656 70 }
71}
72
4cbddf8d 73sub _table_pk_info {
74 my ($self, $table) = @_;
75
76 my $dbh = $self->schema->storage->dbh;
77 my $sth = $dbh->prepare(<<'EOF');
78SELECT iseg.rdb$field_name
79FROM rdb$relation_constraints rc
80JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
81WHERE rc.rdb$constraint_type = 'PRIMARY KEY' and rc.rdb$relation_name = ?
82ORDER BY iseg.rdb$field_position
83EOF
84 $sth->execute($table);
85
86 my @keydata;
87
88 while (my ($col) = $sth->fetchrow_array) {
89 s/^\s+//, s/\s+\z// for $col;
90
18e84656 91 push @keydata, $self->_lc($col);
4cbddf8d 92 }
93
94 return \@keydata;
95}
96
97sub _table_fk_info {
98 my ($self, $table) = @_;
99
100 my ($local_cols, $remote_cols, $remote_table, @rels);
101 my $dbh = $self->schema->storage->dbh;
102 my $sth = $dbh->prepare(<<'EOF');
103SELECT rc.rdb$constraint_name fk, iseg.rdb$field_name local_col, ri.rdb$relation_name remote_tab, riseg.rdb$field_name remote_col
104FROM rdb$relation_constraints rc
105JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
106JOIN rdb$indices li ON rc.rdb$index_name = li.rdb$index_name
107JOIN rdb$indices ri ON li.rdb$foreign_key = ri.rdb$index_name
108JOIN rdb$index_segments riseg ON iseg.rdb$field_position = riseg.rdb$field_position and ri.rdb$index_name = riseg.rdb$index_name
109WHERE rc.rdb$constraint_type = 'FOREIGN KEY' and rc.rdb$relation_name = ?
110ORDER BY iseg.rdb$field_position
111EOF
112 $sth->execute($table);
113
114 while (my ($fk, $local_col, $remote_tab, $remote_col) = $sth->fetchrow_array) {
115 s/^\s+//, s/\s+\z// for $fk, $local_col, $remote_tab, $remote_col;
116
18e84656 117 push @{$local_cols->{$fk}}, $self->_lc($local_col);
118 push @{$remote_cols->{$fk}}, $self->_lc($remote_col);
4cbddf8d 119 $remote_table->{$fk} = $remote_tab;
120 }
121
122 foreach my $fk (keys %$remote_table) {
123 push @rels, {
124 local_columns => $local_cols->{$fk},
125 remote_columns => $remote_cols->{$fk},
126 remote_table => $remote_table->{$fk},
127 };
128 }
129 return \@rels;
130}
131
132sub _table_uniq_info {
133 my ($self, $table) = @_;
134
135 my $dbh = $self->schema->storage->dbh;
136 my $sth = $dbh->prepare(<<'EOF');
137SELECT rc.rdb$constraint_name, iseg.rdb$field_name
138FROM rdb$relation_constraints rc
139JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
140WHERE rc.rdb$constraint_type = 'UNIQUE' and rc.rdb$relation_name = ?
141ORDER BY iseg.rdb$field_position
142EOF
143 $sth->execute($table);
144
145 my $constraints;
146 while (my ($constraint_name, $column) = $sth->fetchrow_array) {
147 s/^\s+//, s/\s+\z// for $constraint_name, $column;
148
18e84656 149 push @{$constraints->{$constraint_name}}, $self->_lc($column);
4cbddf8d 150 }
151
152 my @uniqs = map { [ $_ => $constraints->{$_} ] } keys %$constraints;
153 return \@uniqs;
154}
155
cf0ba25b 156sub _columns_info_for {
157 my $self = shift;
158 my ($table) = @_;
159
160 my $result = $self->next::method(@_);
45be2ce7 161
162 my $dbh = $self->schema->storage->dbh;
163
164 local $dbh->{LongReadLen} = 100000;
165 local $dbh->{LongTruncOk} = 1;
166
cf0ba25b 167 while (my ($column, $info) = each %$result) {
168 my $sth = $dbh->prepare(<<'EOF');
45be2ce7 169SELECT t.rdb$trigger_source
170FROM rdb$triggers t
171WHERE t.rdb$relation_name = ?
93e8c513 172AND t.rdb$system_flag = 0 -- user defined
173AND t.rdb$trigger_type = 1 -- BEFORE INSERT
45be2ce7 174EOF
cf0ba25b 175 $sth->execute($table);
45be2ce7 176
cf0ba25b 177 while (my ($trigger) = $sth->fetchrow_array) {
178 my @trig_cols = map { /^"([^"]+)/ ? $1 : uc($_) } $trigger =~ /new\.("?\w+"?)/ig;
243c6ebc 179
cf0ba25b 180 my ($quoted, $generator) = $trigger =~ /(?:gen_id\s* \( \s* |next \s* value \s* for \s*)(")?(\w+)/ix;
4145a6f3 181
cf0ba25b 182 if ($generator) {
183 $generator = uc $generator unless $quoted;
0e0a4941 184
cf0ba25b 185 if (first { $self->_uc($_) eq $self->_uc($column) } @trig_cols) {
186 $info->{is_auto_increment} = 1;
187 $info->{sequence} = $generator;
188 last;
189 }
0e0a4941 190 }
45be2ce7 191 }
45be2ce7 192
cf0ba25b 193# fix up types
194 $sth = $dbh->prepare(<<'EOF');
195SELECT 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
196FROM rdb$fields f
197JOIN rdb$relation_fields rf ON rf.rdb$field_source = f.rdb$field_name
9dc968df 198LEFT JOIN rdb$types t ON f.rdb$field_type = t.rdb$type AND t.rdb$field_name = 'RDB$FIELD_TYPE'
199LEFT JOIN rdb$types st ON f.rdb$field_sub_type = st.rdb$type AND st.rdb$field_name = 'RDB$FIELD_SUB_TYPE'
cf0ba25b 200WHERE rf.rdb$relation_name = ?
201 AND rf.rdb$field_name = ?
202EOF
203 $sth->execute($table, $self->_uc($column));
204 my ($precision, $scale, $type_num, $sub_type_num, $type_name, $sub_type_name) = $sth->fetchrow_array;
205 $scale = -$scale if $scale && $scale < 0;
206
207 if ($type_name && $sub_type_name) {
208 s/\s+\z// for $type_name, $sub_type_name;
209
210 # fixups primarily for DBD::InterBase
9dc968df 211 if ($info->{data_type} =~ /^(?:integer|int|smallint|bigint|-9581)\z/) {
212 if ($precision && $type_name =~ /^(?:LONG|INT64)\z/ && $sub_type_name eq 'BLR') {
cf0ba25b 213 $info->{data_type} = 'decimal';
214 }
9dc968df 215 elsif ($precision && $type_name =~ /^(?:LONG|SHORT|INT64)\z/ && $sub_type_name eq 'TEXT') {
cf0ba25b 216 $info->{data_type} = 'numeric';
217 }
218 elsif ((not $precision) && $type_name eq 'INT64' && $sub_type_name eq 'BINARY') {
219 $info->{data_type} = 'bigint';
220 }
221 }
222 # ODBC makes regular blobs sub_type blr
223 elsif ($type_name eq 'BLOB') {
224 if ($sub_type_name eq 'BINARY') {
225 $info->{data_type} = 'blob';
226 }
227 elsif ($sub_type_name eq 'TEXT') {
228 $info->{data_type} = 'blob sub_type text';
229 }
230 }
231 }
232
9dc968df 233 if ($info->{data_type} =~ /^(?:decimal|numeric)\z/ && defined $precision && defined $scale) {
cf0ba25b 234 if ($precision == 9 && $scale == 0) {
235 delete $info->{size};
236 }
237 else {
238 $info->{size} = [$precision, $scale];
239 }
240 }
241
242 if ($info->{data_type} eq '11') {
243 $info->{data_type} = 'timestamp';
244 }
245 elsif ($info->{data_type} eq '10') {
246 $info->{data_type} = 'time';
247 }
248 elsif ($info->{data_type} eq '9') {
249 $info->{data_type} = 'date';
250 }
251 elsif ($info->{data_type} eq 'character varying') {
252 $info->{data_type} = 'varchar';
253 }
254 elsif ($info->{data_type} eq 'character') {
255 $info->{data_type} = 'char';
256 }
28d53000 257 elsif ($info->{data_type} eq 'float') {
258 $info->{data_type} = 'real';
259 }
cf0ba25b 260 elsif ($info->{data_type} eq 'int64' || $info->{data_type} eq '-9581') {
261 # the constant is just in case, the query should pick up the type
262 $info->{data_type} = 'bigint';
263 }
264
265 # DBD::InterBase sets scale to '0' for some reason for char types
9dc968df 266 if ($info->{data_type} =~ /^(?:char|varchar)\z/ && ref($info->{size}) eq 'ARRAY') {
cf0ba25b 267 $info->{size} = $info->{size}[0];
268 }
9dc968df 269 elsif ($info->{data_type} !~ /^(?:char|varchar|numeric|decimal)\z/) {
cf0ba25b 270 delete $info->{size};
271 }
4145a6f3 272
273# get default
cf0ba25b 274 delete $info->{default_value} if $info->{default_value} && $info->{default_value} eq 'NULL';
275
276 $sth = $dbh->prepare(<<'EOF');
4145a6f3 277SELECT rf.rdb$default_source
278FROM rdb$relation_fields rf
279WHERE rf.rdb$relation_name = ?
280AND rf.rdb$field_name = ?
281EOF
cf0ba25b 282 $sth->execute($table, $self->_uc($column));
283 my ($default_src) = $sth->fetchrow_array;
4145a6f3 284
cf0ba25b 285 if ($default_src && (my ($def) = $default_src =~ /^DEFAULT \s+ (\S+)/ix)) {
286 if (my ($quoted) = $def =~ /^'(.*?)'\z/) {
287 $info->{default_value} = $quoted;
288 }
289 else {
2a1ff2ee 290 $info->{default_value} = $def =~ /^-?\d/ ? $def : \$def;
cf0ba25b 291 }
4145a6f3 292 }
6e566cc4 293
294 ${ $info->{default_value} } = 'current_timestamp'
295 if ref $info->{default_value} && ${ $info->{default_value} } eq 'CURRENT_TIMESTAMP';
4145a6f3 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: