File Coverage

blib/lib/DBIx/Class/Schema/Loader/DBI/Pg.pm
Criterion Covered Total %
statement 12 140 8.5
branch 0 76 0.0
condition 0 37 0.0
subroutine 4 12 33.3
pod n/a
total 16 265 6.0


line stmt bran cond sub pod time code
1             package DBIx::Class::Schema::Loader::DBI::Pg;
2              
3 1     1   1056 use strict;
  1         3  
  1         31  
4 1     1   6 use warnings;
  1         2  
  1         26  
5 1     1   14 use base 'DBIx::Class::Schema::Loader::DBI::Component::QuotedDefault';
  1         11  
  1         99  
6 1     1   8 use mro 'c3';
  1         49  
  1         8  
7              
8             our $VERSION = '0.07051';
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 (
83             my (
84             $fk, $remote_schema, $remote_table, $col,
85             $remote_col, $delete_rule, $update_rule, $is_deferrable
86             )
87             = $sth->fetchrow_array
88             )
89             {
90 0           push @{ $rels{$fk}{local_columns} }, $self->_lc($col);
  0            
91 0           push @{ $rels{$fk}{remote_columns} }, $self->_lc($remote_col);
  0            
92              
93             $rels{$fk}{remote_table} = DBIx::Class::Schema::Loader::Table->new(
94             loader => $self,
95             name => $remote_table,
96             schema => $remote_schema,
97 0 0         ) unless exists $rels{$fk}{remote_table};
98              
99             $rels{$fk}{attrs} ||= {
100             on_delete => $pg_rules{$delete_rule},
101 0   0       on_update => $pg_rules{$update_rule},
102             is_deferrable => $is_deferrable,
103             };
104             }
105              
106 0           return [ map { $rels{$_} } sort keys %rels ];
  0            
107             }
108              
109             sub _table_uniq_info {
110 0     0     my ( $self, $table ) = @_;
111              
112             # Use the default support if available
113 0 0         return $self->next::method($table)
114             if $DBD::Pg::VERSION >= 1.50;
115              
116 0           my @uniqs;
117              
118             # Most of the SQL here is mostly based on
119             # Rose::DB::Object::Metadata::Auto::Pg, after some prodding from
120             # John Siracusa to use his superior SQL code :)
121              
122 0   0       my $attr_sth = $self->{_cache}->{pg_attr_sth} ||= $self->dbh->prepare(
123             q{SELECT attname FROM pg_catalog.pg_attribute
124             WHERE attrelid = ? AND attnum = ?}
125             );
126              
127 0   0       my $uniq_sth = $self->{_cache}->{pg_uniq_sth} ||= $self->dbh->prepare(
128             q{SELECT x.indrelid, i.relname, x.indkey
129             FROM
130             pg_catalog.pg_index x
131             JOIN pg_catalog.pg_class c ON c.oid = x.indrelid
132             JOIN pg_catalog.pg_class i ON i.oid = x.indexrelid
133             JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
134             WHERE
135             x.indisunique = 't' AND
136             x.indpred IS NULL AND
137             c.relkind = 'r' AND
138             i.relkind = 'i' AND
139             n.nspname = ? AND
140             c.relname = ?
141             ORDER BY i.relname}
142             );
143              
144 0           $uniq_sth->execute( $table->schema, $table->name );
145 0           while ( my $row = $uniq_sth->fetchrow_arrayref ) {
146 0           my ( $tableid, $indexname, $col_nums ) = @$row;
147 0           $col_nums =~ s/^\s+//;
148 0           my @col_nums = split( /\s+/, $col_nums );
149 0           my @col_names;
150              
151 0           foreach (@col_nums) {
152 0           $attr_sth->execute( $tableid, $_ );
153 0           my $name_aref = $attr_sth->fetchrow_arrayref;
154 0 0         push( @col_names, $self->_lc( $name_aref->[0] ) ) if $name_aref;
155             }
156              
157             # skip indexes with missing column names (e.g. expression indexes)
158 0 0         if ( @col_names == @col_nums ) {
159 0           push( @uniqs, [ $indexname => \@col_names ] );
160             }
161             }
162              
163 0           return \@uniqs;
164             }
165              
166             sub _table_comment {
167 0     0     my $self = shift;
168 0           my ($table) = @_;
169              
170 0           my $table_comment = $self->next::method(@_);
171              
172 0 0         return $table_comment if $table_comment;
173              
174 0           ($table_comment) =
175             $self->dbh->selectrow_array( <<'EOF', {}, $table->name, $table->schema );
176             SELECT pg_catalog.obj_description(oid)
177             FROM pg_catalog.pg_class
178             WHERE relname=? AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname=?)
179             EOF
180              
181 0           return $table_comment;
182             }
183              
184             sub _column_comment {
185 0     0     my $self = shift;
186 0           my ( $table, $column_number, $column_name ) = @_;
187              
188 0           my $column_comment = $self->next::method(@_);
189              
190 0 0         return $column_comment if $column_comment;
191              
192 0           return $self->dbh->selectrow_array(
193             <<'EOF', {}, $column_number, $table->name, $table->schema );
194             SELECT pg_catalog.col_description(oid, ?)
195             FROM pg_catalog.pg_class
196             WHERE relname=? AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname=?)
197             EOF
198             }
199              
200             # Make sure data_type's that don't need it don't have a 'size' column_info, and
201             # set the correct precision for datetime and varbit types.
202             sub _columns_info_for {
203 0     0     my $self = shift;
204 0           my ($table) = @_;
205              
206 0           my ( $result, $raw ) = $self->next::method(@_);
207 0           my %pkeys;
208              
209 0           while ( my ( $col, $info ) = each %$result ) {
210 0           my $data_type = $info->{data_type};
211              
212             # these types are fixed size
213             # XXX should this be a negative match?
214 0 0 0       if ( $data_type =~
    0 0        
    0 0        
    0          
    0          
    0          
    0          
215             /^(?: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
216             )
217             {
218 0           delete $info->{size};
219             }
220              
221             # for datetime types, check if it has a precision or not
222             elsif ( $data_type =~ /^(?:interval|time|timestamp)\b/i ) {
223 0 0         if ( lc($data_type) eq 'timestamp without time zone' ) {
    0          
224 0           $info->{data_type} = 'timestamp';
225             }
226             elsif ( lc($data_type) eq 'time without time zone' ) {
227 0           $info->{data_type} = 'time';
228             }
229              
230 0           my ($precision) = $self->schema->storage->dbh->selectrow_array(
231             <name, $col );
232             SELECT datetime_precision
233             FROM information_schema.columns
234             WHERE table_name = ? and column_name = ?
235             EOF
236              
237 0 0 0       if ( $data_type =~ /^time\b/i ) {
    0 0        
238 0 0 0       if ( ( not defined $precision ) || $precision !~ /^\d/ ) {
239 0           delete $info->{size};
240             }
241             else {
242 0           my ($integer_datetimes) =
243             $self->dbh->selectrow_array('show integer_datetimes');
244              
245 0 0         my $max_precision = $integer_datetimes =~ /^on\z/i ? 6 : 10;
246              
247 0 0         if ( $precision == $max_precision ) {
248 0           delete $info->{size};
249             }
250             else {
251 0           $info->{size} = $precision;
252             }
253             }
254             }
255             elsif ( ( not defined $precision )
256             || $precision !~ /^\d/
257             || $precision == 6 )
258             {
259 0           delete $info->{size};
260             }
261             else {
262 0           $info->{size} = $precision;
263             }
264             }
265             elsif ( $data_type =~ /^(?:bit(?: varying)?|varbit)\z/i ) {
266 0 0         $info->{data_type} = 'varbit' if $data_type =~ /var/i;
267              
268 0           my ($precision) =
269             $self->dbh->selectrow_array( <name, $col );
270             SELECT character_maximum_length
271             FROM information_schema.columns
272             WHERE table_name = ? and column_name = ?
273             EOF
274              
275 0 0         $info->{size} = $precision if $precision;
276              
277 0 0 0       $info->{size} = 1 if ( not $precision ) && lc($data_type) eq 'bit';
278             }
279             elsif ( $data_type =~ /^(?:numeric|decimal)\z/i
280             && ( my $size = $info->{size} ) )
281             {
282 0           $size =~ s/\s*//g;
283              
284 0           my ( $scale, $precision ) = split /,/, $size;
285              
286 0           $info->{size} = [ $precision, $scale ];
287             }
288             elsif ( lc($data_type) eq 'character varying' ) {
289 0           $info->{data_type} = 'varchar';
290              
291 0 0         if ( not $info->{size} ) {
292 0           $info->{data_type} = 'text';
293 0           $info->{original}{data_type} = 'varchar';
294             }
295             }
296             elsif ( lc($data_type) eq 'character' ) {
297 0           $info->{data_type} = 'char';
298             }
299              
300             # DBD::Pg < 3.5.2 can get the order wrong on Pg >= 9.1.0
301             elsif (
302             (
303             $DBD::Pg::VERSION >= 3.005002
304             or $self->dbh->{pg_server_version} < 90100
305             )
306             and my $values = $raw->{$col}->{pg_enum_values}
307             )
308             {
309 0           $info->{extra}{list} = $values;
310              
311             # Store its original name in extra for SQLT to pick up.
312 0           $info->{extra}{custom_type_name} = $info->{data_type};
313              
314 0           $info->{data_type} = 'enum';
315              
316 0           delete $info->{size};
317             }
318             else {
319 0           my ($typetype) =
320             $self->schema->storage->dbh->selectrow_array( <
321             SELECT typtype
322             FROM pg_catalog.pg_type
323             WHERE oid = ?::regtype
324             EOF
325 0 0 0       if ( $typetype && $typetype eq 'e' ) {
326              
327             # The following will extract a list of allowed values for the enum.
328             my $order_column =
329 0 0         $self->dbh->{pg_server_version} >= 90100 ? 'enumsortorder' : 'oid';
330             $info->{extra}{list} =
331 0           $self->dbh->selectcol_arrayref( <
332             SELECT e.enumlabel
333             FROM pg_catalog.pg_enum e
334             WHERE e.enumtypid = ?::regtype
335             ORDER BY e.$order_column
336             EOF
337              
338             # Store its original name in extra for SQLT to pick up.
339 0           $info->{extra}{custom_type_name} = $data_type;
340              
341 0           $info->{data_type} = 'enum';
342              
343 0           delete $info->{size};
344             }
345             }
346              
347 0 0         if ( ref( $info->{default_value} ) eq 'SCALAR' ) {
348              
349             # process SERIAL columns
350 0 0         if ( ${ $info->{default_value} } =~ /\bnextval\('([^:]+)'/i ) {
  0 0          
    0          
351 0           $info->{is_auto_increment} = 1;
352 0           $info->{sequence} = $1;
353 0           delete $info->{default_value};
354             }
355              
356             # alias now() to current_timestamp for deploying to other DBs
357 0           elsif ( lc ${ $info->{default_value} } eq 'now()' ) {
358              
359             # do not use a ref to a constant, that breaks Data::Dump output
360 0           ${ $info->{default_value} } = 'current_timestamp';
  0            
361              
362 0           my $now = 'now()';
363 0           $info->{original}{default_value} = \$now;
364             }
365 0           elsif ( ${ $info->{default_value} } =~ /\bCURRENT_TIMESTAMP\b/ ) {
366              
367             # PostgreSQL v10 upcases current_timestamp in default values
368 0           ${ $info->{default_value} } =~ s/\b(CURRENT_TIMESTAMP)\b/lc $1/ge;
  0            
  0            
369             }
370              
371             # if there's a default value + it's a primary key, set to retrieve the default
372             # on insert even if it's not serial specifically
373 0 0         if ( !$info->{is_auto_increment} ) {
374 0 0         %pkeys = map { $_ => 1 } @{ $self->_table_pk_info($table) }
  0            
  0            
375             unless %pkeys;
376              
377 0 0         if ( $pkeys{$col} ) {
378 0           $info->{retrieve_on_insert} = 1;
379             }
380             }
381             }
382              
383             # detect 0/1 for booleans and rewrite
384 0 0 0       if ( $data_type =~ /^bool/i && exists $info->{default_value} ) {
385 0 0         if ( $info->{default_value} eq '0' ) {
    0          
386 0           my $false = 'false';
387 0           $info->{default_value} = \$false;
388             }
389             elsif ( $info->{default_value} eq '1' ) {
390 0           my $true = 'true';
391 0           $info->{default_value} = \$true;
392             }
393             }
394             }
395              
396 0           return $result;
397             }
398              
399             sub _view_definition {
400 0     0     my ( $self, $view ) = @_;
401              
402 0           my $def = $self->schema->storage->dbh->selectrow_array(
403             <<'EOF', {}, $view->schema, $view->name );
404             SELECT pg_catalog.pg_get_viewdef(oid)
405             FROM pg_catalog.pg_class
406             WHERE relnamespace = (SELECT OID FROM pg_catalog.pg_namespace WHERE nspname = ?)
407             AND relname = ?
408             EOF
409              
410             # The definition is returned as a complete statement including the
411             # trailing semicolon, but that's not allowed in CREATE VIEW, so
412             # strip it out
413 0           $def =~ s/\s*;\s*\z//;
414 0           return $def;
415             }
416              
417             =head1 SEE ALSO
418              
419             L, L,
420             L
421              
422             =head1 AUTHORS
423              
424             See L.
425              
426             =head1 LICENSE
427              
428             This library is free software; you can redistribute it and/or modify it under
429             the same terms as Perl itself.
430              
431             =cut
432              
433             1;