WIP
[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 base qw/DBIx::Class::Schema::Loader::DBI/;
383bd2a8 6use mro 'c3';
4cbddf8d 7use Carp::Clan qw/^DBIx::Class/;
4145a6f3 8use List::Util 'first';
2b74a06b 9use namespace::clean;
c4a69b87 10use DBIx::Class::Schema::Loader::Table ();
4cbddf8d 11
ae151d4f 12our $VERSION = '0.07033';
4cbddf8d 13
c4a69b87 14sub _supports_db_schema { 0 }
15
4cbddf8d 16=head1 NAME
17
18DBIx::Class::Schema::Loader::DBI::InterBase - DBIx::Class::Schema::Loader::DBI
19Firebird Implementation.
20
21=head1 DESCRIPTION
22
bc1cb85e 23See L<DBIx::Class::Schema::Loader> and L<DBIx::Class::Schema::Loader::Base>.
24
25=head1 COLUMN NAME CASE ISSUES
4cbddf8d 26
b511f36e 27By default column names from unquoted DDL will be generated in lowercase, for
0b763036 28consistency with other backends.
18e84656 29
b511f36e 30Set the L<preserve_case|DBIx::Class::Schema::Loader::Base/preserve_case> option
31to true if you would like to have column names in the internal case, which is
32uppercase for DDL that uses unquoted identifiers.
18e84656 33
b511f36e 34Do not use quoting (the L<quote_char|DBIx::Class::Storage::DBI/quote_char>
35option in L<connect_info|DBIx::Class::Storage::DBI/connect_info> when in the
36default C<< preserve_case => 0 >> mode.
18e84656 37
bc1cb85e 38Be careful to also not use any SQL reserved words in your DDL.
39
18e84656 40This will generate lowercase column names (as opposed to the actual uppercase
41names) in your Result classes that will only work with quoting off.
42
43Mixed-case table and column names will be ignored when this option is on and
44will not work with quoting turned off.
45
bc1cb85e 46=cut
243c6ebc 47
ffb03c96 48sub _setup {
49 my $self = shift;
50
bc1cb85e 51 $self->next::method(@_);
18e84656 52
bc1cb85e 53 if (not defined $self->preserve_case) {
b511f36e 54 $self->preserve_case(0);
ec957051 55 }
c4a69b87 56 elsif ($self->preserve_case) {
18e84656 57 $self->schema->storage->sql_maker->quote_char('"');
c930f78b 58 $self->schema->storage->sql_maker->name_sep('.');
18e84656 59 }
c4a69b87 60
61 if ($self->db_schema) {
62 carp "db_schema is not supported on Firebird";
63
64 if ($self->db_schema->[0] eq '%') {
65 $self->db_schema(undef);
66 }
18e84656 67 }
68}
69
4cbddf8d 70sub _table_pk_info {
71 my ($self, $table) = @_;
72
c4a69b87 73 my $sth = $self->dbh->prepare(<<'EOF');
4cbddf8d 74SELECT iseg.rdb$field_name
75FROM rdb$relation_constraints rc
76JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
77WHERE rc.rdb$constraint_type = 'PRIMARY KEY' and rc.rdb$relation_name = ?
78ORDER BY iseg.rdb$field_position
79EOF
c4a69b87 80 $sth->execute($table->name);
4cbddf8d 81
82 my @keydata;
83
84 while (my ($col) = $sth->fetchrow_array) {
85 s/^\s+//, s/\s+\z// for $col;
86
18e84656 87 push @keydata, $self->_lc($col);
4cbddf8d 88 }
89
90 return \@keydata;
91}
92
93sub _table_fk_info {
94 my ($self, $table) = @_;
95
0b763036 96 my ($local_cols, $remote_cols, $remote_table, $attrs, @rels);
c4a69b87 97 my $sth = $self->dbh->prepare(<<'EOF');
4cbddf8d 98SELECT rc.rdb$constraint_name fk, iseg.rdb$field_name local_col, ri.rdb$relation_name remote_tab, riseg.rdb$field_name remote_col
99FROM rdb$relation_constraints rc
100JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
101JOIN rdb$indices li ON rc.rdb$index_name = li.rdb$index_name
102JOIN rdb$indices ri ON li.rdb$foreign_key = ri.rdb$index_name
103JOIN rdb$index_segments riseg ON iseg.rdb$field_position = riseg.rdb$field_position and ri.rdb$index_name = riseg.rdb$index_name
104WHERE rc.rdb$constraint_type = 'FOREIGN KEY' and rc.rdb$relation_name = ?
105ORDER BY iseg.rdb$field_position
106EOF
c4a69b87 107 $sth->execute($table->name);
4cbddf8d 108
109 while (my ($fk, $local_col, $remote_tab, $remote_col) = $sth->fetchrow_array) {
110 s/^\s+//, s/\s+\z// for $fk, $local_col, $remote_tab, $remote_col;
111
18e84656 112 push @{$local_cols->{$fk}}, $self->_lc($local_col);
0b763036 113
18e84656 114 push @{$remote_cols->{$fk}}, $self->_lc($remote_col);
0b763036 115
c4a69b87 116 $remote_table->{$fk} = DBIx::Class::Schema::Loader::Table->new(
117 loader => $self,
118 name => $remote_tab,
119 ($self->db_schema ? (
120 schema => $self->db_schema->[0],
121 ignore_schema => 1,
122 ) : ()),
123 );
4cbddf8d 124 }
125
0b763036 126 local $self->dbh->{LongReadLen} = 100_000;
127 local $self->dbh->{LongTruncOk} = 1;
128
129 my %RULE_FOR = (
130 4 => 'on_update',
131 6 => 'on_delete',
132 );
133
134 $sth = $self->dbh->prepare_cached(<<'EOF');
135select rdb$trigger_blr, rdb$trigger_type
136from rdb$triggers
137where rdb$trigger_type in (4,6)
138 and rdb$system_flag > 0
139 and rdb$relation_name = ?
140EOF
141
142 foreach my $fk (keys %$remote_table) {
143 my $uk_table = $remote_table->{$fk};
144
145 $sth->execute($uk_table);
146
147 while (my ($blr, $type) = $sth->fetchrow_array) {
148 $type = $RULE_FOR{$type};
149
150 print STDERR "GOT $type:\n";
151 use Data::Dumper;
152 print STDERR Dumper($blr), "\n";
153 }
154 }
155
4cbddf8d 156 foreach my $fk (keys %$remote_table) {
157 push @rels, {
158 local_columns => $local_cols->{$fk},
159 remote_columns => $remote_cols->{$fk},
160 remote_table => $remote_table->{$fk},
161 };
162 }
163 return \@rels;
164}
165
166sub _table_uniq_info {
167 my ($self, $table) = @_;
168
c4a69b87 169 my $sth = $self->dbh->prepare(<<'EOF');
4cbddf8d 170SELECT rc.rdb$constraint_name, iseg.rdb$field_name
171FROM rdb$relation_constraints rc
172JOIN rdb$index_segments iseg ON rc.rdb$index_name = iseg.rdb$index_name
173WHERE rc.rdb$constraint_type = 'UNIQUE' and rc.rdb$relation_name = ?
174ORDER BY iseg.rdb$field_position
175EOF
c4a69b87 176 $sth->execute($table->name);
4cbddf8d 177
178 my $constraints;
179 while (my ($constraint_name, $column) = $sth->fetchrow_array) {
180 s/^\s+//, s/\s+\z// for $constraint_name, $column;
181
18e84656 182 push @{$constraints->{$constraint_name}}, $self->_lc($column);
4cbddf8d 183 }
184
185 my @uniqs = map { [ $_ => $constraints->{$_} ] } keys %$constraints;
186 return \@uniqs;
187}
188
cf0ba25b 189sub _columns_info_for {
190 my $self = shift;
191 my ($table) = @_;
192
193 my $result = $self->next::method(@_);
45be2ce7 194
c4a69b87 195 local $self->dbh->{LongReadLen} = 100000;
196 local $self->dbh->{LongTruncOk} = 1;
45be2ce7 197
cf0ba25b 198 while (my ($column, $info) = each %$result) {
c4a69b87 199 my $data_type = $info->{data_type};
200
201 my $sth = $self->dbh->prepare(<<'EOF');
45be2ce7 202SELECT t.rdb$trigger_source
203FROM rdb$triggers t
204WHERE t.rdb$relation_name = ?
93e8c513 205AND t.rdb$system_flag = 0 -- user defined
206AND t.rdb$trigger_type = 1 -- BEFORE INSERT
45be2ce7 207EOF
c4a69b87 208 $sth->execute($table->name);
45be2ce7 209
cf0ba25b 210 while (my ($trigger) = $sth->fetchrow_array) {
211 my @trig_cols = map { /^"([^"]+)/ ? $1 : uc($_) } $trigger =~ /new\.("?\w+"?)/ig;
243c6ebc 212
cf0ba25b 213 my ($quoted, $generator) = $trigger =~ /(?:gen_id\s* \( \s* |next \s* value \s* for \s*)(")?(\w+)/ix;
4145a6f3 214
cf0ba25b 215 if ($generator) {
216 $generator = uc $generator unless $quoted;
0e0a4941 217
cf0ba25b 218 if (first { $self->_uc($_) eq $self->_uc($column) } @trig_cols) {
219 $info->{is_auto_increment} = 1;
220 $info->{sequence} = $generator;
221 last;
222 }
0e0a4941 223 }
45be2ce7 224 }
45be2ce7 225
cf0ba25b 226# fix up types
c4a69b87 227 $sth = $self->dbh->prepare(<<'EOF');
5111e5d0 228SELECT f.rdb$field_precision, f.rdb$field_scale, f.rdb$field_type, f.rdb$field_sub_type, f.rdb$character_set_id, f.rdb$character_length, t.rdb$type_name, st.rdb$type_name
cf0ba25b 229FROM rdb$fields f
230JOIN rdb$relation_fields rf ON rf.rdb$field_source = f.rdb$field_name
9dc968df 231LEFT JOIN rdb$types t ON f.rdb$field_type = t.rdb$type AND t.rdb$field_name = 'RDB$FIELD_TYPE'
232LEFT JOIN rdb$types st ON f.rdb$field_sub_type = st.rdb$type AND st.rdb$field_name = 'RDB$FIELD_SUB_TYPE'
cf0ba25b 233WHERE rf.rdb$relation_name = ?
234 AND rf.rdb$field_name = ?
235EOF
c4a69b87 236 $sth->execute($table->name, $self->_uc($column));
5111e5d0 237 my ($precision, $scale, $type_num, $sub_type_num, $char_set_id, $char_length, $type_name, $sub_type_name) = $sth->fetchrow_array;
cf0ba25b 238 $scale = -$scale if $scale && $scale < 0;
239
240 if ($type_name && $sub_type_name) {
241 s/\s+\z// for $type_name, $sub_type_name;
242
243 # fixups primarily for DBD::InterBase
c4a69b87 244 if ($data_type =~ /^(?:integer|int|smallint|bigint|-9581)\z/) {
9dc968df 245 if ($precision && $type_name =~ /^(?:LONG|INT64)\z/ && $sub_type_name eq 'BLR') {
cf0ba25b 246 $info->{data_type} = 'decimal';
247 }
9dc968df 248 elsif ($precision && $type_name =~ /^(?:LONG|SHORT|INT64)\z/ && $sub_type_name eq 'TEXT') {
cf0ba25b 249 $info->{data_type} = 'numeric';
250 }
251 elsif ((not $precision) && $type_name eq 'INT64' && $sub_type_name eq 'BINARY') {
252 $info->{data_type} = 'bigint';
253 }
254 }
255 # ODBC makes regular blobs sub_type blr
256 elsif ($type_name eq 'BLOB') {
257 if ($sub_type_name eq 'BINARY') {
258 $info->{data_type} = 'blob';
259 }
260 elsif ($sub_type_name eq 'TEXT') {
4a01c33f 261 if (defined $char_set_id && $char_set_id == 3) {
5111e5d0 262 $info->{data_type} = 'blob sub_type text character set unicode_fss';
263 }
264 else {
265 $info->{data_type} = 'blob sub_type text';
266 }
cf0ba25b 267 }
268 }
269 }
270
c4a69b87 271 $data_type = $info->{data_type};
272
273 if ($data_type =~ /^(?:decimal|numeric)\z/ && defined $precision && defined $scale) {
cf0ba25b 274 if ($precision == 9 && $scale == 0) {
275 delete $info->{size};
276 }
277 else {
278 $info->{size} = [$precision, $scale];
279 }
280 }
281
c4a69b87 282 if ($data_type eq '11') {
cf0ba25b 283 $info->{data_type} = 'timestamp';
284 }
c4a69b87 285 elsif ($data_type eq '10') {
cf0ba25b 286 $info->{data_type} = 'time';
287 }
c4a69b87 288 elsif ($data_type eq '9') {
cf0ba25b 289 $info->{data_type} = 'date';
290 }
c4a69b87 291 elsif ($data_type eq 'character varying') {
cf0ba25b 292 $info->{data_type} = 'varchar';
293 }
c4a69b87 294 elsif ($data_type eq 'character') {
cf0ba25b 295 $info->{data_type} = 'char';
296 }
c4a69b87 297 elsif ($data_type eq 'float') {
28d53000 298 $info->{data_type} = 'real';
299 }
c4a69b87 300 elsif ($data_type eq 'int64' || $data_type eq '-9581') {
cf0ba25b 301 # the constant is just in case, the query should pick up the type
302 $info->{data_type} = 'bigint';
303 }
304
c4a69b87 305 $data_type = $info->{data_type};
306
307 if ($data_type =~ /^(?:char|varchar)\z/) {
5111e5d0 308 $info->{size} = $char_length;
309
4a01c33f 310 if (defined $char_set_id && $char_set_id == 3) {
5111e5d0 311 $info->{data_type} .= '(x) character set unicode_fss';
312 }
cf0ba25b 313 }
c4a69b87 314 elsif ($data_type !~ /^(?:numeric|decimal)\z/) {
cf0ba25b 315 delete $info->{size};
316 }
4145a6f3 317
318# get default
cf0ba25b 319 delete $info->{default_value} if $info->{default_value} && $info->{default_value} eq 'NULL';
320
c4a69b87 321 $sth = $self->dbh->prepare(<<'EOF');
4145a6f3 322SELECT rf.rdb$default_source
323FROM rdb$relation_fields rf
324WHERE rf.rdb$relation_name = ?
325AND rf.rdb$field_name = ?
326EOF
c4a69b87 327 $sth->execute($table->name, $self->_uc($column));
cf0ba25b 328 my ($default_src) = $sth->fetchrow_array;
4145a6f3 329
cf0ba25b 330 if ($default_src && (my ($def) = $default_src =~ /^DEFAULT \s+ (\S+)/ix)) {
331 if (my ($quoted) = $def =~ /^'(.*?)'\z/) {
332 $info->{default_value} = $quoted;
333 }
334 else {
2a1ff2ee 335 $info->{default_value} = $def =~ /^-?\d/ ? $def : \$def;
cf0ba25b 336 }
4145a6f3 337 }
6e566cc4 338
339 ${ $info->{default_value} } = 'current_timestamp'
340 if ref $info->{default_value} && ${ $info->{default_value} } eq 'CURRENT_TIMESTAMP';
4145a6f3 341 }
342
cf0ba25b 343 return $result;
45be2ce7 344}
345
4cbddf8d 346=head1 SEE ALSO
347
348L<DBIx::Class::Schema::Loader>, L<DBIx::Class::Schema::Loader::Base>,
349L<DBIx::Class::Schema::Loader::DBI>
350
351=head1 AUTHOR
352
353See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
354
355=head1 LICENSE
356
357This library is free software; you can redistribute it and/or modify it under
358the same terms as Perl itself.
359
360=cut
361
3621;
cf0ba25b 363# vim:et sw=4 sts=4 tw=0: