File Coverage

blib/lib/DBIx/Class/Schema/Loader/DBI/Pg.pm
Criterion Covered Total %
statement 12 133 9.0
branch 0 70 0.0
condition 0 37 0.0
subroutine 4 12 33.3
pod n/a
total 16 252 6.3


line stmt bran cond sub pod time code
1             package DBIx::Class::Schema::Loader::DBI::Pg;
2              
3 1     1   1061 use strict;
  1         2  
  1         31  
4 1     1   5 use warnings;
  1         2  
  1         29  
5 1     1   7 use base 'DBIx::Class::Schema::Loader::DBI::Component::QuotedDefault';
  1         4  
  1         125  
6 1     1   8 use mro 'c3';
  1         53  
  1         7  
7              
8             our $VERSION = '0.07050';
9              
10             =head1 NAME
11              
12             DBIx::Class::Schema::Loader::DBI::Pg - DBIx::Class::Schema::Loader::DBI
13             PostgreSQL Implementation.
14              
15             =head1 DESCRIPTION
16              
17             See L and L.
18              
19             =cut
20              
21             sub _setup {
22 0     0     my $self = shift;
23              
24 0           $self->next::method(@_);
25              
26 0   0       $self->{db_schema} ||= ['public'];
27              
28 0 0         if (not defined $self->preserve_case) {
    0          
29 0           $self->preserve_case(0);
30             }
31             elsif ($self->preserve_case) {
32 0           $self->schema->storage->sql_maker->quote_char('"');
33 0           $self->schema->storage->sql_maker->name_sep('.');
34             }
35             }
36              
37             sub _system_schemas {
38 0     0     my $self = shift;
39              
40 0           return ($self->next::method(@_), 'pg_catalog');
41             }
42              
43             my %pg_rules = (
44             a => 'NO ACTION',
45             r => 'RESTRICT',
46             c => 'CASCADE',
47             n => 'SET NULL',
48             d => 'SET DEFAULT',
49             );
50              
51             sub _table_fk_info {
52 0     0     my ($self, $table) = @_;
53              
54 0           my $sth = $self->dbh->prepare_cached(<<"EOF");
55             select constr.conname, to_ns.nspname, to_class.relname, from_col.attname, to_col.attname,
56             constr.confdeltype, constr.confupdtype, constr.condeferrable
57             from pg_catalog.pg_constraint constr
58             join pg_catalog.pg_namespace from_ns on constr.connamespace = from_ns.oid
59             join pg_catalog.pg_class from_class on constr.conrelid = from_class.oid and from_class.relnamespace = from_ns.oid
60             join pg_catalog.pg_class to_class on constr.confrelid = to_class.oid
61             join pg_catalog.pg_namespace to_ns on to_class.relnamespace = to_ns.oid
62             -- can't do unnest() until 8.4, so join against a series table instead
63             join pg_catalog.generate_series(1, pg_catalog.current_setting('max_index_keys')::integer) colnum(i)
64             on colnum.i <= pg_catalog.array_upper(constr.conkey,1)
65             join pg_catalog.pg_attribute to_col
66             on to_col.attrelid = constr.confrelid
67             and to_col.attnum = constr.confkey[colnum.i]
68             join pg_catalog.pg_attribute from_col
69             on from_col.attrelid = constr.conrelid
70             and from_col.attnum = constr.conkey[colnum.i]
71             where from_ns.nspname = ?
72             and from_class.relname = ?
73             and from_class.relkind = 'r'
74             and constr.contype = 'f'
75             order by constr.conname, colnum.i
76             EOF
77              
78 0           $sth->execute($table->schema, $table->name);
79              
80 0           my %rels;
81              
82 0           while (my ($fk, $remote_schema, $remote_table, $col, $remote_col,
83             $delete_rule, $update_rule, $is_deferrable) = $sth->fetchrow_array) {
84 0           push @{ $rels{$fk}{local_columns} }, $self->_lc($col);
  0            
85 0           push @{ $rels{$fk}{remote_columns} }, $self->_lc($remote_col);
  0            
86              
87             $rels{$fk}{remote_table} = DBIx::Class::Schema::Loader::Table->new(
88             loader => $self,
89             name => $remote_table,
90             schema => $remote_schema,
91 0 0         ) unless exists $rels{$fk}{remote_table};
92              
93             $rels{$fk}{attrs} ||= {
94             on_delete => $pg_rules{$delete_rule},
95 0   0       on_update => $pg_rules{$update_rule},
96             is_deferrable => $is_deferrable,
97             };
98             }
99              
100 0           return [ map { $rels{$_} } sort keys %rels ];
  0            
101             }
102              
103              
104             sub _table_uniq_info {
105 0     0     my ($self, $table) = @_;
106              
107             # Use the default support if available
108 0 0         return $self->next::method($table)
109             if $DBD::Pg::VERSION >= 1.50;
110              
111 0           my @uniqs;
112              
113             # Most of the SQL here is mostly based on
114             # Rose::DB::Object::Metadata::Auto::Pg, after some prodding from
115             # John Siracusa to use his superior SQL code :)
116              
117 0   0       my $attr_sth = $self->{_cache}->{pg_attr_sth} ||= $self->dbh->prepare(
118             q{SELECT attname FROM pg_catalog.pg_attribute
119             WHERE attrelid = ? AND attnum = ?}
120             );
121              
122 0   0       my $uniq_sth = $self->{_cache}->{pg_uniq_sth} ||= $self->dbh->prepare(
123             q{SELECT x.indrelid, i.relname, x.indkey
124             FROM
125             pg_catalog.pg_index x
126             JOIN pg_catalog.pg_class c ON c.oid = x.indrelid
127             JOIN pg_catalog.pg_class i ON i.oid = x.indexrelid
128             JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
129             WHERE
130             x.indisunique = 't' AND
131             x.indpred IS NULL AND
132             c.relkind = 'r' AND
133             i.relkind = 'i' AND
134             n.nspname = ? AND
135             c.relname = ?
136             ORDER BY i.relname}
137             );
138              
139 0           $uniq_sth->execute($table->schema, $table->name);
140 0           while(my $row = $uniq_sth->fetchrow_arrayref) {
141 0           my ($tableid, $indexname, $col_nums) = @$row;
142 0           $col_nums =~ s/^\s+//;
143 0           my @col_nums = split(/\s+/, $col_nums);
144 0           my @col_names;
145              
146 0           foreach (@col_nums) {
147 0           $attr_sth->execute($tableid, $_);
148 0           my $name_aref = $attr_sth->fetchrow_arrayref;
149 0 0         push(@col_names, $self->_lc($name_aref->[0])) if $name_aref;
150             }
151              
152             # skip indexes with missing column names (e.g. expression indexes)
153 0 0         if(@col_names == @col_nums) {
154 0           push(@uniqs, [ $indexname => \@col_names ]);
155             }
156             }
157              
158 0           return \@uniqs;
159             }
160              
161             sub _table_comment {
162 0     0     my $self = shift;
163 0           my ($table) = @_;
164              
165 0           my $table_comment = $self->next::method(@_);
166              
167 0 0         return $table_comment if $table_comment;
168              
169 0           ($table_comment) = $self->dbh->selectrow_array(<<'EOF', {}, $table->name, $table->schema);
170             SELECT pg_catalog.obj_description(oid)
171             FROM pg_catalog.pg_class
172             WHERE relname=? AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname=?)
173             EOF
174              
175 0           return $table_comment
176             }
177              
178              
179             sub _column_comment {
180 0     0     my $self = shift;
181 0           my ($table, $column_number, $column_name) = @_;
182              
183 0           my $column_comment = $self->next::method(@_);
184              
185 0 0         return $column_comment if $column_comment;
186              
187 0           return $self->dbh->selectrow_array(<<'EOF', {}, $column_number, $table->name, $table->schema);
188             SELECT pg_catalog.col_description(oid, ?)
189             FROM pg_catalog.pg_class
190             WHERE relname=? AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname=?)
191             EOF
192             }
193              
194             # Make sure data_type's that don't need it don't have a 'size' column_info, and
195             # set the correct precision for datetime and varbit types.
196             sub _columns_info_for {
197 0     0     my $self = shift;
198 0           my ($table) = @_;
199              
200 0           my ($result, $raw) = $self->next::method(@_);
201              
202 0           while (my ($col, $info) = each %$result) {
203 0           my $data_type = $info->{data_type};
204              
205             # these types are fixed size
206             # XXX should this be a negative match?
207 0 0 0       if ($data_type =~
    0 0        
    0 0        
    0          
    0          
    0          
    0          
208             /^(?:bigint|int8|bigserial|serial8|bool(?:ean)?|box|bytea|cidr|circle|date|double precision|float8|inet|integer|int|int4|line|lseg|macaddr|money|path|point|polygon|real|float4|smallint|int2|serial|serial4|text)\z/i) {
209 0           delete $info->{size};
210             }
211             # for datetime types, check if it has a precision or not
212             elsif ($data_type =~ /^(?:interval|time|timestamp)\b/i) {
213 0 0         if (lc($data_type) eq 'timestamp without time zone') {
    0          
214 0           $info->{data_type} = 'timestamp';
215             }
216             elsif (lc($data_type) eq 'time without time zone') {
217 0           $info->{data_type} = 'time';
218             }
219              
220 0           my ($precision) = $self->schema->storage->dbh
221             ->selectrow_array(<name, $col);
222             SELECT datetime_precision
223             FROM information_schema.columns
224             WHERE table_name = ? and column_name = ?
225             EOF
226              
227 0 0 0       if ($data_type =~ /^time\b/i) {
    0 0        
228 0 0 0       if ((not defined $precision) || $precision !~ /^\d/) {
229 0           delete $info->{size};
230             }
231             else {
232 0           my ($integer_datetimes) = $self->dbh
233             ->selectrow_array('show integer_datetimes');
234              
235 0 0         my $max_precision =
236             $integer_datetimes =~ /^on\z/i ? 6 : 10;
237              
238 0 0         if ($precision == $max_precision) {
239 0           delete $info->{size};
240             }
241             else {
242 0           $info->{size} = $precision;
243             }
244             }
245             }
246             elsif ((not defined $precision) || $precision !~ /^\d/ || $precision == 6) {
247 0           delete $info->{size};
248             }
249             else {
250 0           $info->{size} = $precision;
251             }
252             }
253             elsif ($data_type =~ /^(?:bit(?: varying)?|varbit)\z/i) {
254 0 0         $info->{data_type} = 'varbit' if $data_type =~ /var/i;
255              
256 0           my ($precision) = $self->dbh->selectrow_array(<name, $col);
257             SELECT character_maximum_length
258             FROM information_schema.columns
259             WHERE table_name = ? and column_name = ?
260             EOF
261              
262 0 0         $info->{size} = $precision if $precision;
263              
264 0 0 0       $info->{size} = 1 if (not $precision) && lc($data_type) eq 'bit';
265             }
266             elsif ($data_type =~ /^(?:numeric|decimal)\z/i && (my $size = $info->{size})) {
267 0           $size =~ s/\s*//g;
268              
269 0           my ($scale, $precision) = split /,/, $size;
270              
271 0           $info->{size} = [ $precision, $scale ];
272             }
273             elsif (lc($data_type) eq 'character varying') {
274 0           $info->{data_type} = 'varchar';
275              
276 0 0         if (not $info->{size}) {
277 0           $info->{data_type} = 'text';
278 0           $info->{original}{data_type} = 'varchar';
279             }
280             }
281             elsif (lc($data_type) eq 'character') {
282 0           $info->{data_type} = 'char';
283             }
284             # DBD::Pg < 3.5.2 can get the order wrong on Pg >= 9.1.0
285             elsif (
286             ($DBD::Pg::VERSION >= 3.005002 or $self->dbh->{pg_server_version} < 90100)
287             and
288             my $values = $raw->{$col}->{pg_enum_values}
289             ) {
290 0           $info->{extra}{list} = $values;
291              
292             # Store its original name in extra for SQLT to pick up.
293 0           $info->{extra}{custom_type_name} = $info->{data_type};
294              
295 0           $info->{data_type} = 'enum';
296              
297 0           delete $info->{size};
298             }
299             else {
300 0           my ($typetype) = $self->schema->storage->dbh
301             ->selectrow_array(<
302             SELECT typtype
303             FROM pg_catalog.pg_type
304             WHERE oid = ?::regtype
305             EOF
306 0 0 0       if ($typetype && $typetype eq 'e') {
307             # The following will extract a list of allowed values for the enum.
308 0 0         my $order_column = $self->dbh->{pg_server_version} >= 90100 ? 'enumsortorder' : 'oid';
309 0           $info->{extra}{list} = $self->dbh
310             ->selectcol_arrayref(<
311             SELECT e.enumlabel
312             FROM pg_catalog.pg_enum e
313             WHERE e.enumtypid = ?::regtype
314             ORDER BY e.$order_column
315             EOF
316              
317             # Store its original name in extra for SQLT to pick up.
318 0           $info->{extra}{custom_type_name} = $data_type;
319              
320 0           $info->{data_type} = 'enum';
321              
322 0           delete $info->{size};
323             }
324             }
325              
326 0 0         if (ref($info->{default_value}) eq 'SCALAR') {
327             # process SERIAL columns
328 0 0         if (${ $info->{default_value} } =~ /\bnextval\('([^:]+)'/i) {
  0 0          
    0          
329 0           $info->{is_auto_increment} = 1;
330 0           $info->{sequence} = $1;
331 0           delete $info->{default_value};
332             }
333             # alias now() to current_timestamp for deploying to other DBs
334 0           elsif (lc ${ $info->{default_value} } eq 'now()') {
335             # do not use a ref to a constant, that breaks Data::Dump output
336 0           ${$info->{default_value}} = 'current_timestamp';
  0            
337              
338 0           my $now = 'now()';
339 0           $info->{original}{default_value} = \$now;
340             }
341 0           elsif (${ $info->{default_value} } =~ /\bCURRENT_TIMESTAMP\b/) {
342             # PostgreSQL v10 upcases current_timestamp in default values
343 0           ${ $info->{default_value} } =~ s/\b(CURRENT_TIMESTAMP)\b/lc $1/ge;
  0            
  0            
344             }
345             }
346              
347             # detect 0/1 for booleans and rewrite
348 0 0 0       if ($data_type =~ /^bool/i && exists $info->{default_value}) {
349 0 0         if ($info->{default_value} eq '0') {
    0          
350 0           my $false = 'false';
351 0           $info->{default_value} = \$false;
352             }
353             elsif ($info->{default_value} eq '1') {
354 0           my $true = 'true';
355 0           $info->{default_value} = \$true;
356             }
357             }
358             }
359              
360 0           return $result;
361             }
362              
363             sub _view_definition {
364 0     0     my ($self, $view) = @_;
365              
366 0           my $def = $self->schema->storage->dbh->selectrow_array(<<'EOF', {}, $view->schema, $view->name);
367             SELECT pg_catalog.pg_get_viewdef(oid)
368             FROM pg_catalog.pg_class
369             WHERE relnamespace = (SELECT OID FROM pg_catalog.pg_namespace WHERE nspname = ?)
370             AND relname = ?
371             EOF
372             # The definition is returned as a complete statement including the
373             # trailing semicolon, but that's not allowed in CREATE VIEW, so
374             # strip it out
375 0           $def =~ s/\s*;\s*\z//;
376 0           return $def;
377             }
378              
379             =head1 SEE ALSO
380              
381             L, L,
382             L
383              
384             =head1 AUTHORS
385              
386             See L.
387              
388             =head1 LICENSE
389              
390             This library is free software; you can redistribute it and/or modify it under
391             the same terms as Perl itself.
392              
393             =cut
394              
395             1;