File Coverage

blib/lib/PGObject/Util/PGConfig.pm
Criterion Covered Total %
statement 42 73 57.5
branch 6 8 75.0
condition n/a
subroutine 12 18 66.6
pod 12 12 100.0
total 72 111 64.8


line stmt bran cond sub pod time code
1             package PGObject::Util::PGConfig;
2              
3 4     4   84771 use 5.006;
  4         18  
4 4     4   25 use strict;
  4         16  
  4         104  
5 4     4   21 use warnings;
  4         12  
  4         126  
6 4     4   27 use Carp;
  4         21  
  4         4284  
7              
8             =head1 NAME
9              
10             PGObject::Util::PGConfig - Postgres Configuration Management
11              
12             =head1 VERSION
13              
14             Version 0.10.2
15              
16             =cut
17              
18             our $VERSION = 0.010002;
19              
20              
21             =head1 SYNOPSIS
22              
23             use PGObject::Util::PGConfig;
24              
25             my $config = PGObject::Util::PGConfig->new();
26             # setting values in the internal store
27             $config->set('statement_timeout', 3600); # set the desired state
28             $config->set('datestyle', 'ISO');
29              
30             # session configuration management
31             $config->list($dbh);
32             $config->fetch($dbh, 'statement_timeout'); # get the session statement timeout
33             # We can now sync with Pg for the dbh session
34             $config->sync_session($dbh, qw(statement_timeout datestyle));
35              
36             # or we can get from a file
37             $config->fromfile('path/to/file');
38             # or from file contents
39             $config->fromcontents($string);
40             # and can return current state as file contents
41             $config->filecontents();
42             # or write to a file
43             $config->tofile('path/to/new.conf');
44              
45             =head1 DESCRIPTION
46              
47             The current config module provides an abstraction around the PostgreSQL GUC
48             (configuration system). This includes parsing config files (postgresql.conf,
49             recovery.conf) and retrieve current settings from a database configuration.
50              
51             The module does not depend on a database configuration so it can be used to
52             aggregate configuration data from different sources.
53              
54             Session update guarantees that only appropriate session variables are
55             updated.
56              
57             =head1 Methods
58              
59             =head2 Constructor
60              
61             =head3 new
62              
63             The constructor takes no arguments and initializes an empty store. The store
64             is implemented as a hashref similar to what you would expect from a Moo/Moose
65             object but it is recommended that you do not inspect directly because this
66             behavior is not guaranteed for subclasses.
67              
68             If a subclass overwrites the storage approach, it MUST override this method
69             as well.
70              
71             =cut
72              
73             sub new {
74 2     2 1 38 my ($pkg) = @_;
75 2         7 my $self = {};
76 2         20 bless $self, $pkg;
77             }
78              
79             =head2 Internal store
80              
81             There are several things which are not the responsibility of the internal
82             store. These include checking validity of variable names as these could
83             vary between major versions of PostgreSQL. Subclasses MAY override these
84             methods safely and provide a different storage mechanism.
85              
86             =head3 set($key, $value)
87              
88             Sets a current GUC variable to a particular value.
89              
90             =cut
91              
92             sub set {
93 10     10 1 36 my ($self, $key, $value) = @_;
94 10 50       37 croak 'References unsupported' if ref $value;
95 10         42 $self->{$key} = $value;
96             }
97              
98             =head3 forget($key)
99              
100             Deletes a key from the store
101              
102             =cut
103              
104             sub forget {
105 0     0 1 0 my ($self, $key) = @_;
106 0         0 delete $self->{$key};
107             }
108              
109             =head3 known_keys()
110              
111             Returns a list of keys from the store.
112              
113             =cut
114              
115             sub known_keys {
116 5     5 1 19 my ($self) = @_;
117 5         44 return keys %$self;
118             }
119              
120             =head3 get_value($key)
121              
122             Returns a value from the key in the store.
123              
124             =cut
125              
126             sub get_value {
127 18     18 1 59 my ($self, $key) = @_;
128 18         99 return $self->{$key};
129             }
130              
131             =head2 DB Session
132              
133             The methods in this session integrate with a database session and pull
134             data from these. The module itself does not depend on the database
135             session for general use.
136              
137             =head3 fetch($dbh, $key)
138              
139             Retrieves a setting from the session and saves it to the store.
140              
141             Returns the stored value.
142              
143             =cut
144              
145             sub fetch {
146 0     0 1 0 my ($self, $dbh, $key) = @_;
147 0         0 my $sth = $dbh->prepare("SELECT current_setting(?)");
148 0         0 $sth->execute($key);
149 0         0 $self->set($key, $sth->fetchrow_array);
150 0         0 return $self->get_value($key);
151             }
152              
153             =head3 list($dbh)
154              
155             Returns a list of all GUC variables set for the database session at $dbh
156              
157             Does not affect store.
158              
159             =cut
160              
161             sub list {
162 0     0 1 0 my ($self, $dbh) = @_;
163 0         0 my $sth = $dbh->prepare('SELECT name FROM pg_settings ORDER BY name');
164 0         0 $sth->execute;
165 0         0 my @keys;
166 0         0 while (my ($key) = $sth->fetchrow_array){
167 0         0 push @keys, $key;
168             }
169 0         0 return @keys;
170             }
171              
172             =head3 sync_session($dbh)
173              
174             Synchronizes all stored variables into the current session if applicable.
175              
176             =cut
177              
178             sub sync_session{
179 0     0 1 0 my ($self, $dbh) = @_;
180 0         0 my $query = "
181             SELECT s.name FROM pg_settings s
182             JOIN pg_roles r ON rolname = session_user
183             WHERE name = any(?)
184             AND (s.context = 'user'
185             OR s.context = 'superuser' AND r.rolsuper)
186             ";
187 0         0 my $sth = $dbh->prepare($query);
188 0         0 $sth->execute([$self->known_keys]);
189 0         0 my $setsth = $dbh->prepare(
190             "SELECT set_config(?, ?, false)");
191 0         0 while (my ($setname) = $sth->fetchrow_array){
192 0         0 $setsth->execute($setname, $self->get_value($setname));
193             }
194             }
195              
196             =head2 File and Contents
197              
198             This module is also capable of reading to and writing to files
199             and generating file content in the format expected. This means that the
200             general whitespace rules and escaping approach PostgreSQL expects are met.
201              
202             =head3 fromfile($path)
203              
204             Reads the contents from a file. Loads the whole file into memory.
205              
206             =cut
207              
208             sub fromfile {
209 0     0 1 0 my ($self, $file) = @_;
210 0         0 my $fh;
211 0         0 open $fh, '<', $file;
212 0         0 $self->fromcontents(join("", <$fh>));
213 0         0 close $fh;
214             }
215              
216             =head3 fromcontents($contents)
217              
218             Parses file content and sets the internal store accordingly.
219              
220             =cut
221              
222             sub _unescape {
223 7     7   20 my ($val) = @_;
224 7 50       23 return unless defined $val;
225 7         23 $val =~ s/''/'/g;
226 7         41 $val =~ s/(^'|'$)//g;
227 7         27 return $val;
228             }
229              
230             sub _escape {
231 5     5   12 my ($val) = @_;
232 5         19 $val =~ s/'/''/g;
233 5         25 return $val;
234             }
235              
236             sub fromcontents {
237 2     2 1 11 my ($self, $contents) = @_;
238 2         31 for my $line (split(/(\r|\n)/, $contents)){
239 17         51 $line =~ s/\#.*//;
240 17         267 $line =~ s/(^\s*|\s*$)//g;
241 17 100       65 next unless $line;
242              
243 7         15 my ($key, $value);
244 7 100       28 if ($line =~ /=/){
245 4         25 ($key, $value) = split(/\s*=\s*/, $line, 2);
246             } else {
247 3         14 ($key, $value) = split(/\s/, $line, 2);
248             }
249            
250 7         26 $self->set($key, _unescape($value));
251             }
252             }
253              
254             =head3 filecontents()
255              
256             Returns file contents. Variables are set in alphabetical order
257              
258             =cut
259              
260             sub filecontents{
261 1     1 1 4 my ($self) = @_;
262             return join "\n",
263 1         6 (map {"$_ = '" . _escape($self->get_value($_)) . "'" }
  5         17  
264             sort $self->known_keys);
265             }
266              
267             =head3 tofile($path)
268              
269             Writes the contents, per filecontents above, to $path
270              
271             =cut
272              
273             sub tofile {
274 0     0 1   my ($self, $path) = @_;
275 0           my $fh;
276 0           open $fh, '>', $path;
277 0           print $fh $self->filecontents;
278 0           close $fh;
279             }
280              
281             =head2 Future Versions
282              
283             =head3 sync_system($dbh)
284              
285             This command will use ALTER SYSTEM statements to set defaults to be used on
286             next PostgreSQL restart or reload. Not yet supported.
287              
288             =head1 AUTHOR
289              
290             Chris Travers, C<< >>
291              
292             =head1 BUGS
293              
294             Please report any bugs or feature requests to C, or through
295             the web interface at L. I will be notified, and then you'll
296             automatically be notified of progress on your bug as I make changes.
297              
298              
299              
300              
301             =head1 SUPPORT
302              
303             You can find documentation for this module with the perldoc command.
304              
305             perldoc PGObject::Util::PGConfig
306              
307              
308             You can also look for information at:
309              
310             =over 4
311              
312             =item * RT: CPAN's request tracker (report bugs here)
313              
314             L
315              
316             =item * AnnoCPAN: Annotated CPAN documentation
317              
318             L
319              
320             =item * CPAN Ratings
321              
322             L
323              
324             =item * Search CPAN
325              
326             L
327              
328             =back
329              
330              
331             =head1 ACKNOWLEDGEMENTS
332              
333              
334             =head1 LICENSE AND COPYRIGHT
335              
336             Copyright 2017 Adjust.com
337              
338             This program is distributed under the (Revised) BSD License:
339             L
340              
341             Redistribution and use in source and binary forms, with or without
342             modification, are permitted provided that the following conditions
343             are met:
344              
345             * Redistributions of source code must retain the above copyright
346             notice, this list of conditions and the following disclaimer.
347              
348             * Redistributions in binary form must reproduce the above copyright
349             notice, this list of conditions and the following disclaimer in the
350             documentation and/or other materials provided with the distribution.
351              
352             * Neither the name of Adjust.com
353             nor the names of its contributors may be used to endorse or promote
354             products derived from this software without specific prior written
355             permission.
356              
357             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
358             "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
359             LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
360             A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
361             OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
362             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
363             LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
364             DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
365             THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
366             (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
367             OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
368              
369              
370             =cut
371              
372             1; # End of PGObject::Util::PGConfig