File Coverage

lib/UR/DataSource/Pg.pm
Criterion Covered Total %
statement 28 90 31.1
branch 9 36 25.0
condition 3 14 21.4
subroutine 6 21 28.5
pod 0 10 0.0
total 46 171 26.9


line stmt bran cond sub pod time code
1             package UR::DataSource::Pg;
2 3     3   95 use strict;
  3         4  
  3         74  
3 3     3   9 use warnings;
  3         3  
  3         3021  
4              
5             require UR;
6             our $VERSION = "0.46"; # UR $VERSION;
7              
8             UR::Object::Type->define(
9             class_name => 'UR::DataSource::Pg',
10             is => ['UR::DataSource::RDBMS'],
11             is_abstract => 1,
12             );
13              
14             # RDBMS API
15              
16 0     0 0 0 sub driver { "Pg" }
17              
18             #sub server {
19             # my $self = shift->_singleton_object();
20             # $self->_init_database;
21             # return $self->_database_file_path;
22             #}
23              
24 0     0 0 0 sub owner { shift->_singleton_object->login }
25              
26             #sub login {
27             # undef
28             #}
29             #
30             #sub auth {
31             # undef
32             #}
33              
34 3     3   12 sub _default_sql_like_escape_string { return '\\\\' };
35              
36             sub _format_sql_like_escape_string {
37 3     3   8 my $class = shift;
38 3         6 my $escape = shift;
39 3         8 return "E'$escape'";
40             }
41              
42 0     0 0 0 sub can_savepoint { 1;}
43              
44             sub set_savepoint {
45 0     0 0 0 my($self,$sp_name) = @_;
46              
47 0         0 my $dbh = $self->get_default_handle;
48 0         0 $dbh->pg_savepoint($sp_name);
49             }
50              
51             sub rollback_to_savepoint {
52 0     0 0 0 my($self,$sp_name) = @_;
53              
54 0         0 my $dbh = $self->get_default_handle;
55 0         0 $dbh->pg_rollback_to($sp_name);
56             }
57              
58              
59             *_init_created_dbh = \&init_created_handle;
60             sub init_created_handle
61             {
62 0     0 0 0 my ($self, $dbh) = @_;
63 0 0       0 return unless defined $dbh;
64 0         0 $dbh->{LongTruncOk} = 0;
65 0         0 return $dbh;
66             }
67              
68             sub _ignore_table {
69 0     0   0 my $self = shift;
70 0         0 my $table_name = shift;
71 0 0       0 return 1 if $table_name =~ /^(pg_|sql_)/;
72             }
73              
74              
75             sub _get_next_value_from_sequence {
76 0     0   0 my($self,$sequence_name) = @_;
77              
78             # we may need to change how this db handle is gotten
79 0         0 my $dbh = $self->get_default_handle;
80 0         0 my($new_id) = $dbh->selectrow_array("SELECT nextval('$sequence_name')");
81              
82 0 0       0 if ($dbh->err) {
83 0         0 die "Failed to prepare SQL to generate a column id from sequence: $sequence_name.\n" . $dbh->errstr . "\n";
84 0         0 return;
85             }
86              
87 0         0 return $new_id;
88             }
89              
90             # The default for PostgreSQL's serial datatype is to create a sequence called
91             # tablename_columnname_seq
92             sub _get_sequence_name_for_table_and_column {
93 0     0   0 my($self,$table_name, $column_name) = @_;
94 0         0 return sprintf("%s_%s_seq",$table_name, $column_name);
95             }
96              
97              
98             sub get_bitmap_index_details_from_data_dictionary {
99             # FIXME Postgres has bitmap indexes, but we don't support them yet. See the Oracle
100             # datasource module for details about how to get it working
101 0     0 0 0 return [];
102             }
103              
104              
105             sub get_unique_index_details_from_data_dictionary {
106 0     0 0 0 my($self, $owner_name, $table_name) = @_;
107              
108 0         0 my $sql = qq(
109             SELECT c_index.relname, a.attname
110             FROM pg_catalog.pg_class c_table
111             JOIN pg_catalog.pg_index i ON i.indrelid = c_table.oid
112             JOIN pg_catalog.pg_class c_index ON c_index.oid = i.indexrelid
113             JOIN pg_catalog.pg_attribute a ON a.attrelid = c_index.oid
114             JOIN pg_catalog.pg_namespace n ON c_table.relnamespace = n.oid
115             WHERE c_table.relname = ? AND n.nspname = ?
116             and (i.indisunique = 't' or i.indisprimary = 't')
117             and i.indisvalid = 't'
118             );
119            
120 0         0 my $dbh = $self->get_default_handle();
121 0 0       0 return undef unless $dbh;
122              
123 0         0 my $sth = $dbh->prepare($sql);
124 0 0       0 return undef unless $sth;
125              
126             #my $db_owner = $self->owner(); # We should probably do something with the owner/schema
127 0         0 $sth->execute($table_name, $owner_name);
128              
129 0         0 my $ret;
130 0         0 while (my $data = $sth->fetchrow_hashref()) {
131 0   0     0 $ret->{$data->{'relname'}} ||= [];
132 0         0 push @{ $ret->{ $data->{'relname'} } }, $data->{'attname'};
  0         0  
133             }
134              
135 0         0 return $ret;
136             }
137              
138             my %ur_data_type_for_vendor_data_type = (
139             # DB type UR Type
140             'SMALLINT' => ['Integer', undef],
141             'BIGINT' => ['Integer', undef],
142             'SERIAL' => ['Integer', undef],
143             'TEXT' => ['Text', undef],
144             'BYTEA' => ['Blob', undef],
145             'CHARACTER VARYING' => ['Text', undef],
146             'TIMESTAMP WITHOUT TIME ZONE' => ['DateTime', undef],
147             'NUMERIC' => ['Number', undef],
148              
149             'DOUBLE PRECISION' => ['Number', undef],
150             );
151             sub ur_data_type_for_data_source_data_type {
152 0     0 0 0 my($class,$type) = @_;
153              
154 0         0 $type = $class->normalize_vendor_type($type);
155 0         0 my $urtype = $ur_data_type_for_vendor_data_type{$type};
156 0 0       0 unless (defined $urtype) {
157 0         0 $urtype = $class->SUPER::ur_data_type_for_data_source_data_type($type);
158             }
159 0         0 return $urtype;
160             }
161              
162             sub _vendor_data_type_for_ur_data_type {
163 0     0   0 return ( BOOLEAN => 'BOOLEAN',
164             XML => 'XML',
165             shift->SUPER::_vendor_data_type_for_ur_data_type(),
166             );
167             }
168              
169             sub _alter_sth_for_selecting_blob_columns {
170 0     0   0 my($self, $sth, $column_objects) = @_;
171              
172 0         0 for (my $n = 0; $n < @$column_objects; $n++) {
173 0 0       0 next unless defined ($column_objects->[$n]); # No metaDB info for this one
174 0 0       0 if (uc($column_objects->[$n]->data_type) eq 'BLOB') {
175 0         0 require DBD::Pg;
176 0         0 $sth->bind_param($n+1, undef, { pg_type => DBD::Pg::PG_BYTEA() });
177             }
178             }
179             }
180              
181             my $DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
182             my $TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.US';
183             sub cast_for_data_conversion {
184 4     4 0 8 my($class, $left_type, $right_type, $operator, $sql_clause) = @_;
185              
186 4         15 my @retval = ('%s','%s');
187              
188             # compatible types
189 4 50 33     53 if ($left_type->isa($right_type)
190             or
191             $right_type->isa($left_type)
192             ) {
193 0         0 return @retval;
194             }
195              
196             # So far, the only casting is to support using 'like' and one or both are strings
197 4 50 33     52 if ($operator ne 'like'
      33        
198             or
199             ( ! $left_type->isa('UR::Value::Text') and ! $right_type->isa('UR::Value::Text') )
200             ) {
201 0         0 return @retval;
202             }
203              
204             # Figure out which one is the non-string
205 4 50       17 my($data_type, $i) = $left_type->isa('UR::Value::Text')
206             ? ( $right_type, 1)
207             : ( $left_type, 0);
208              
209 4 100       18 if ($data_type->isa('UR::Value::Timestamp')) {
    50          
210 3         10 $retval[$i] = qq{to_char(%s, '$TIMESTAMP_FORMAT')};
211              
212             } elsif ($data_type->isa('UR::Value::DateTime')) {
213 1         3 $retval[$i] = qq{to_char(%s, '$DATE_FORMAT')};
214              
215             } else {
216 0         0 @retval = $class->SUPER::cast_for_data_conversion($left_type, $right_type, $operator);
217             }
218              
219 4         15 return @retval;
220             }
221              
222             sub _resolve_order_by_clause_for_column {
223 3     3   6 my($self, $column_name, $query_plan, $property_meta) = @_;
224              
225 3         6 my $column_clause = $column_name;
226              
227 3         13 my $is_text_type = $property_meta->is_text;
228 3 50       12 if ($is_text_type) {
229             # Tell the DB to sort the same order as Perl's cmp
230 3         5 $column_clause .= q( COLLATE "C");
231             }
232              
233 3         10 my $is_desc = $query_plan->order_by_column_is_descending($column_name);
234 3 100       9 if ($is_desc) {
235 1         2 $column_clause .= q( DESC);
236             }
237              
238 3         10 return $column_clause;
239             }
240              
241             sub _assure_schema_exists_for_table {
242 0     0     my($self, $table_name, $dbh) = @_;
243              
244 0   0       $dbh ||= $self->get_default_handle;
245              
246 0           my ($schema_name, undef) = $self->_extract_schema_and_table_name($table_name);
247 0 0         if ($schema_name) {
248 0           my $exists = $dbh->selectrow_array("SELECT schema_name FROM information_schema.schemata WHERE schema_name = ?;",
249             undef, $schema_name);
250 0 0         unless ($exists) {
251 0 0         $dbh->do("CREATE SCHEMA $schema_name")
252             or Carp::croak("Could not create schema $schema_name: " . $dbh->errstr);
253             }
254             }
255             }
256              
257             1;
258              
259             =pod
260              
261             =head1 NAME
262              
263             UR::DataSource::Pg - PostgreSQL specific subclass of UR::DataSource::RDBMS
264              
265             =head1 DESCRIPTION
266              
267             This module provides the PostgreSQL-specific methods necessary for interacting with
268             PostgreSQL databases
269              
270             =head1 SEE ALSO
271              
272             L, L
273              
274             =cut
275