File Coverage

blib/lib/Module/Build/Database/PostgreSQL.pm
Criterion Covered Total %
statement 36 308 11.6
branch 0 98 0.0
condition 0 47 0.0
subroutine 12 44 27.2
pod 0 6 0.0
total 48 503 9.5


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Module::Build::Database::PostgreSQL - PostgreSQL implementation for MBD
4              
5             =head1 SYNOPSIS
6              
7             In Build.PL :
8              
9             my $builder = Module::Build::Database->new(
10             database_type => "PostgreSQL",
11             database_options => {
12             name => "my_database_name",
13             schema => "my_schema_name",
14             # Extra items for scratch databases :
15             append_to_conf => "text to add to postgresql.conf",
16             after_create => q[create schema audit;],
17             },
18             database_extensions => {
19             postgis => { schema => "public", },
20             # directory with postgis.sql and spatial_ref_sys.sql
21             postgis_base => '/usr/local/share/postgresql/contrib'
22             },
23             );
24              
25             =head1 DESCRIPTION
26              
27             Postgres driver for L.
28              
29             =head1 OPTIONS
30              
31             All of the options above may be changed via the Module::Build option
32             handling, e.g.
33              
34             perl Build.PL --database_options name=my_name
35             perl Build.PL --postgis_base=/usr/local/share/postgresql/contrib
36              
37             The options are as follows ;
38              
39             =over 4
40              
41             =item database_options
42              
43             =over 4
44              
45             =item name
46              
47             the name of the database (i.e. 'create database $name')
48              
49             =item schema
50              
51             the name of the schema to be managed by MBD
52              
53             =item append_to_conf
54              
55             extra options to append to C before starting test instances of postgres
56              
57             =item after_create
58              
59             extra SQL to run after running a 'create database' statement. Note that this will be run in several
60             different situations :
61              
62             =over 4
63              
64             =item 1.
65              
66             during a L (creating a test db)
67              
68             =item 2.
69              
70             during a L (also creating a test db)
71              
72             =item 3.
73              
74             during an initial L; when the target database does not yet exist.
75              
76             =back
77              
78             An example of using the after_create statement would be to create a second schema which
79             will not be managed by MBD, but on which the MBD-managed schema depends.
80              
81             =back
82              
83             =item database_extension
84              
85             To specify a server side procedural language you can use the C -E C
86             option, like so:
87              
88             my $builder = Module::Build::Database->new(
89             database_extension => {
90             languages => [ 'plperl', 'pltcl' ],
91             },
92             );
93              
94             Trying to create languages to a patch will not work because they not stored in the main schema and will
95             not be included in C when you run C.
96              
97             This is also similar to
98              
99             after_create => 'create extension ...',
100              
101             except it is executed on B L meaning you can use this to add extensions to
102             existing database deployments.
103              
104             =item postgis_base
105              
106             Specify the directory containing postgis.sql and spatial_ref_sys.sql. If specified these SQL files will be loaded so that
107             you can use PostGIS in your database.
108              
109             =item leave_running
110              
111             If set to true, and if you are not using a persistent scratch database (see next option), then the scratch database will
112             not be stopped and torn down after running C or C.
113              
114             =item scratch_database
115              
116             You can use this option to specify the connection settings for a persistent scratch or temporary database instance, used by
117             the C and C to test schema. B: the C and C
118             will drop and re-create databases on the scratch instance with the same name as the database on your production instance so
119             it is I important that if you use a persistent scratch database that it be dedicated to that task.
120              
121             my $builder = Module::Build::Database->new(
122             scratch_database => {
123             PGHOST => 'databasehost',
124             PGPORT => '5555',
125             PGUSER => 'dbuser',
126             },
127             );
128              
129             If you specify any one of these keys for this option (C, C, C) then MBD will use a persistent
130             scratch database. Any missing values will use the default.
131              
132             You can also specify these settings using environment variables:
133              
134             % export MBD_SCRATCH_PGHOST=databasehost
135             % export MBD_SCRATCH_PGPORT=5555
136             % export MBD_SCRATCH_PGUSER=dbuser
137              
138             By default this module will create its own scratch PostgreSQL instance that uses unix domain sockets for communication
139             each time it needs one when you use the C or C commands. Situations where you might
140             need to use a persistent scratch database:
141              
142             =over 4
143              
144             =item 1.
145              
146             The server and server binaries are hosted on a system different to the one that you are doing development
147              
148             =item 2.
149              
150             You are using MBD on Windows where unix domain sockets are not available
151              
152             =back
153              
154             =back
155              
156             =head1 NOTES
157              
158             The environment variables understood by C:
159             C, C and C will be used when
160             connecting to a live database (for L and
161             L). C will be ignored;
162             the name of the database should be specified in
163             Build.PL instead.
164              
165             =cut
166              
167             package Module::Build::Database::PostgreSQL;
168 4     4   123887 use base 'Module::Build::Database';
  4         8  
  4         2010  
169 4     4   32 use File::Temp qw/tempdir/;
  4         6  
  4         246  
170 4     4   18 use File::Path qw/rmtree/;
  4         6  
  4         153  
171 4     4   19 use File::Basename qw/dirname/;
  4         7  
  4         148  
172 4     4   2137 use File::Copy::Recursive qw/fcopy dirmove/;
  4         8702  
  4         264  
173 4     4   25 use Path::Class qw/file/;
  4         5  
  4         134  
174 4     4   16 use IO::File;
  4         7  
  4         448  
175 4     4   18 use File::Which qw( which );
  4         4  
  4         126  
176              
177 4     4   2386 use Module::Build::Database::PostgreSQL::Templates;
  4         8  
  4         118  
178 4     4   17 use Module::Build::Database::Helpers qw/do_system verify_bin info debug/;
  4         5  
  4         36  
179 4     4   1476 use strict;
  4         8  
  4         97  
180 4     4   15 use warnings;
  4         5  
  4         11093  
181             our $VERSION = '0.57';
182              
183             __PACKAGE__->add_property(database_options => default => { name => "foo", schema => "bar" });
184             __PACKAGE__->add_property(database_extensions => default => { postgis => 0 } );
185             __PACKAGE__->add_property(postgis_base => default => "/usr/local/share/postgis" );
186             __PACKAGE__->add_property(_tmp_db_dir => default => "" );
187             __PACKAGE__->add_property(leave_running => default => 0 ); # leave running after dbtest?
188             __PACKAGE__->add_property(scratch_database => default => { map {; "PG$_" => $ENV{"MBD_SCRATCH_PG$_"} }
189             grep { defined $ENV{"MBD_SCRATCH_PG$_"} }
190             qw( HOST PORT USER ) } );
191              
192             # Binaries used by this module. They should be in $ENV{PATH}.
193             our %Bin = (
194             Psql => 'psql',
195             Pgctl => 'pg_ctl',
196             Postgres => 'postgres',
197             Initdb => 'initdb',
198             Createdb => 'createdb',
199             Dropdb => 'dropdb',
200             Pgdump => 'pg_dump',
201             Pgdoc => [ qw/pg_autodoc postgresql_autodoc/ ],
202             );
203             my $server_bin_dir;
204             if(my $pg_config = which 'pg_config')
205             {
206             $pg_config = Win32::GetShortPathName($pg_config) if $^O eq 'MSWin32' && $pg_config =~ /\s/;
207             $server_bin_dir = `$pg_config --bindir`;
208             chomp $server_bin_dir;
209             $server_bin_dir = Win32::GetShortPathName($server_bin_dir) if $^O eq 'MSWin32' && $server_bin_dir =~ /\s/;
210             undef $server_bin_dir unless -d $server_bin_dir;
211             }
212             verify_bin(\%Bin, $server_bin_dir);
213              
214             sub _do_psql {
215 0     0     my $self = shift;
216 0           my $sql = shift;
217 0           my $database_name = $self->database_options('name');
218 0           my $tmp = File::Temp->new(TEMPLATE => "tmp_db_XXXX", SUFFIX => '.sql');
219 0           print $tmp $sql;
220 0           $tmp->close;
221             # -q: quiet, ON_ERROR_STOP: throw exceptions
222 0           local $ENV{PERL5LIB};
223 0           my $ret = do_system( $Bin{Psql}, "-q", "-vON_ERROR_STOP=1", "-f", "$tmp", $database_name );
224 0           $tmp->unlink_on_destroy($ret);
225 0           $ret;
226             }
227             sub _do_psql_out {
228 0     0     my $self = shift;
229 0           my $sql = shift;
230 0           my $database_name = $self->database_options('name');
231             # -F field separator, -x extended output, -A: unaligned
232 0           local $ENV{PERL5LIB};
233 0           do_system( $Bin{Psql}, "-q", "-vON_ERROR_STOP=1", "-A", "-F ' : '", "-x", "-c", qq["$sql"], $database_name );
234             }
235             sub _do_psql_file {
236 0     0     my $self = shift;
237 0           my $filename = shift;
238 0 0         unless (-e $filename) {
239 0           warn "could not open file $filename";
240 0           return 0;
241             }
242 0 0         unless (-s $filename) {
243 0           warn "file $filename is empty";
244 0           return 0;
245             }
246 0           my $database_name = $self->database_options('name');
247             # -q: quiet, ON_ERROR_STOP: throw exceptions
248 0           local $ENV{PERL5LIB};
249 0           do_system($Bin{Psql},"-q","-vON_ERROR_STOP=1","-f",$filename, $database_name);
250             }
251             sub _do_psql_into_file {
252 0     0     my $self = shift;
253 0           my $filename = shift;
254 0           my $sql = shift;
255 0           my $database_name = $self->database_options('name');
256             # -A: unaligned, -F: field separator, -t: tuples only, ON_ERROR_STOP: throw exceptions
257 0           local $ENV{PERL5LIB};
258 0 0         my $q = $^O eq 'MSWin32' ? '"' : "'";
259 0           do_system( $Bin{Psql}, "-q", "-vON_ERROR_STOP=1", "-A", "-F $q\t$q", "-t", "-c", qq["$sql"], $database_name, ">", "$filename" );
260             }
261             sub _do_psql_capture {
262 0     0     my $self = shift;
263 0           my $sql = shift;
264 0           my $database_name = $self->database_options('name');
265 0           local $ENV{PERL5LIB};
266 0           return qx[$Bin{Psql} -c "$sql" $database_name];
267             }
268              
269             sub _cleanup_old_dbs {
270 0     0     my $self = shift;
271 0           my %args = @_; # pass all => 1 to clean up the current one too
272              
273 0           my $glob;
274             {
275 0           my $tmpdir = tempdir("mbdtest_XXXXXX", TMPDIR => 1);
  0            
276 0           $glob = "$tmpdir";
277 0           rmtree($tmpdir);
278             }
279 0           $glob =~ s/mbdtest_.*$/mbdtest_*/;
280 0           for my $thisdir (glob $glob) {
281 0 0 0       next unless -d $thisdir && -w $thisdir;
282 0           debug "cleaning up old tmp instance : $thisdir";
283 0           $self->_stop_db("$thisdir/db");
284 0           rmtree($thisdir);
285             }
286             }
287              
288             sub _start_new_db {
289 0     0     my $self = shift;
290             # Start a new database and return the host on which it was started.
291              
292 0           my $database_name = $self->database_options('name');
293 0           $ENV{PGDATABASE} = $database_name;
294              
295 0 0         if(%{ $self->scratch_database }) {
  0            
296 0           delete @ENV{qw( PGHOST PGUSER PGPORT )};
297 0           %ENV = (%ENV, %{ $self->scratch_database });
  0            
298 0           do_system("_silent", $Bin{Dropdb}, $database_name);
299              
300             } else {
301              
302 0           $self->_cleanup_old_dbs();
303              
304 0           my $tmpdir = tempdir("mbdtest_XXXXXX", TMPDIR => 1);
305 0           my $dbdir = $tmpdir."/db";
306 0           my $initlog = "$tmpdir/postgres.log";
307 0           $self->_tmp_db_dir($dbdir);
308              
309 0           $ENV{PGHOST} = "$dbdir"; # makes psql use a socket, not a tcp port
310 0           delete $ENV{PGUSER};
311 0           delete $ENV{PGPORT};
312              
313 0           debug "initializing database (log: $initlog)";
314              
315 0 0         do_system($Bin{Initdb}, "-D", "$dbdir", ">>", "$initlog", "2>&1") or do {
316 0           my $log = '';
317 0 0         $log = file($initlog)->slurp if -e $initlog;
318 0           die "could not initdb ($Bin{Initdb})\n$log\n";
319             };
320              
321 0 0         if (my $conf_append = $self->database_options('append_to_conf')) {
322 0 0         die "cannot find postgresql.conf" unless -e "$dbdir/postgresql.conf";
323 0 0         open my $fp, ">> $dbdir/postgresql.conf" or die "could not open postgresql.conf : $!";
324 0           print $fp $conf_append;
325 0           close $fp;
326             }
327              
328 0           my $pmopts = qq[-k $dbdir -h '' -p 5432];
329              
330 0           debug "# starting postgres in $dbdir";
331 0 0         do_system($Bin{Pgctl}, qq[-o "$pmopts"], "-w", "-t", 120, "-D", "$dbdir", "-l", "postmaster.log", "start") or do {
332 0           my $log;
333 0 0         if (-e "$dbdir/postmaster.log") {
334 0           $log = file("$dbdir/postmaster.log")->slurp;
335             } else {
336 0           $log = "no log file : $dbdir/postmaster.log";
337             }
338 0           die "could not start postgres\n$log\n ";
339             };
340              
341 0           my $domain = $dbdir.'/.s.PGSQL.5432';
342 0 0         -e $domain or die "could not find $domain";
343             }
344              
345 0           $self->_create_database();
346              
347 0           return $self->_dbhost;
348             }
349              
350             sub _remove_db {
351 0     0     my $self = shift;
352 0 0 0       return if $ENV{MBD_DONT_STOP_TEST_DB} || %{ $self->scratch_database };
  0            
353 0   0       my $dbdir = shift || $self->_tmp_db_dir();
354 0           $dbdir =~ s/\/db$//;
355 0           rmtree $dbdir;
356             }
357              
358             sub _stop_db {
359 0     0     my $self = shift;
360 0 0 0       return if $ENV{MBD_DONT_STOP_TEST_DB} || %{ $self->scratch_database };
  0            
361 0   0       my $dbdir = shift || $self->_tmp_db_dir();
362 0           my $pid_file = "$dbdir/postmaster.pid";
363 0 0         unless (-e $pid_file) {
364 0           debug "no pid file ($pid_file), not stopping db";
365 0           return;
366             }
367 0           my ($pid) = IO::File->new("<$pid_file")->getlines;
368 0           chomp $pid;
369 0           kill "TERM", $pid;
370 0           sleep 1;
371 0 0         return unless kill 0, $pid;
372 0 0         kill 9, $pid or info "could not send signal to $pid";
373             }
374              
375             sub _apply_base_sql {
376 0     0     my $self = shift;
377 0   0       my $filename = shift || $self->base_dir."/db/dist/base.sql";
378 0 0         return unless -e $filename;
379 0           info "applying base.sql";
380 0           $self->_do_psql_file($filename);
381             }
382              
383             sub _apply_base_data {
384 0     0     my $self = shift;
385 0   0       my $filename = shift || $self->base_dir."/db/dist/base_data.sql";
386 0 0         return 1 unless -e $filename;
387 0           info "applying base_data.sql";
388 0           $self->_do_psql_file($filename);
389             }
390              
391             sub _dump_base_sql {
392             # Optional parameter "outfile" gives the name of the file into which to dump the schema.
393             # If the parameter is omitted, dump and atomically rename to db/dist/base.sql.
394 0     0     my $self = shift;
395 0           my %args = @_;
396 0   0       my $outfile = $args{outfile} || $self->base_dir. "/db/dist/base.sql";
397              
398 0           my $tmpfile = file( tempdir( CLEANUP => 1 ), 'dump.sql');
399              
400             # -x : no privileges, -O : no owner, -s : schema only, -n : only this schema
401 0           my $database_schema = $self->database_options('schema');
402 0           my $database_name = $self->database_options('name');
403 0           local $ENV{PERL5LIB};
404             do_system( $Bin{Pgdump}, "-xOs", "-E", "utf8", "-n", $database_schema, $database_name, ">", $tmpfile )
405 0 0         or do {
406 0           info "Error running pgdump";
407 0           die "Error running pgdump : $! ${^CHILD_ERROR_NATIVE}";
408 0           return 0;
409             };
410              
411 0           my @lines = $tmpfile->slurp();
412 0 0         unless (@lines) {
413 0           die "# Could not run pgdump and write to $tmpfile";
414             }
415 0   0       @lines = grep {
416 0           $_ !~ /^--/
417             and $_ !~ /^CREATE SCHEMA $database_schema;$/
418             and $_ !~ /^SET (search_path|lock_timeout)/
419             } @lines;
420 0           for (@lines) {
421 0 0         /alter table/i and s/$database_schema\.//;
422             }
423 0           file($outfile)->spew(join '', @lines);
424 0 0 0       if (@lines > 0 && !-s $outfile) {
425 0           die "# Unable to write to $outfile";
426             }
427 0           return 1;
428             }
429              
430             sub _dump_base_data {
431             # Optional parameter "outfile, defaults to db/dist/base_data.sql
432 0     0     my $self = shift;
433 0           my %args = @_;
434 0   0       my $outfile = $args{outfile} || $self->base_dir. "/db/dist/base_data.sql";
435              
436 0           my $tmpfile = File::Temp->new(
437             TEMPLATE => (dirname $outfile)."/dump_XXXXXX",
438             UNLINK => 0
439             );
440 0           $tmpfile->close;
441              
442             # -x : no privileges, -O : no owner, -s : schema only, -n : only this schema
443 0           my $database_schema = $self->database_options('schema');
444 0           my $database_name = $self->database_options('name');
445 0           local $ENV{PERL5LIB};
446 0 0         do_system( $Bin{Pgdump}, "--data-only", "-xO", "-E", "utf8", "-n", $database_schema, $database_name,
447             "|", "egrep -v '^SET (lock_timeout|search_path)'",
448             ">", "$tmpfile" )
449             or return 0;
450 0 0         rename "$tmpfile", $outfile or die "rename failed: $!";
451             }
452              
453             sub _apply_patch {
454 0     0     my $self = shift;
455 0           my $patch_file = shift;
456              
457 0           return $self->_do_psql_file($self->base_dir."/db/patches/$patch_file");
458             }
459              
460             sub _is_fresh_install {
461 0     0     my $self = shift;
462              
463 0           my $database_name = $self->database_options('name');
464 0 0         unless ($self->_database_exists) {
465 0           info "database $database_name does not exist";
466 0           return 1;
467             }
468              
469 0           my $file = File::Temp->new(); $file->close;
  0            
470 0           my $database_schema = $self->database_options('schema');
471 0           $self->_do_psql_into_file("$file","\\dn $database_schema");
472 0           return !do_system("_silent","grep -q $database_schema $file");
473             }
474              
475             sub _show_live_db {
476             # Display the connection information
477 0     0     my $self = shift;
478              
479 0   0       info "PGUSER : " . ( $ENV{PGUSER} || "" );
480 0   0       info "PGHOST : " . ( $ENV{PGHOST} || "" );
481 0   0       info "PGPORT : " . ( $ENV{PGPORT} || "" );
482              
483 0   0       my $database_name = shift || $self->database_options('name');
484 0           info "database : $database_name";
485              
486 0 0         return unless $self->_database_exists;
487 0           $self->_do_psql_out("select current_database(),session_user,version();");
488             }
489              
490             sub _patch_table_exists {
491             # returns true or false
492 0     0     my $self = shift;
493 0           my $file = File::Temp->new(); $file->close;
  0            
494 0           my $database_schema = $self->database_options('schema');
495 0           $self->_do_psql_into_file("$file","select tablename from pg_tables where tablename='patches_applied' and schemaname = '$database_schema'");
496 0           return do_system("_silent","grep -q patches_applied $file");
497             }
498              
499             sub _dump_patch_table {
500             # Dump the patch table in an existing db into a flat file, that
501             # will be in the same format as patches_applied.txt.
502 0     0     my $self = shift;
503 0           my %args = @_;
504 0 0         my $filename = $args{outfile} or Carp::confess "need a filename";
505 0           my $database_schema = $self->database_options('schema');
506 0           $self->_do_psql_into_file($filename,"select patch_name,patch_md5 from $database_schema.patches_applied order by patch_name");
507             }
508              
509             sub _create_patch_table {
510 0     0     my $self = shift;
511             # create a new patch table
512 0           my $database_schema = $self->database_options('schema');
513 0           my $sql = <
514             CREATE TABLE $database_schema.patches_applied (
515             patch_name varchar(255) primary key,
516             patch_md5 varchar(255),
517             when_applied timestamp );
518             EOSQL
519 0           $self->_do_psql($sql);
520             }
521              
522             sub _insert_patch_record {
523 0     0     my $self = shift;
524 0           my $record = shift;
525 0           my ($name,$md5) = @$record;
526 0           my $database_schema = $self->database_options('schema');
527 0           $self->_do_psql("insert into $database_schema.patches_applied (patch_name, patch_md5, when_applied) ".
528             " values ('$name','$md5',now()) ");
529             }
530              
531             sub _database_exists {
532 0     0     my $self = shift;
533 0   0       my $database_name = shift || $self->database_options('name');
534 0           local $ENV{PERL5LIB};
535 0           scalar grep /^$database_name$/, map { [split /:/]->[0] } `psql -Alt -F:`;
  0            
536             }
537              
538             sub _create_language_extensions {
539 0     0     my $self = shift;
540 0           my $list = $self->database_extensions('languages');
541 0 0         return unless $list;
542 0           foreach my $lang (@$list) {
543 0 0         $self->_do_psql("create extension if not exists $lang") || die "error creating language: $lang";
544             }
545             }
546              
547             sub _create_database {
548 0     0     my $self = shift;
549              
550 0           my $database_name = $self->database_options('name');
551 0           my $database_schema = $self->database_options('schema');
552              
553             # create the database if necessary
554 0 0         unless ($self->_database_exists($database_name)) {
555 0           local $ENV{PERL5LIB};
556 0 0         do_system($Bin{Createdb}, $database_name) or die "could not createdb";
557             }
558              
559             # Create a fresh schema in the database.
560 0 0         $self->_do_psql("create schema $database_schema") unless $database_schema eq 'public';
561              
562 0           $self->_do_psql("alter database $database_name set client_min_messages to ERROR");
563              
564 0           $self->_do_psql("alter database $database_name set search_path to $database_schema;");
565              
566             # stolen from http://wiki.postgresql.org/wiki/CREATE_OR_REPLACE_LANGUAGE
567 0           $self->_do_psql(<<'SAFE_MAKE_PLPGSQL');
568             CREATE OR REPLACE FUNCTION make_plpgsql()
569             RETURNS VOID
570             LANGUAGE SQL
571             AS $$
572             CREATE LANGUAGE plpgsql;
573             $$;
574              
575             SELECT
576             CASE
577             WHEN EXISTS(
578             SELECT 1
579             FROM pg_catalog.pg_language
580             WHERE lanname='plpgsql'
581             )
582             THEN NULL
583             ELSE make_plpgsql() END;
584              
585             DROP FUNCTION make_plpgsql();
586             SAFE_MAKE_PLPGSQL
587              
588 0 0         if (my $postgis = $self->database_extensions('postgis')) {
589 0           info "applying postgis extension";
590 0 0         my $postgis_schema = $postgis->{schema} or die "No schema given for postgis";
591 0 0         $self->_do_psql("create schema $postgis_schema") unless $postgis_schema eq 'public';
592 0           $self->_do_psql("alter database $database_name set search_path to $postgis_schema;");
593             # We need to run "createlang plpgsql" first.
594 0 0         $self->_do_psql_file($self->postgis_base. "/postgis.sql") or die "could not do postgis.sql";
595 0 0         $self->_do_psql_file($self->postgis_base. "/spatial_ref_sys.sql") or die "could not do spatial_ref_sys.sql";
596 0           $self->_do_psql("alter database $database_name set search_path to $database_schema, $postgis_schema");
597             }
598              
599 0 0         if (my $sql = $self->database_options('post_initdb')) {
600 0           info "applying post_initdb (nb: this option has been renamed to 'after_create')";
601 0           $self->_do_psql($sql);
602             }
603              
604 0 0         if (my $sql = $self->database_options('after_create')) {
605 0           info "applying after_create";
606 0           $self->_do_psql($sql);
607             }
608              
609 0           1;
610             }
611              
612             sub _remove_patches_applied_table {
613 0     0     my $self = shift;
614 0           my $database_schema = $self->database_options('schema');
615 0           $self->_do_psql("drop table if exists $database_schema.patches_applied;");
616             }
617              
618             sub _generate_docs {
619 0     0     my $self = shift;
620 0           my %args = @_;
621 0 0         my $dir = $args{dir} or die "missing dir";
622 0           my $tmpdir = tempdir;
623 0           my $tc = "Module::Build::Database::PostgreSQL::Templates";
624 0           my $database_name = $self->database_options('name');
625 0           my $database_schema = $self->database_options('schema');
626              
627 0           $self->_start_new_db();
628 0           $self->_apply_base_sql();
629              
630 0           chdir $tmpdir;
631 0           for my $filename ($tc->filenames) {
632 0 0         open my $fp, ">$filename" or die $!;
633 0           print ${fp} $tc->file_contents($filename);
634 0           close $fp;
635             }
636              
637             # http://perlmonks.org/?node_id=821413
638 0           do_system( $Bin{Pgdoc}, "-d", $database_name, "-s", $database_schema, "-l .", "-t pod" );
639 0           do_system( $Bin{Pgdoc}, "-d", $database_name, "-s", $database_schema, "-l .", "-t html" );
640 0           do_system( $Bin{Pgdoc}, "-d", $database_name, "-s", $database_schema, "-l .", "-t dot" );
641              
642 0           for my $type (qw(pod html)) {
643 0 0         my $fp = IO::File->new("<$database_name.$type") or die $!;
644 0 0         mkdir $type or die $!;
645 0           my $outfp;
646 0           while (<$fp>) {
647 0 0         s/^_CUT: (.*)$// and do { $outfp = IO::File->new(">$type/$1") or die $!; };
  0 0          
648 0 0         s/^_DB: (.*)$// and do { $_ = $self->_do_psql_capture($1); s/^/ /gm; };
  0            
  0            
649 0 0         print ${outfp} $_ if defined($outfp);
650             }
651             }
652 0           dirmove "$tmpdir/pod", "$dir/pod";
653 0           info "Generated $dir/pod";
654 0           dirmove "$tmpdir/html", "$dir/html";
655 0           info "Generated $dir/html";
656 0           fcopy "$tmpdir/$database_name.dot", "$dir";
657 0           info "Generated $dir/$database_name.dot";
658             }
659              
660 0     0 0   sub ACTION_dbtest { shift->SUPER::ACTION_dbtest(@_); }
661 0     0 0   sub ACTION_dbclean { shift->SUPER::ACTION_dbclean(@_); }
662 0     0 0   sub ACTION_dbdist { shift->SUPER::ACTION_dbdist(@_); }
663 0     0 0   sub ACTION_dbdocs { shift->SUPER::ACTION_dbdocs(@_); }
664 0     0 0   sub ACTION_dbinstall { shift->SUPER::ACTION_dbinstall(@_); }
665 0     0 0   sub ACTION_dbfakeinstall { shift->SUPER::ACTION_dbfakeinstall(@_); }
666              
667             sub _dbhost {
668 0   0 0     return $ENV{PGHOST} || 'localhost';
669             }
670              
671             1;
672