fix negative number defaults for Firebird
[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
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 }
cf0ba25b 257 elsif ($info->{data_type} eq 'int64' || $info->{data_type} eq '-9581') {
258 # the constant is just in case, the query should pick up the type
259 $info->{data_type} = 'bigint';
260 }
261
262 # DBD::InterBase sets scale to '0' for some reason for char types
9dc968df 263 if ($info->{data_type} =~ /^(?:char|varchar)\z/ && ref($info->{size}) eq 'ARRAY') {
cf0ba25b 264 $info->{size} = $info->{size}[0];
265 }
9dc968df 266 elsif ($info->{data_type} !~ /^(?:char|varchar|numeric|decimal)\z/) {
cf0ba25b 267 delete $info->{size};
268 }
4145a6f3 269
270# get default
cf0ba25b 271 delete $info->{default_value} if $info->{default_value} && $info->{default_value} eq 'NULL';
272
273 $sth = $dbh->prepare(<<'EOF');
4145a6f3 274SELECT rf.rdb$default_source
275FROM rdb$relation_fields rf
276WHERE rf.rdb$relation_name = ?
277AND rf.rdb$field_name = ?
278EOF
cf0ba25b 279 $sth->execute($table, $self->_uc($column));
280 my ($default_src) = $sth->fetchrow_array;
4145a6f3 281
cf0ba25b 282 if ($default_src && (my ($def) = $default_src =~ /^DEFAULT \s+ (\S+)/ix)) {
283 if (my ($quoted) = $def =~ /^'(.*?)'\z/) {
284 $info->{default_value} = $quoted;
285 }
286 else {
2a1ff2ee 287 $info->{default_value} = $def =~ /^-?\d/ ? $def : \$def;
cf0ba25b 288 }
4145a6f3 289 }
6e566cc4 290
291 ${ $info->{default_value} } = 'current_timestamp'
292 if ref $info->{default_value} && ${ $info->{default_value} } eq 'CURRENT_TIMESTAMP';
4145a6f3 293 }
294
cf0ba25b 295 return $result;
45be2ce7 296}
297
4cbddf8d 298=head1 SEE ALSO
299
300L<DBIx::Class::Schema::Loader>, L<DBIx::Class::Schema::Loader::Base>,
301L<DBIx::Class::Schema::Loader::DBI>
302
303=head1 AUTHOR
304
305See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
306
307=head1 LICENSE
308
309This library is free software; you can redistribute it and/or modify it under
310the same terms as Perl itself.
311
312=cut
313
3141;
cf0ba25b 315# vim:et sw=4 sts=4 tw=0: