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   67830 use utf8;
  3         44  
  3         15  
4 3     3   166 use 5.016;
  3         11  
5 3     3   16 use strict;
  3         7  
  3         58  
6 3     3   1522 use Readonly;
  3         11383  
  3         156  
7 3     3   22 use warnings;
  3         7  
  3         133  
8              
9             $Geoffrey::Converter::Pg::VERSION = '0.000203';
10              
11 3     3   1292 use parent 'Geoffrey::Role::Converter';
  3         881  
  3         17  
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   18198 use parent 'Geoffrey::Role::ConverterType';
  3         6  
  3         17  
23              
24             sub new {
25 1     1   2 my $class = shift;
26 1         11 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             }
35             }
36             {
37              
38             package Geoffrey::Converter::Pg::View;
39              
40 3     3   46107 use parent 'Geoffrey::Role::ConverterType';
  3         7  
  3         17  
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   371 use parent 'Geoffrey::Role::ConverterType';
  3         7  
  3         44  
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             }
91             }
92             {
93              
94             package Geoffrey::Converter::Pg::Sequence;
95 3     3   331 use parent 'Geoffrey::Role::ConverterType';
  3         6  
  3         62  
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   352 use parent 'Geoffrey::Role::ConverterType';
  3         7  
  3         29  
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             }
121             }
122             {
123              
124             package Geoffrey::Converter::Pg::UniqueIndex;
125 3     3   353 use parent 'Geoffrey::Role::ConverterType';
  3         7  
  3         11  
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             }
158             }
159             {
160              
161             package Geoffrey::Converter::Pg::Function;
162 3     3   431 use parent 'Geoffrey::Role::ConverterType';
  3         54  
  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             }
190             }
191             {
192              
193             package Geoffrey::Converter::Pg::Trigger;
194 3     3   392 use parent 'Geoffrey::Role::ConverterType';
  3         6  
  3         10  
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             }
212              
213 0     0   0 sub drop { return 'DROP TRIGGER IF EXISTS {1}'; }
214             }
215              
216             sub new {
217 2     2 1 6793 my $class = shift;
218 2         18 my $self = $class->SUPER::new(@_);
219 2         22 $self->{min_version} = '9.1';
220 2         6 return bless $self, $class;
221             }
222              
223             sub defaults {
224 1     1 1 8525 return {current_timestamp => 'CURRENT_TIMESTAMP', autoincrement => 'SERIAL',};
225             }
226              
227             sub type {
228 2     2 1 8378 my ($self, $hr_column_params) = @_;
229 2 50 33     10 if ($hr_column_params->{default} && $hr_column_params->{default} eq 'autoincrement') {
230             $hr_column_params->{type}
231             = lc $hr_column_params->{type} eq 'bigint' ? 'bigserial'
232 0 0       0 : lc $hr_column_params->{type} eq 'smallint' ? 'smallserial'
    0          
233             : 'serial';
234 0         0 delete $hr_column_params->{default};
235             }
236 2         12 return $self->SUPER::type($hr_column_params);
237             }
238              
239             sub types {
240             return {
241 2     2 1 855 abstime => 'abstime',
242             aclitem => 'aclitem',
243             bigint => 'bigint',
244             bigserial => 'bigserial',
245             bit => 'bit',
246             var_bit => 'bit varying',
247             bool => 'boolean',
248             box => 'box',
249             bytea => 'bytea',
250             char => '"char"',
251             character => 'character',
252             varchar => 'character varying',
253             cid => 'cid',
254             cidr => 'cidr',
255             circle => 'circle',
256             date => 'date',
257             daterange => 'daterange',
258             decimal => 'decimal',
259             double_precision => 'double precision',
260             gtsvector => 'gtsvector',
261             inet => 'inet',
262             int2vector => 'int2vector',
263             int4range => 'int4range',
264             int8range => 'int8range',
265             integer => 'integer',
266             interval => 'interval',
267             json => 'json',
268             line => 'line',
269             lseg => 'lseg',
270             macaddr => 'macaddr',
271             money => 'money',
272             name => 'name',
273             numeric => 'numeric',
274             numrange => 'numrange',
275             oid => 'oid',
276             oidvector => 'oidvector',
277             path => 'path',
278             pg_node_tree => 'pg_node_tree',
279             point => 'point',
280             polygon => 'polygon',
281             real => 'real',
282             refcursor => 'refcursor',
283             regclass => 'regclass',
284             regconfig => 'regconfig',
285             regdictionary => 'regdictionary',
286             regoper => 'regoper',
287             regoperator => 'regoperator',
288             regproc => 'regproc',
289             regprocedure => 'regprocedure',
290             regtype => 'regtype',
291             reltime => 'reltime',
292             serial => 'serial',
293             smallint => 'smallint',
294             smallserial => 'smallserial',
295             smgr => 'smgr',
296             text => 'text',
297             tid => 'tid',
298             timestamp => 'timestamp without time zone',
299             timestamp_tz => 'timestamp with time zone',
300             time => 'time without time zone',
301             time_tz => 'time with time zone',
302             tinterval => 'tinterval',
303             tsquery => 'tsquery',
304             tsrange => 'tsrange',
305             tstzrange => 'tstzrange',
306             tsvector => 'tsvector',
307             txid_snapshot => 'txid_snapshot',
308             uuid => 'uuid',
309             xid => 'xid',
310             xml => 'xml',
311             };
312             }
313              
314             sub select_get_table {
315             return
316 1     1 1 5 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 = ?~;
317             }
318              
319             sub convert_defaults {
320 2     2 1 5 my ($self, $params) = @_;
321 2 100       8 $params->{default} ? $params->{default} =~ s/^'(.*)'$/$1/ : undef;
322 2 100 66     14 if ($params->{default} && $params->{type} eq 'bit') {
323 1         7 return qq~$params->{default}::bit~;
324             }
325 1         4 return $params->{default};
326             }
327              
328             sub parse_default {
329 1     1 1 4 my ($self, $default_value) = @_;
330 1 50       6 return $1 * 1 if ($default_value =~ m/\w+\s*(?:\((\d+)\))::(.*)(?:\;|\s)/);
331 1         5 return $default_value;
332             }
333              
334 1     1 1 51 sub can_create_empty_table { return 1 }
335              
336             sub colums_information {
337 1     1 1 3 my ($self, $ar_raw_data) = @_;
338 1 50       2 return [] if scalar @{$ar_raw_data} == 0;
  1         5  
339 1         2 my $table_row = shift @{$ar_raw_data};
  1         2  
340 1         10 $table_row->{sql} =~ s/^.*(CREATE|create) .*\(//g;
341 1         3 my $columns = [];
342 1         5 for (split m/,/, $table_row->{sql}) {
343 7         93 s/^\s*(.*)\s*$/$1/g;
344 7         14 my $rx_not_null = 'NOT NULL';
345 7         10 my $rx_primary_key = 'PRIMARY KEY';
346 7         8 my $rx_default = 'SERIAL|DEFAULT';
347 7         65 my $rx_column_values = qr/($rx_not_null)*\s($rx_primary_key)*.*($rx_default \w{1,})*/;
348 7         109 my @column = m/^(\w+)\s([[:upper:]]+)(\(\d*\))*\s$rx_column_values$/;
349 7 50       46 next if scalar @column == 0;
350 0 0       0 $column[$I_CONST_LENGTH_VALUE] =~ s/([\(\)])//g if $column[$I_CONST_LENGTH_VALUE];
351 0 0       0 push @{$columns},
  0 0       0  
    0          
    0          
352             {
353             name => $column[0],
354             type => $column[1],
355             ($column[$I_CONST_LENGTH_VALUE] ? (length => $column[$I_CONST_LENGTH_VALUE]) : ()),
356             ($column[$I_CONST_NOT_NULL_VALUE] ? (not_null => $column[$I_CONST_NOT_NULL_VALUE]) : ()),
357             ($column[$I_CONST_PRIMARY_KEY_VALUE] ? (primary_key => $column[$I_CONST_PRIMARY_KEY_VALUE]) : ()),
358             ($column[$I_CONST_DEFAULT_VALUE] ? (default => $column[$I_CONST_DEFAULT_VALUE]) : ()),
359             };
360             }
361 1         10 return $columns;
362             }
363              
364             sub index_information {
365 1     1 1 635 my ($self, $ar_raw_data) = @_;
366 1         3 my @mapped = ();
367 1         2 for (@{$ar_raw_data}) {
  1         3  
368 0 0       0 next if !$_->{sql};
369 0         0 my ($s_columns) = $_->{sql} =~ m/\((.*)\)$/;
370 0         0 my @columns = split m/,/, $s_columns;
371 0         0 s/^\s+|\s+$//g for @columns;
372 0         0 push @mapped, {name => $_->{name}, table => $_->{tbl_name}, columns => \@columns};
373             }
374 1         5 return \@mapped;
375             }
376              
377             sub view_information {
378 1     1 1 614 my ($self, $ar_raw_data) = @_;
379 1 50       5 return [] unless $ar_raw_data;
380 1         2 return [map { {name => $_->{name}, sql => $_->{sql}} } @{$ar_raw_data}];
  0         0  
  1         5  
381             }
382              
383             sub constraints {
384 1     1 1 616 return shift->_get_value('constraints', 'Geoffrey::Converter::Pg::Constraints', 1);
385             }
386              
387             sub index {
388 1     1 1 3 my ($self, $new_value) = @_;
389 1 50       5 $self->{index} = $new_value if defined $new_value;
390 1         2 return $self->_get_value('index', 'Geoffrey::Converter::Pg::Index');
391             }
392              
393             sub table {
394 1     1 1 4 return shift->_get_value('table', 'Geoffrey::Converter::Pg::Tables');
395             }
396              
397             sub view {
398 1     1 1 4 return shift->_get_value('view', 'Geoffrey::Converter::Pg::View', 1);
399             }
400              
401             sub foreign_key {
402 1     1 1 4 my ($self, $new_value) = @_;
403 1 50       4 $self->{foreign_key} = $new_value if defined $new_value;
404 1         17 return $self->_get_value('foreign_key', 'Geoffrey::Converter::Pg::ForeignKey', 1);
405             }
406              
407             sub trigger {
408 1     1 1 5 return shift->_get_value('trigger', 'Geoffrey::Converter::Pg::Trigger', 1);
409             }
410              
411             sub primary_key {
412 1     1 1 4 return shift->_get_value('primary_key', 'Geoffrey::Converter::Pg::PrimaryKey', 1);
413             }
414              
415             sub unique {
416 1     1 1 3 return shift->_get_value('unique', 'Geoffrey::Converter::Pg::UniqueIndex', 1);
417             }
418              
419             sub sequence {
420 1     1 1 4 return shift->_get_value('sequence', 'Geoffrey::Converter::Pg::Sequence', 1);
421             }
422              
423             sub _get_value {
424 9     9   22 my ($self, $key, $s_package_name, $b_ignore_require) = @_;
425 9   33     44 $self->{$key} //= $self->_set_value($key, $s_package_name, $b_ignore_require);
426 9         87 return $self->{$key};
427             }
428              
429             sub _set_value {
430 9     9   17 my ($self, $key, $s_package_name, $b_ignore_require) = @_;
431 9         52 require Geoffrey::Utils;
432 9 100       74 $self->{$key} = $b_ignore_require ? $s_package_name->new(@_) : Geoffrey::Utils::obj_from_name($s_package_name);
433 9         153 return $self->{$key};
434              
435             }
436              
437             1; # End of Geoffrey::Converter::Pg
438              
439             __END__