File Coverage

blib/lib/App/LedgerSMB/Admin/Database.pm
Criterion Covered Total %
statement 18 94 19.1
branch 0 12 0.0
condition 0 7 0.0
subroutine 6 16 37.5
pod 7 7 100.0
total 31 136 22.7


line stmt bran cond sub pod time code
1             package App::LedgerSMB::Admin::Database;
2 2     2   3523 use Moo;
  2         18490  
  2         694  
3             extends 'PGObject::Util::DBAdmin';
4 2     2   3274 use File::Temp;
  2         29878  
  2         119  
5 2     2   13 use Cwd;
  2         4  
  2         77  
6 2     2   775 use PGObject::Util::DBChange;
  2         44113  
  2         56  
7 2     2   14 use App::LedgerSMB::Admin;
  2         4  
  2         39  
8 2     2   734 use App::LedgerSMB::Admin::Database::Setting;
  2         9  
  2         1888  
9              
10             =head1 NAME
11              
12             App::LedgerSMB::Admin::Database - Administer LedgerSMB Databases
13              
14             =head1 SYNOPSIS
15              
16             Upgrading to 1.4 from 1.3, after updating 1.3 instance to latest:
17              
18             use App::LedgerSMB::Admin;
19             use App::LedgerSMB::Admin::Database;
20             App::LedgerSMB::Admin->add_paths(
21             '1.3' => '/usr/local/ledgersmb_1.3',
22             '1.4' => '/usr/local/ledgersmb_1.4',
23             ); # setting the version paths
24             my $db = App::LedgerSMB::Admin::Database->new(
25             username => 'postgres',
26             password => 'secretpassword',
27             host => 'localhost',
28             port => '5432',
29             dbname => 'mycompany',
30             );
31             if ($db->major_version eq '1.3') {
32             $db->reload;
33             $db->upgrade_to('1.4');
34             }
35              
36             =head1 VERSION
37              
38             0.04
39              
40             =cut
41              
42             our $VERSION=0.04;
43              
44             =head1 PROPERTIES INHERITED FROM PGObject::Util::DBAdmin
45              
46             Please see the docs for PGObject::Util::DBAdmin.
47              
48             =head2 username
49              
50             =head2 password
51              
52             =head2 host
53              
54             =head2 port
55              
56             =head2 dbname
57              
58             =head1 ADDITIONAL PROPERTIES
59              
60             =head2 version
61              
62             Returns the version number of the database.
63              
64             =cut
65              
66             has version => (is => 'lazy');
67              
68             sub _build_version {
69 0     0     my $self = shift;
70 0           return App::LedgerSMB::Admin::Database::Setting->new(
71             database => $self,
72             setting_key => 'version')->value;
73             }
74              
75             =head2 major_version
76              
77             Major versions are generally understood to be not backwards compatible. In
78             LedgerSMB, as with PostgreSQL, major versions are based on the second numbers
79             in the version, so 1.2, 1.3, and 1.4 are major versions.
80              
81             =cut
82              
83             has major_version => (is => 'lazy');
84              
85             sub _build_major_version {
86 0     0     my $self = shift;
87 0           my $version = $self->version;
88 0           $version =~ s/\.\d*(?:-dev)?$//;
89 0           return $version;
90             }
91              
92             =head1 METHODS INHERITED
93              
94             Please see the docs for PGObject::Util::DBAdmin
95              
96             =head2 create
97              
98             =head2 connect
99              
100             =head2 drop
101              
102             =head2 run_file
103              
104             =head2 backup
105              
106             =head2 restore_backup
107              
108             =head1 NEW METHODS
109              
110             =head2 stats
111              
112             Returns a hashref of table names to rows. The following tables are counted:
113              
114             =over
115              
116             =item ar
117              
118             =item ap
119              
120             =item gl
121              
122             =item oe
123              
124             =item acc_trans
125              
126             =item users
127              
128             =item entity_credit_account
129              
130             =item entity
131              
132             =back
133              
134             =cut
135              
136             my @tables = qw(ar ap gl users entity_credit_account entity acc_trans oe);
137              
138             sub stats {
139 0     0 1   my ($self) = @_;
140 0           my $dbh = $self->connect;
141 0           my $results;
142              
143             $results->{$_->{table}} = $_->{count}
144 0           for map {
145 0           my $sth = $dbh->prepare($_->{query});
146 0           $sth->execute;
147 0           my ($count) = $sth->fetchrow_array;
148 0           { table => $_->{table}, count => $count };
149             } map {
150 0           my $qt = 'SELECT COUNT(*) FROM __TABLE__';
151 0           my $id = $dbh->quote_identifier($_);
152 0           $qt =~ s/__TABLE__/$id/;
153 0           { table => $_, query => $qt };
154             } @tables;
155              
156 0           return $results;
157             }
158              
159             =head2 load($major_version)
160              
161             Loads the db schema for the major version requested.
162              
163             =cut
164              
165             sub load {
166 0     0 1   my ($self, $major_version) = @_;
167 0           eval {
168 0           $self->run_file(
169             file => App::LedgerSMB::Admin->path_for($major_version)
170             . "/sql/Pg-database.sql"
171             );
172             };
173 0           my $sqlpath = App::LedgerSMB::Admin->path_for($major_version) . "/sql/modules";
174 0           return $self->process_loadorder($sqlpath, "$sqlpath/LOADORDER");
175             }
176              
177             =head2 reload
178              
179             Reloads all modules in a LedgerSMB instance.
180              
181             =cut
182              
183             sub reload {
184 0     0 1   my ($self) = @_;
185 0           my $path = Cwd::getcwd();
186 0           my $sqlpath = App::LedgerSMB::Admin->path_for($self->major_version);
187 0           chdir $sqlpath;
188 0           my $rc = $self->process_loadorder('sql/modules', "sql/modules/LOADORDER");
189 0           chdir $path;
190 0           return $rc;
191             }
192              
193             =head2 process_loadorder($sql_path, $loadorder_path);
194              
195             Processes a specific loadorder. Useful for installing extensions to LedgerSMB
196              
197             Dies if any SQL files produce errors except from a file starting with "Fixes."
198              
199             =cut
200              
201             sub process_loadorder {
202 0     0 1   my ($self, $sql_path, $loadorder_path) = @_;
203 0           $sql_path =~ s|/$||;
204 0           for my $line (_loadorder_entries($loadorder_path)){
205 0 0         if ($line =~ /^Fixes/){
206 0           eval { $self->run_file(file => "$sql_path/$line") };
  0            
207             } else {
208 0           $self->run_file(file => "$sql_path/$line");
209             }
210             }
211 0 0         $self->process_old_roles() if $self->major_version eq '1.3';
212 0           return 1;
213             }
214              
215             sub _loadorder_entries {
216 0     0     my $loadorderpath = shift;
217 0 0         open(LOAD, '<', $loadorderpath) || die "Cannot open loadorder: $!";
218 0           return grep {$_} map { my $l = $_; $l =~ s/(\s*|#.*)//g; $l} ;
  0            
  0            
  0            
  0            
219             }
220              
221             =head2 process_changes($loadorderfile)
222             applies db changes (post-1.4) to the db as specified in the provided LOADORDER
223              
224             =cut
225              
226             sub process_changes {
227 0     0 1   my ($self) = @_;
228 0           my $loadorderpath = App::LedgerSMB::Admin->path_for($self->major_version) .
229             "/sql/changes";
230 0           my $dbh = $self->connect({ AutoCommit => 0});
231 0           my $sql_path = $loadorderpath;
232 0           PGObject::Util::DBChange->init($dbh);
233 0           for my $line (_loadorder_entries($loadorderpath . '/LOADORDER')){
234 0           $line =~ s/^(!)?//;
235 0           my $failure_ok = $1;
236 0           my $dbchange = PGObject::Util::DBChange->new(
237             path => "$sql_path/$line",
238             no_transactions => 1
239             );
240 0 0         next if $dbchange->is_applied($dbh);
241 0 0 0       $dbchange->apply($dbh) || $failure_ok
242             || die "Change $line failed: " . $dbh->errstr;
243             }
244             }
245              
246             =head2 process_old_roles($rolefile)
247              
248             Processes an old-style (1.3-era) roles file and runs it on the database.
249              
250             =cut
251              
252             sub process_old_roles {
253 0     0 1   my ($self, $rolefile, %args) = @_;
254 0   0       $rolefile ||= 'Roles.sql';
255 0           my $sqlpath = App::LedgerSMB::Admin->path_for($self->major_version)
256             . '/sql/modules';
257 0   0       my $tempdir = $args{tempdir} || $ENV{TEMP} || '/tmp';
258 0           my $temp = File::Temp->new(DIR => $tempdir);
259 0           open ROLES, '<', "$sqlpath/$rolefile";
260 0           for my $line (){
261 0           my $dbname = $self->dbname;
262 0           $line =~ s/<\?lsmb dbname ?>/$dbname/g;
263 0           print $temp $line;
264             }
265 0           eval { $self->run_file($temp->filename); };
  0            
266             }
267              
268             =head2 upgrade_to($major_version)
269              
270             Upgrades to the major version specified. Provides an error if the upgrade file
271             is not found.
272              
273             =cut
274              
275             sub upgrade_to {
276 0     0 1   my ($self, $major_version) = @_;
277 0           my $up_filename = "lsmb" . $self->major_version . '-' . $major_version
278             . ".sql";
279 0           my $versionpath = App::LedgerSMB::Admin->path_for($major_version);
280 0 0         die 'No version path registered' unless $versionpath;
281 0           $self->run_file(file => "$versionpath/sql/upgrade/$up_filename");
282 0           $self->new($self->export)->reload;
283             }
284              
285             =head1 LICENSE AND COPYRIGHT
286              
287             Copyright 2014 Chris Travers.
288              
289             This program is distributed under the (Revised) BSD License:
290             L
291              
292             Redistribution and use in source and binary forms, with or without
293             modification, are permitted provided that the following conditions
294             are met:
295              
296             * Redistributions of source code must retain the above copyright
297             notice, this list of conditions and the following disclaimer.
298              
299             * Redistributions in binary form must reproduce the above copyright
300             notice, this list of conditions and the following disclaimer in the
301             documentation and/or other materials provided with the distribution.
302              
303             * Neither the name of Chris Travers's Organization
304             nor the names of its contributors may be used to endorse or promote
305             products derived from this software without specific prior written
306             permission.
307              
308             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
309             "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
310             LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
311             A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
312             OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
313             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
314             LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
315             DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
316             THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
317             (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
318             OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
319              
320             =cut
321              
322             1; ;