File Coverage

lib/SQL/Admin/Driver/Pg/DBI.pm
Criterion Covered Total %
statement 12 142 8.4
branch 0 36 0.0
condition 0 8 0.0
subroutine 4 27 14.8
pod 0 10 0.0
total 16 223 7.1


line stmt bran cond sub pod time code
1              
2             package SQL::Admin::Driver::Pg::DBI;
3 1     1   1747 use base qw( SQL::Admin::Driver::Base::DBI );
  1         1  
  1         86  
4              
5 1     1   5 use strict;
  1         2  
  1         32  
6 1     1   5 use warnings;
  1         2  
  1         54  
7              
8             our $VERSION = v0.5.0;
9              
10             ######################################################################
11              
12 1     1   624 use SQL::Admin::Driver::Pg::Parser;
  1         3  
  1         3204  
13              
14             ######################################################################
15              
16             our %TYPE_MAP = (
17             smallint => 'int2', int2 => 'int2',
18             integer => 'int4', int4 => 'int4',
19             bigint => 'int8', int8 => 'int8',
20             vargraphic => 'varchar',
21             varchar => 'varchar',
22             character => 'character',
23             decimal => 'decimal',
24             numeric => 'decimal',
25             float8 => 'double',
26             float4 => 'real',
27             date => 'date',
28             time => 'time',
29             timestamp => 'timestamp',
30             );
31              
32             our %TYPE_WITH_SIZE = (
33             varchar => 1,
34             character => 1,
35             vargraphic => 2,
36             decimal => 1,
37             numeric => 1,
38             );
39              
40             our %TYPE_WITH_SCALE = (
41             decimal => 1,
42             numeric => 1,
43             );
44              
45              
46             ######################################################################
47             ######################################################################
48             sub lcws ( $ ) { # ;
49 0     0 0   $_[0] =~ s/\s+$//;
50 0           $_[0] = lc $_[0];
51             }
52              
53              
54             ######################################################################
55             ######################################################################
56             sub driver { # ;
57 0     0 0   shift;
58             }
59              
60              
61             ######################################################################
62             ######################################################################
63             sub parser { # ;
64 0     0 0   my $self = shift;
65 0   0       $self->{parser} ||= SQL::Admin::Driver::Pg::Parser->new;
66             }
67              
68              
69             ######################################################################
70             ######################################################################
71             sub load { # ;
72 0     0 0   my ($self, $catalog, @schemas) = @_;
73              
74 0           local $self->{TCM};
75 0           local $self->{pg_map} = {};
76              
77 0           $self->SUPER::load ($catalog, map lc, @schemas);
78             # shift->SUPER::load (map ref ($_) ? lc : $_, @_);
79             }
80              
81              
82             ######################################################################
83             ######################################################################
84             sub _list_sequence { # ;
85 0     0     my ($self, $catalog) = @_;
86              
87 0           ();
88             }
89              
90              
91             ######################################################################
92             ######################################################################
93             sub _query_table { # ;
94 0     0     my ($self, @schemas) = @_;
95              
96 0 0         $self->sqla->select (
97             [ 'pg_class t, pg_namespace n' ],
98             [
99             'n.nspname as table_schema',
100             't.relname as table_name',
101             ],
102             {
103             'n.nspname' => ( @schemas ? \ @schemas : { 'not in' => [ 'pg_catalog', 'information_schema' ] }),
104             't.relkind' => 'r',
105             't.relnamespace' => \ ' = n.oid',
106             },
107             );
108             }
109              
110              
111             ######################################################################
112             ######################################################################
113             sub _query_table_column { # ;
114 0     0     my ($self, @schemas) = @_;
115              
116 0 0         $self->sqla->select (
117             [ join ', ', (
118             'pg_attribute c',
119             'pg_class t',
120             'pg_namespace n',
121             'pg_type y',
122             )],
123             [
124             't.oid as table_oid',
125             'n.nspname as table_schema',
126             't.relname as table_name',
127             'c.attname as column_name',
128             'y.typname as data_type',
129             'c.attnotnull as not_null',
130             'case when c.atttypmod > 0 then
131             case when y.typname = \'varchar\' then c.atttypmod - 4
132             when y.typname = \'numeric\' then atttypmod / 65536
133             end
134             end as data_type_size',
135             'case when c.atttypmod > 0 then
136             case when y.typname = \'numeric\' then atttypmod % 65536 - 4
137             end
138             end as data_type_scale',
139             'attnum as column_number'
140             ],
141             {
142             'n.nspname' => ( @schemas ? \ @schemas : { 'not in' => [ 'pg_catalog', 'information_schema' ] }),
143             't.relkind' => 'r',
144             't.relnamespace' => \ ' = n.oid',
145             'c.attrelid' => \ ' = t.oid',
146             'c.attnum' => { '>', 0 },
147             'c.atttypid' => \ ' = y.oid',
148             'c.attisdropped' => 'f',
149             },
150             [ 'table_schema, table_name, attnum' ],
151             );
152             }
153              
154              
155             ######################################################################
156             ######################################################################
157             sub _query_table_column_default { # ;
158 0     0     my ($self, @schemas) = @_;
159              
160 0           $self->sqla->select (
161             [ 'pg_attrdef'],
162             [
163             'adrelid as table_oid',
164             'adnum as column_number',
165             'adsrc as default_clause',
166             ],
167             {
168             # ignore sequences (for now)
169             'adsrc' => { 'not like' => 'nextval%' },
170             },
171             );
172             }
173              
174              
175             ######################################################################
176             ######################################################################
177             sub _query_table_column_autoincrement { # ;
178 0     0     my ($self, @schemas) = @_;
179              
180 0           $self->sqla->select (
181             [
182             'pg_depend d',
183             'pg_class S',
184             'pg_namespace N',
185             ],
186             [
187             'd.refobjid as table_oid',
188             'd.refobjsubid as column_number',
189             'N.nspname as sequence_schema',
190             'S.relname as sequence_name',
191             ],
192             {
193             'd.objid' => \ ' = S.oid',
194             'd.deptype' => 'a',
195             'S.relkind' => 'S',
196             'S.relnamespace' => \ ' = N.oid',
197             },
198             );
199             }
200              
201              
202             ######################################################################
203             ######################################################################
204             sub _query_constraint_primary_key { # ;
205 0     0     my ($self, @schemas) = @_;
206              
207 0           $self->sqla->select (
208             [ 'pg_constraint' ],
209             [
210             'conrelid as table_oid',
211             'conkey as column_list',
212             ],
213             {
214             contype => 'p',
215             },
216             );
217              
218             }
219              
220              
221             ######################################################################
222             ######################################################################
223             sub _query_constraint_unique { # ;
224 0     0     my ($self, @schemas) = @_;
225              
226 0           $self->sqla->select (
227             [ 'pg_constraint' ],
228             [
229             'conrelid as table_oid',
230             'conkey as column_list',
231             ],
232             {
233             contype => 'u',
234             },
235             );
236              
237             }
238              
239              
240             ######################################################################
241             ######################################################################
242             sub _query_constraint_foreign_key { # ;
243 0     0     my ($self, @schemas) = @_;
244 0           $_ = uc for @schemas;
245 0 0         $self->sqla->select (
246             [ 'syscat.references fk', 'syscat.keycoluse tc', 'syscat.keycoluse rc' ],
247             [
248             'fk.constname as constraint_name',
249             'fk.tabschema as table_schema',
250             'fk.tabname as table_name',
251             'tc.colname as column_name',
252             'fk.reftabschema as reftable_schema',
253             'fk.reftabname as reftable_name',
254             'rc.colname as refcolumn_name',
255             'case when fk.deleterule = \'A\' then \'no_action\'
256             when fk.deleterule = \'C\' then \'cascade\'
257             when fk.deleterule = \'N\' then \'set_null\'
258             when fk.deleterule = \'R\' then \'restrict\'
259             end as delete_rule',
260             'case when fk.updaterule = \'A\' then \'no_action\'
261             when fk.updaterule = \'C\' then \'cascade\'
262             when fk.updaterule = \'N\' then \'set_null\'
263             when fk.updaterule = \'R\' then \'restrict\'
264             end as update_rule',
265              
266             'tc.colseq as colno',
267             'fk.colcount',
268             ],
269             {
270             'fk.tabschema' => ( @schemas ? \ @schemas : { not_like => 'SYS%' }),
271             'fk.constname' => \ ' = tc.constname',
272             'fk.refkeyname' => \ ' = rc.constname',
273             'tc.colseq' => \ ' = rc.colseq',
274             },
275             [ 'fk.tabschema, fk.tabname, fk.constname, tc.colseq' ],
276             );
277             }
278              
279              
280             ######################################################################
281             ######################################################################
282             sub _query_index { # ;
283 0     0     my ($self, @schemas) = @_;
284 0           $_ = uc for @schemas;
285 0 0         $self->sqla->select (
286             [ 'syscat.indexes ix', 'syscat.indexcoluse cu' ],
287             [
288             'ix.tabschema as table_schema',
289             'ix.tabname as table_name',
290             'ix.indschema as index_schema',
291             'ix.indname as index_name',
292             'cu.colname as column_name',
293             'case when cu.colorder = \'A\' then \'ASC\'
294             when cu.colorder = \'D\' then \'DESC\'
295             when cu.colorder = \'I\' then null
296             end as column_order',
297             'case when ix.uniquerule = \'U\' then 1
298             when ix.uniquerule = \'D\' then 0
299             else null
300             end as unique',
301             'case when ix.pctfree > -1 then ix.pctfree
302             else null
303             end as hint_db2_pctfree',
304              
305             'cu.colseq as colno',
306             'ix.colcount as colcount',
307             ],
308             {
309             'ix.indschema' => ( @schemas ? \ @schemas : { not_like => 'SYS%' }),
310             'cu.indschema' => \ ' = ix.indschema',
311             'cu.indname' => \ ' = ix.indname',
312             'ix.uniquerule' => [ 'U', 'D' ],
313             'ix.user_defined' => 1,
314             },
315             [ 'ix.indschema, ix.indname, cu.colseq' ],
316             );
317             }
318              
319              
320             ######################################################################
321             ######################################################################
322             sub __load_index { # ;
323 0     0     my ($self, $catalog, @schemas) = @_;
324              
325 0           my $column_list = [];
326 0           my $sth = $self->execute ($self->_query_index (@schemas));
327 0           while (my $row = $sth->fetchrow_hashref) {
328 0           push @$column_list, [ lcws $row->{column_name}, $row->{column_order} ];
329              
330 0 0         if ($row->{colno} == $row->{colcount}) {
331 0           my $index = $catalog->add (index => (
332             schema => lcws $row->{index_schema},
333             name => lcws $row->{index_name}
334             ));
335              
336 0           $index->table ($catalog->add (table => (
337             schema => lcws $row->{table_schema},
338             name => lcws $row->{table_name}
339             )));
340              
341 0 0         $index->unique (1) if $row->{unique};
342 0           $index->column_list ($column_list);
343              
344 0           while (my ($key, $value) = each %$row) {
345 0 0         next unless defined $value;
346 0 0         $index->hint ($1, $value) if $key =~ m/^hint_(.*)/;
347             }
348              
349 0           $column_list = [];
350             }
351             }
352              
353             ##################################################################
354              
355 0           $sth->finish;
356              
357 0           ();
358             }
359              
360              
361             ######################################################################
362             ######################################################################
363             sub _load_tcm { # ;
364 0     0     my ($self, @schemas) = @_;
365              
366 0   0       $self->{TCM} ||= do {
367 0           my $map = {};
368              
369 0           my $sth = $self->execute ($self->_query_table_column (@schemas));
370 0           while (my $row = $sth->fetchrow_hashref) {
371 0           my $oid = $row->{table_oid};
372 0   0       $map->{$oid} ||= {
373             -name => lcws $row->{table_name},
374             -schema => lcws $row->{table_schema},
375             -column_list => [],
376             };
377 0           $row->{column_name} = lcws $row->{column_name};
378 0           $map->{$oid}{$row->{column_number}} = $row->{column_name};
379 0           push @{ $map->{$oid}{-column_list} }, $row;
  0            
380             }
381 0           $sth->finish;
382              
383 0           $map;
384             };
385             }
386              
387              
388             ######################################################################
389             ######################################################################
390             sub load_table { # ;
391 0     0 0   my ($self, $catalog, @schemas) = @_;
392              
393 0           my $sth = $self->execute ($self->_query_table (@schemas));
394 0           while (my $row = $sth->fetchrow_hashref) {
395 0           lcws $_ for @$row{qw{ table_schema table_name }};
396              
397 0           my $table = $catalog->add (table => (
398             schema => $row->{table_schema},
399             name => $row->{table_name},
400             ));
401              
402 0           while (my ($key, $value) = each %$row) {
403 0 0         next unless defined $value;
404              
405 0 0         $table->hint ($1, $value) if $key =~ m/^hint_(.*)/;
406 0 0         $table->info ($1, $value) if $key =~ m/^info_(.*)/;
407             }
408             }
409              
410             ##################################################################
411              
412 0           $sth->finish;
413              
414 0           ();
415             }
416              
417              
418             ######################################################################
419             ######################################################################
420             sub load_table_column { # ;
421 0     0 0   my ($self, $catalog, @schemas) = @_;
422 0           my $tcm = $self->_load_tcm (@schemas);
423              
424 0           while (my ($oid, $def) = each %$tcm) {
425 0           my $table = $catalog->add (table => (
426             schema => $def->{-schema},
427             name => $def->{-name},
428             ));
429              
430 0           for my $row (@{ $def->{-column_list} }) {
  0            
431 0           my $column = $table->add (column => (
432             name => lcws $row->{column_name}
433             ));
434              
435 0           my $type = lcws $row->{data_type};
436 0 0         die "Unknown data type: $def->{-schema}.$def->{-name}.$row->{column_name}: $type\n"
437             unless exists $TYPE_MAP{ $type };
438              
439 0           $column->type ({
440             type => $TYPE_MAP{ $type },
441             (map +(size => $_ * $row->{data_type_size}), grep $_, $TYPE_WITH_SIZE{ $type }),
442             (map +(scale => $row->{data_type_scale}), grep $_, $TYPE_WITH_SCALE{ $type }),
443             });
444              
445             ##########################################################
446              
447 0 0         $column->not_null (1)
448             if $row->{not_null};
449              
450             $column->default ($self->parser->default_clause_value ($_))
451 0           for grep defined, $row->{default};
452             }
453             }
454              
455             ##################################################################
456              
457 0           ();
458             }
459              
460              
461             ######################################################################
462             ######################################################################
463             sub load_table_column_default { # ;
464 0     0 0   my ($self, $catalog, @schemas) = @_;
465 0           my $tcm = $self->_load_tcm (@schemas);
466              
467 0           my $sth = $self->execute ($self->_query_table_column_default (@schemas));
468 0           while (my $row = $sth->fetchrow_hashref) {
469 0 0         next unless my $table = $tcm->{ $row->{table_oid} };
470              
471 0           my $column = $catalog->add (table => (
472             schema => $table->{-schema},
473             name => $table->{-name},
474             ))->add (column => (
475             name => $table->{ $row->{column_number} },
476             ));
477              
478             $column->default ($self->parser->default_clause_value ($_))
479 0           for grep defined, $row->{default_clause};
480             }
481              
482             ##################################################################
483              
484 0           $sth->finish;
485              
486 0           ();
487             }
488              
489              
490             ######################################################################
491             ######################################################################
492             sub load_table_column_autoincrement { # ;
493 0     0 0   my ($self, $catalog, @schemas) = @_;
494 0           my $tcm = $self->_load_tcm (@schemas);
495              
496 0           my $sth = $self->execute ($self->_query_table_column_autoincrement);
497 0           while (my $row = $sth->fetchrow_hashref) {
498 0 0         next unless my $table = $tcm->{ $row->{table_oid} };
499              
500 0           my $column = $catalog->add (table => (
501             schema => $table->{-schema},
502             name => $table->{-name},
503             ))->add (column => (
504             name => $table->{ $row->{column_number} },
505             ));
506              
507 0           $column->autoincrement (1);
508             }
509              
510             ##################################################################
511              
512 0           $sth->finish;
513              
514 0           ();
515             }
516              
517              
518             ######################################################################
519             ######################################################################
520             sub load_constraint_primary_key { # ;
521 0     0 0   my ($self, $catalog, @schemas) = @_;
522 0           my $tcm = $self->_load_tcm (@schemas); # tcm = table column map
523              
524 0           my $sth = $self->execute ($self->_query_constraint_primary_key);
525 0           while (my $row = $sth->fetchrow_hashref) {
526 0 0         next unless my $table = $tcm->{ $row->{table_oid} };
527              
528             # primary key is a list of column indexes
529 0           $catalog->add (table => (
530             schema => $table->{-schema},
531             name => $table->{-name},
532             ))->add (primary_key => (
533 0           column_list => [ map $table->{$_}, @{ $row->{column_list} } ],
534             ));
535             }
536              
537             ##################################################################
538              
539 0           $sth->finish;
540              
541 0           ();
542             }
543              
544              
545             ######################################################################
546             ######################################################################
547             sub load_constraint_unique { # ;
548 0     0 0   my ($self, $catalog, @schemas) = @_;
549 0           my $tcm = $self->_load_tcm (@schemas); # tcm = table column map
550              
551 0           my $sth = $self->execute ($self->_query_constraint_unique);
552 0           while (my $row = $sth->fetchrow_hashref) {
553 0 0         next unless my $table = $tcm->{ $row->{table_oid} };
554              
555             # primary key is a list of column indexes
556 0           $catalog->add (table => (
557             schema => $table->{-schema},
558             name => $table->{-name},
559             ))->add (unique => (
560 0           column_list => [ map $table->{$_}, @{ $row->{column_list} } ],
561             ));
562             }
563              
564             ##################################################################
565              
566 0           $sth->finish;
567              
568 0           ();
569             }
570              
571              
572             ######################################################################
573             ######################################################################
574             sub _load_constraint_foreign_key { # ;
575 0     0     my ($self, $catalog, @schemas) = @_;
576              
577 0           my $column_list = [];
578 0           my $refcolumn_list = [];
579 0           my $sth = $self->execute ($self->_query_constraint_foreign_key (@schemas));
580              
581 0           while (my $row = $sth->fetchrow_hashref) {
582 0           push @$column_list, lcws $row->{column_name};
583 0           push @$refcolumn_list, lcws $row->{refcolumn_name};
584              
585 0 0         if ($row->{colno} == $row->{colcount}) {
586 0           my $constr = $catalog->add (table => (
587             schema => lcws $row->{table_schema},
588             name => lcws $row->{table_name}
589             ))->add (foreign_key => (
590             name => lcws $row->{constraint_name},
591             referenced_table => $catalog->add (table => (
592             schema => lcws $row->{reftable_schema},
593             name => lcws $row->{reftable_name}
594             )),
595             referencing_column_list => $column_list,
596             referenced_column_list => $refcolumn_list,
597             update_rule => $row->{update_rule},
598             delete_rule => $row->{delete_rule},
599             ));
600              
601 0           $column_list = [];
602 0           $refcolumn_list = [];
603             };
604             }
605             }
606              
607              
608             ######################################################################
609             ######################################################################
610 0     0     sub _load_constraint_check { # ; TODO;
611             }
612              
613              
614             ######################################################################
615             ######################################################################
616              
617             package SQL::Admin::Driver::Pg::DBI;
618              
619             1;
620              
621             __END__