File Coverage

blib/lib/Geoffrey/Converter/Pg.pm
Criterion Covered Total %
statement 107 140 76.4
branch 13 38 34.2
condition 4 9 44.4
subroutine 37 55 67.2
pod 20 20 100.0
total 181 262 69.0


line stmt bran cond sub pod time code
1             package Geoffrey::Converter::Pg;
2              
3 3     3   722821 use utf8;
  3         1114  
  3         20  
4 3     3   172 use 5.016;
  3         12  
5 3     3   24 use strict;
  3         9  
  3         69  
6 3     3   1886 use Readonly;
  3         15440  
  3         217  
7 3     3   23 use warnings;
  3         4  
  3         234  
8              
9             $Geoffrey::Converter::Pg::VERSION = '0.000204';
10              
11 3     3   421 use parent 'Geoffrey::Role::Converter';
  3         378  
  3         23  
12              
13             Readonly::Scalar my $I_CONST_LENGTH_VALUE => 2;
14             Readonly::Scalar my $I_CONST_NOT_NULL_VALUE => 3;
15             Readonly::Scalar my $I_CONST_PRIMARY_KEY_VALUE => 4;
16             Readonly::Scalar my $I_CONST_DEFAULT_VALUE => 5;
17              
18             {
19              
20             package Geoffrey::Converter::Pg::Constraints;
21              
22 3     3   51396 use parent 'Geoffrey::Role::ConverterType';
  3         7  
  3         31  
23              
24             sub new {
25 1     1   4 my $class = shift;
26 1         17 return bless $class->SUPER::new(
27             not_null => 'NOT NULL',
28             unique => 'UNIQUE',
29             primary_key => 'PRIMARY KEY',
30             foreign_key => 'FOREIGN KEY',
31             check => 'CHECK',
32             default => 'DEFAULT',
33             ), $class;
34             } ## end sub new
35             }
36             {
37              
38             package Geoffrey::Converter::Pg::View;
39              
40 3     3   83420 use parent 'Geoffrey::Role::ConverterType';
  3         5  
  3         18  
41              
42 0     0   0 sub add { return 'CREATE VIEW {0} AS {1}'; }
43              
44 0     0   0 sub drop { return 'DROP VIEW {0}'; }
45              
46             sub list {
47 0     0   0 my ( $self, $schema ) = @_;
48 0         0 return q~SELECT * FROM pg_views WHERE schemaname NOT IN('information_schema', 'pg_catalog')~;
49             }
50             }
51             {
52              
53             package Geoffrey::Converter::Pg::ForeignKey;
54 3     3   446 use parent 'Geoffrey::Role::ConverterType';
  3         7  
  3         11  
55 0     0   0 sub add { return 'FOREIGN KEY ({0}) REFERENCES {1}({2})' }
56              
57             sub list {
58 0     0   0 return q~SELECT
59             source_table::regclass,
60             source_attr.attname AS source_column,
61             target_table::regclass,
62             target_attr.attname AS target_column
63             FROM
64             pg_attribute target_attr,
65             pg_attribute source_attr,
66             (
67             SELECT
68             source_table,
69             target_table,
70             source_constraints[i] AS source_constraints,
71             target_constraints[i] AS target_constraints
72             FROM (
73             SELECT
74             conrelid as source_table,
75             confrelid AS target_table,
76             conkey AS source_constraints,
77             confkey AS target_constraints,
78             generate_series(1, array_upper(conkey, 1)) AS i
79             FROM
80             pg_constraint
81             WHERE
82             contype = 'f'
83             ) query1
84             ) query2
85             WHERE
86             target_attr.attnum = target_constraints
87             AND target_attr.attrelid = target_table
88             AND source_attr.attnum = source_constraints
89             AND source_attr.attrelid = source_table~;
90             } ## end sub list
91             }
92             {
93              
94             package Geoffrey::Converter::Pg::Sequence;
95 3     3   485 use parent 'Geoffrey::Role::ConverterType';
  3         6  
  3         19  
96 0     0   0 sub add { return 'CREATE SEQUENCE {0} INCREMENT {1} MINVALUE {2} MAXVALUE {3} START {4} CACHE {5}' }
97 0     0   0 sub nextval { return q~DEFAULT nextval('{0}'::regclass~ }
98             }
99              
100             {
101              
102             package Geoffrey::Converter::Pg::PrimaryKey;
103 3     3   344 use parent 'Geoffrey::Role::ConverterType';
  3         8  
  3         10  
104 0     0   0 sub add { return 'CONSTRAINT {0} PRIMARY KEY ( {1} )'; }
105              
106             sub list {
107 0     0   0 return q~SELECT
108             tc.table_schema,
109             tc.table_name,
110             kc.column_name,
111             kc.constraint_name
112             FROM
113             information_schema.table_constraints tc,
114             information_schema.key_column_usage kc
115             WHERE
116             tc.constraint_type = 'PRIMARY KEY'
117             AND kc.table_name = tc.table_name
118             AND kc.table_schema = tc.table_schema
119             AND kc.constraint_name = tc.constraint_name~;
120             } ## end sub list
121             }
122             {
123              
124             package Geoffrey::Converter::Pg::UniqueIndex;
125 3     3   412 use parent 'Geoffrey::Role::ConverterType';
  3         6  
  3         10  
126 0     0   0 sub append { return 'CREATE UNIQUE INDEX IF NOT EXISTS {0} ON {1} ( {2} )'; }
127 0     0   0 sub add { return 'CONSTRAINT {0} UNIQUE ( {1} )'; }
128 0     0   0 sub drop { return 'DROP INDEX IF EXISTS {1}'; }
129              
130             sub list {
131 0     0   0 list => q~SELECT
132             U.usename AS user_name,
133             ns.nspname AS schema_name,
134             idx.indrelid :: REGCLASS AS table_name,
135             i.relname AS index_name,
136             am.amname AS index_type,
137             idx.indkey,
138             ARRAY(
139             SELECT
140             pg_get_indexdef(idx.indexrelid, k + 1, TRUE)
141             FROM
142             generate_subscripts(idx.indkey, 1) AS k
143             ORDER BY k
144             ) AS index_keys,
145             (idx.indexprs IS NOT NULL) OR (idx.indkey::int[] @> array[0]) AS is_functional,
146             idx.indpred IS NOT NULL AS is_partial
147             FROM
148             pg_index AS idx
149             JOIN pg_class AS i ON i.oid = idx.indexrelid
150             JOIN pg_am AS am ON i.relam = am.oid
151             JOIN pg_namespace AS NS ON i.relnamespace = NS.OID
152             JOIN pg_user AS U ON i.relowner = U.usesysid
153             WHERE
154             NOT nspname LIKE 'pg%'
155             AND NOT idx.indisprimary
156             AND idx.indisunique;~;
157             } ## end sub list
158             }
159             {
160              
161             package Geoffrey::Converter::Pg::Function;
162 3     3   540 use parent 'Geoffrey::Role::ConverterType';
  3         6  
  3         14  
163 0     0   0 sub add { return q~CREATE FUNCTION {0}({1}) RETURNS {2} AS ' {3} ' LANGUAGE {4} VOLATILE COST {5}~; }
164 0     0   0 sub drop { return 'DROP FUNCTION {0} ({1})'; }
165              
166             sub list {
167 0     0   0 list => q~SELECT n.nspname as "Schema",
168             p.proname as "Name",
169             p.prosrc,
170             p.procost,
171             pg_catalog.pg_get_function_result(p.oid) as result_data_type,
172             pg_catalog.pg_get_function_arguments(p.oid) as argument_data_types,
173             CASE
174             WHEN p.proisagg THEN 'agg'
175             WHEN p.proiswindow THEN 'window'
176             WHEN p.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype THEN 'trigger'
177             ELSE
178             'normal'
179             END as
180             function_type
181             FROM
182             pg_catalog.pg_proc p
183             LEFT JOIN pg_catalog.pg_namespace n
184             ON ( n.oid = p.pronamespace )
185             WHERE
186             pg_catalog.pg_function_is_visible( p.oid )
187             AND n.nspname <> 'pg_catalog'
188             AND n.nspname <> 'information_schema'~;
189             } ## end sub list
190             }
191             {
192              
193             package Geoffrey::Converter::Pg::Trigger;
194 3     3   460 use parent 'Geoffrey::Role::ConverterType';
  3         6  
  3         12  
195              
196             sub add {
197 0     0   0 my ( $self, $options ) = @_;
198 0         0 my $s_sql_standard = <<'EOF';
199             CREATE TRIGGER {0} UPDATE OF {1} ON {2}
200             BEGIN
201             {4}
202             END
203             EOF
204 0         0 my $s_sql_view = <<'EOF';
205             CREATE TRIGGER {0} INSTEAD OF UPDATE OF {1} ON {2}
206             BEGIN
207             {4}
208             END
209             EOF
210 0 0       0 return $options->{for_view} ? $s_sql_view : $s_sql_standard;
211             } ## end sub add
212              
213 0     0   0 sub drop { return 'DROP TRIGGER IF EXISTS {1}'; }
214             }
215              
216             sub new {
217 2     2 1 7847 my $class = shift;
218 2         21 my $self = $class->SUPER::new(@_);
219 2         28 $self->{min_version} = '9.1';
220 2         6 return bless $self, $class;
221             } ## end sub new
222              
223             sub defaults {
224 1     1 1 16332 return { current_timestamp => 'CURRENT_TIMESTAMP', autoincrement => 'SERIAL', };
225             }
226              
227             sub type {
228 2     2 1 12209 my ( $self, $hr_column_params ) = @_;
229 2 50 33     13 if ( $hr_column_params->{default} && $hr_column_params->{default} eq 'autoincrement' ) {
230 0 0       0 $hr_column_params->{type} = lc $hr_column_params->{type} eq 'bigint' ? 'bigserial' : lc $hr_column_params->{type} eq 'smallint' ? 'smallserial' : 'serial';
    0          
231 0         0 $hr_column_params->{default} = '';
232             }
233 2         20 return $self->SUPER::type($hr_column_params);
234             } ## end sub type
235              
236             sub types {
237             return {
238 2     2 1 1371 abstime => 'abstime',
239             aclitem => 'aclitem',
240             bigint => 'bigint',
241             bigserial => 'bigserial',
242             bit => 'bit',
243             var_bit => 'bit varying',
244             bool => 'boolean',
245             box => 'box',
246             bytea => 'bytea',
247             char => '"char"',
248             character => 'character',
249             varchar => 'character varying',
250             cid => 'cid',
251             cidr => 'cidr',
252             circle => 'circle',
253             date => 'date',
254             daterange => 'daterange',
255             decimal => 'decimal',
256             double_precision => 'double precision',
257             float => 'float',
258             gtsvector => 'gtsvector',
259             inet => 'inet',
260             int2vector => 'int2vector',
261             int4range => 'int4range',
262             int8range => 'int8range',
263             integer => 'integer',
264             interval => 'interval',
265             json => 'json',
266             line => 'line',
267             lseg => 'lseg',
268             macaddr => 'macaddr',
269             money => 'money',
270             name => 'name',
271             numeric => 'numeric',
272             numrange => 'numrange',
273             oid => 'oid',
274             oidvector => 'oidvector',
275             path => 'path',
276             pg_node_tree => 'pg_node_tree',
277             point => 'point',
278             polygon => 'polygon',
279             real => 'real',
280             refcursor => 'refcursor',
281             regclass => 'regclass',
282             regconfig => 'regconfig',
283             regdictionary => 'regdictionary',
284             regoper => 'regoper',
285             regoperator => 'regoperator',
286             regproc => 'regproc',
287             regprocedure => 'regprocedure',
288             regtype => 'regtype',
289             reltime => 'reltime',
290             serial => 'serial',
291             smallint => 'smallint',
292             smallserial => 'smallserial',
293             smgr => 'smgr',
294             text => 'text',
295             tid => 'tid',
296             timestamp => 'timestamp without time zone',
297             timestamp_tz => 'timestamp with time zone',
298             time => 'time without time zone',
299             time_tz => 'time with time zone',
300             tinterval => 'tinterval',
301             tsquery => 'tsquery',
302             tsrange => 'tsrange',
303             tstzrange => 'tstzrange',
304             tsvector => 'tsvector',
305             txid_snapshot => 'txid_snapshot',
306             uuid => 'uuid',
307             xid => 'xid',
308             xml => 'xml',
309             };
310             } ## end sub types
311              
312             sub select_get_table {
313 1     1 1 5 return q~SELECT t.table_name AS table_name FROM information_schema.tables t WHERE t.table_type = 'BASE TABLE' AND t.table_schema = ? AND t.table_name = ?~;
314             }
315              
316             sub convert_defaults {
317 2     2 1 7 my ( $self, $params ) = @_;
318 2 100       12 $params->{default} ? $params->{default} =~ s/^'(.*)'$/$1/ : undef;
319 2 100 66     14 if ( $params->{default} && $params->{type} eq 'bit' ) {
320 1         8 return qq~$params->{default}::bit~;
321             }
322 1         5 return $params->{default};
323             } ## end sub convert_defaults
324              
325             sub parse_default {
326 1     1 1 4 my ( $self, $default_value ) = @_;
327 1 50       8 return $1 * 1 if ( $default_value =~ m/\w+\s*(?:\((\d+)\))::(.*)(?:\;|\s)/ );
328 1         6 return $default_value;
329             }
330              
331 1     1 1 6 sub can_create_empty_table { return 1 }
332              
333             sub colums_information {
334 1     1 1 4 my ( $self, $ar_raw_data ) = @_;
335 1 50       2 return [] if scalar @{$ar_raw_data} == 0;
  1         6  
336 1         3 my $table_row = shift @{$ar_raw_data};
  1         3  
337 1         13 $table_row->{sql} =~ s/^.*(CREATE|create) .*\(//g;
338 1         4 my $columns = [];
339 1         7 for ( split m/,/, $table_row->{sql} ) {
340 7         147 s/^\s*(.*)\s*$/$1/g;
341 7         28 my $rx_not_null = 'NOT NULL';
342 7         13 my $rx_primary_key = 'PRIMARY KEY';
343 7         12 my $rx_default = 'SERIAL|DEFAULT';
344 7         133 my $rx_column_values = qr/($rx_not_null)*\s($rx_primary_key)*.*($rx_default \w{1,})*/;
345 7         185 my @column = m/^(\w+)\s([[:upper:]]+)(\(\d*\))*\s$rx_column_values$/;
346 7 50       31 next if scalar @column == 0;
347 0 0       0 $column[$I_CONST_LENGTH_VALUE] =~ s/([\(\)])//g if $column[$I_CONST_LENGTH_VALUE];
348 0 0       0 push @{$columns},
  0 0       0  
    0          
    0          
349             {
350             name => $column[0],
351             type => $column[1],
352             ( $column[$I_CONST_LENGTH_VALUE] ? ( length => $column[$I_CONST_LENGTH_VALUE] ) : () ),
353             ( $column[$I_CONST_NOT_NULL_VALUE] ? ( not_null => $column[$I_CONST_NOT_NULL_VALUE] ) : () ),
354             ( $column[$I_CONST_PRIMARY_KEY_VALUE] ? ( primary_key => $column[$I_CONST_PRIMARY_KEY_VALUE] ) : () ),
355             ( $column[$I_CONST_DEFAULT_VALUE] ? ( default => $column[$I_CONST_DEFAULT_VALUE] ) : () ),
356             };
357             } ## end for ( split m/,/, $table_row...)
358 1         18 return $columns;
359             } ## end sub colums_information
360              
361             sub index_information {
362 1     1 1 1023 my ( $self, $ar_raw_data ) = @_;
363 1         3 my @mapped = ();
364 1         3 for ( @{$ar_raw_data} ) {
  1         3  
365 0 0       0 next if !$_->{sql};
366 0         0 my ($s_columns) = $_->{sql} =~ m/\((.*)\)$/;
367 0         0 my @columns = split m/,/, $s_columns;
368 0         0 s/^\s+|\s+$//g for @columns;
369 0         0 push @mapped, { name => $_->{name}, table => $_->{tbl_name}, columns => \@columns };
370             } ## end for ( @{$ar_raw_data} )
371 1         7 return \@mapped;
372             } ## end sub index_information
373              
374             sub view_information {
375 1     1 1 1013 my ( $self, $ar_raw_data ) = @_;
376 1 50       6 return [] unless $ar_raw_data;
377 1         2 return [ map { { name => $_->{name}, sql => $_->{sql} } } @{$ar_raw_data} ];
  0         0  
  1         8  
378             }
379              
380             sub constraints {
381 1     1 1 1064 return shift->_get_value( 'constraints', 'Geoffrey::Converter::Pg::Constraints', 1 );
382             }
383              
384             sub index {
385 1     1 1 4 my ( $self, $new_value ) = @_;
386 1 50       5 $self->{index} = $new_value if defined $new_value;
387 1         8 return $self->_get_value( 'index', 'Geoffrey::Converter::Pg::Index' );
388             }
389              
390             sub table {
391 1     1 1 4 return shift->_get_value( 'table', 'Geoffrey::Converter::Pg::Tables' );
392             }
393              
394             sub view {
395 1     1 1 5 return shift->_get_value( 'view', 'Geoffrey::Converter::Pg::View', 1 );
396             }
397              
398             sub foreign_key {
399 1     1 1 4 my ( $self, $new_value ) = @_;
400 1 50       6 $self->{foreign_key} = $new_value if defined $new_value;
401 1         4 return $self->_get_value( 'foreign_key', 'Geoffrey::Converter::Pg::ForeignKey', 1 );
402             }
403              
404             sub trigger {
405 1     1 1 5 return shift->_get_value( 'trigger', 'Geoffrey::Converter::Pg::Trigger', 1 );
406             }
407              
408             sub primary_key {
409 1     1 1 5 return shift->_get_value( 'primary_key', 'Geoffrey::Converter::Pg::PrimaryKey', 1 );
410             }
411              
412             sub unique {
413 1     1 1 5 return shift->_get_value( 'unique', 'Geoffrey::Converter::Pg::UniqueIndex', 1 );
414             }
415              
416             sub sequence {
417 1     1 1 7 return shift->_get_value( 'sequence', 'Geoffrey::Converter::Pg::Sequence', 1 );
418             }
419              
420             sub _get_value {
421 9     9   34 my ( $self, $key, $s_package_name, $b_ignore_require ) = @_;
422 9   33     56 $self->{$key} //= $self->_set_value( $key, $s_package_name, $b_ignore_require );
423 9         138 return $self->{$key};
424             }
425              
426             sub _set_value {
427 9     9   22 my ( $self, $key, $s_package_name, $b_ignore_require ) = @_;
428 9         79 require Geoffrey::Utils;
429 9 100       113 $self->{$key} = $b_ignore_require ? $s_package_name->new(@_) : Geoffrey::Utils::obj_from_name($s_package_name);
430 9         208 return $self->{$key};
431              
432             } ## end sub _set_value
433              
434             1; # End of Geoffrey::Converter::Pg
435              
436             __END__