1package App::Info::RDBMS::PostgreSQL;
2
3=head1 NAME
4
5App::Info::RDBMS::PostgreSQL - Information about PostgreSQL
6
7=head1 SYNOPSIS
8
9  use App::Info::RDBMS::PostgreSQL;
10
11  my $pg = App::Info::RDBMS::PostgreSQL->new;
12
13  if ($pg->installed) {
14      print "App name: ", $pg->name, "\n";
15      print "Version:  ", $pg->version, "\n";
16      print "Bin dir:  ", $pg->bin_dir, "\n";
17  } else {
18      print "PostgreSQL is not installed. :-(\n";
19  }
20
21=head1 DESCRIPTION
22
23App::Info::RDBMS::PostgreSQL supplies information about the PostgreSQL
24database server installed on the local system. It implements all of the
25methods defined by App::Info::RDBMS. Methods that trigger events will trigger
26them only the first time they're called (See L<App::Info|App::Info> for
27documentation on handling events). To start over (after, say, someone has
28installed PostgreSQL) construct a new App::Info::RDBMS::PostgreSQL object to
29aggregate new meta data.
30
31Some of the methods trigger the same events. This is due to cross-calling of
32shared subroutines. However, any one event should be triggered no more than
33once. For example, although the info event "Executing `pg_config --version`"
34is documented for the methods C<name()>, C<version()>, C<major_version()>,
35C<minor_version()>, and C<patch_version()>, rest assured that it will only be
36triggered once, by whichever of those four methods is called first.
37
38=cut
39
40use strict;
41use App::Info::RDBMS;
42use App::Info::Util;
43use vars qw(@ISA $VERSION);
44@ISA = qw(App::Info::RDBMS);
45$VERSION = '0.57';
46use constant WIN32 => $^O eq 'MSWin32';
47
48my $u = App::Info::Util->new;
49my @EXES = qw(postgres createdb createlang createuser dropdb droplang
50              dropuser initdb pg_dump pg_dumpall pg_restore postmaster
51              vacuumdb psql);
52
53=head1 INTERFACE
54
55=head2 Constructor
56
57=head3 new
58
59  my $pg = App::Info::RDBMS::PostgreSQL->new(@params);
60
61Returns an App::Info::RDBMS::PostgreSQL object. See L<App::Info|App::Info> for
62a complete description of argument parameters.
63
64When it called, C<new()> searches the file system for an executable named for
65the list returned by C<search_exe_names()>, usually F<pg_config>, in the list
66of directories returned by C<search_bin_dirs()>. If found, F<pg_config> will
67be called by the object methods below to gather the data necessary for
68each. If F<pg_config> cannot be found, then PostgreSQL is assumed not to be
69installed, and each of the object methods will return C<undef>.
70
71C<new()> also takes a number of optional parameters in addition to those
72documented for App::Info. These parameters allow you to specify alternate
73names for PostgreSQL executables (other than F<pg_config>, which you specify
74via the C<search_exe_names> parameter). These parameters are:
75
76=over
77
78=item search_postgres_names
79
80=item search_createdb_names
81
82=item search_createlang_names
83
84=item search_createuser_names
85
86=item search_dropd_names
87
88=item search_droplang_names
89
90=item search_dropuser_names
91
92=item search_initdb_names
93
94=item search_pg_dump_names
95
96=item search_pg_dumpall_names
97
98=item search_pg_restore_names
99
100=item search_postmaster_names
101
102=item search_psql_names
103
104=item search_vacuumdb_names
105
106=back
107
108B<Events:>
109
110=over 4
111
112=item info
113
114Looking for pg_config
115
116=item confirm
117
118Path to pg_config?
119
120=item unknown
121
122Path to pg_config?
123
124=back
125
126=cut
127
128sub new {
129    # Construct the object.
130    my $self = shift->SUPER::new(@_);
131
132    # Find pg_config.
133    $self->info("Looking for pg_config");
134
135    my @paths = $self->search_bin_dirs;
136    my @exes = $self->search_exe_names;
137
138    if (my $cfg = $u->first_cat_exe(\@exes, @paths)) {
139        # We found it. Confirm.
140        $self->{pg_config} = $self->confirm( key      => 'path to pg_config',
141                                             prompt   => "Path to pg_config?",
142                                             value    => $cfg,
143                                             callback => sub { -x },
144                                             error    => 'Not an executable');
145    } else {
146        # Handle an unknown value.
147        $self->{pg_config} = $self->unknown( key      => 'path to pg_config',
148                                             prompt   => "Path to pg_config?",
149                                             callback => sub { -x },
150                                             error    => 'Not an executable');
151    }
152
153    # Set up search defaults.
154    for my $exe (@EXES) {
155        my $attr = "search_$exe\_names";
156        if (exists $self->{$attr}) {
157            $self->{$attr} = [$self->{$attr}] unless ref $self->{$attr} eq 'ARRAY';
158        } else {
159            $self->{$attr} = [];
160        }
161    }
162
163    return $self;
164}
165
166# We'll use this code reference as a common way of collecting data.
167my $get_data = sub {
168    return unless $_[0]->{pg_config};
169    $_[0]->info(qq{Executing `"$_[0]->{pg_config}" $_[1]`});
170    my $info = `"$_[0]->{pg_config}" $_[1]`;
171    chomp $info;
172    return $info;
173};
174
175##############################################################################
176
177=head2 Class Method
178
179=head3 key_name
180
181  my $key_name = App::Info::RDBMS::PostgreSQL->key_name;
182
183Returns the unique key name that describes this class. The value returned is
184the string "PostgreSQL".
185
186=cut
187
188sub key_name { 'PostgreSQL' }
189
190##############################################################################
191
192=head2 Object Methods
193
194=head3 installed
195
196  print "PostgreSQL is ", ($pg->installed ? '' : 'not '), "installed.\n";
197
198Returns true if PostgreSQL is installed, and false if it is not.
199App::Info::RDBMS::PostgreSQL determines whether PostgreSQL is installed based
200on the presence or absence of the F<pg_config> application on the file system
201as found when C<new()> constructed the object. If PostgreSQL does not appear
202to be installed, then all of the other object methods will return empty
203values.
204
205=cut
206
207sub installed { return $_[0]->{pg_config} ? 1 : undef }
208
209##############################################################################
210
211=head3 name
212
213  my $name = $pg->name;
214
215Returns the name of the application. App::Info::RDBMS::PostgreSQL parses the
216name from the system call C<`pg_config --version`>.
217
218B<Events:>
219
220=over 4
221
222=item info
223
224Executing `pg_config --version`
225
226=item error
227
228Failed to find PostgreSQL version with `pg_config --version`
229
230Unable to parse name from string
231
232Unable to parse version from string
233
234Failed to parse PostgreSQL version parts from string
235
236=item unknown
237
238Enter a valid PostgreSQL name
239
240=back
241
242=cut
243
244# This code reference is used by name(), version(), major_version(),
245# minor_version(), and patch_version() to aggregate the data they need.
246my $get_version = sub {
247    my $self = shift;
248    $self->{'--version'} = 1;
249    my $data = $get_data->($self, '--version');
250    unless ($data) {
251        $self->error("Failed to find PostgreSQL version with ".
252                     "`$self->{pg_config} --version`");
253            return;
254    }
255
256    chomp $data;
257    my ($name, $version) =  split /\s+/, $data, 2;
258
259    # Check for and assign the name.
260    $name ?
261      $self->{name} = $name :
262      $self->error("Unable to parse name from string '$data'");
263
264    # Parse the version number.
265    if ($version) {
266        my ($x, $y, $z) = $version =~ /(\d+)\.(\d+).(\d+)/;
267        if (defined $x and defined $y and defined $z) {
268            # Beta/devel/release candidates are treated as patch level "0"
269            @{$self}{qw(version major minor patch)} =
270              ($version, $x, $y, $z);
271        } elsif ($version =~ /(\d+)\.(\d+)/) {
272            # New versions, such as "7.4", are treated as patch level "0"
273            @{$self}{qw(version major minor patch)} =
274              ($version, $1, $2, 0);
275        } else {
276            $self->error("Failed to parse PostgreSQL version parts from " .
277                         "string '$version'");
278        }
279    } else {
280        $self->error("Unable to parse version from string '$data'");
281    }
282};
283
284sub name {
285    my $self = shift;
286    return unless $self->{pg_config};
287
288    # Load data.
289    $get_version->($self) unless $self->{'--version'};
290
291    # Handle an unknown name.
292    $self->{name} ||= $self->unknown( key => 'postgres name' );
293
294    # Return the name.
295    return $self->{name};
296}
297
298##############################################################################
299
300=head3 version
301
302  my $version = $pg->version;
303
304Returns the PostgreSQL version number. App::Info::RDBMS::PostgreSQL parses the
305version number from the system call C<`pg_config --version`>.
306
307B<Events:>
308
309=over 4
310
311=item info
312
313Executing `pg_config --version`
314
315=item error
316
317Failed to find PostgreSQL version with `pg_config --version`
318
319Unable to parse name from string
320
321Unable to parse version from string
322
323Failed to parse PostgreSQL version parts from string
324
325=item unknown
326
327Enter a valid PostgreSQL version number
328
329=back
330
331=cut
332
333sub version {
334    my $self = shift;
335    return unless $self->{pg_config};
336
337    # Load data.
338    $get_version->($self) unless $self->{'--version'};
339
340    # Handle an unknown value.
341    unless ($self->{version}) {
342        # Create a validation code reference.
343        my $chk_version = sub {
344            # Try to get the version number parts.
345            my ($x, $y, $z) = /^(\d+)\.(\d+).(\d+)$/;
346            # Return false if we didn't get all three.
347            return unless $x and defined $y and defined $z;
348            # Save all three parts.
349            @{$self}{qw(major minor patch)} = ($x, $y, $z);
350            # Return true.
351            return 1;
352        };
353        $self->{version} = $self->unknown( key     => 'postgres version number',
354                                           callback => $chk_version);
355    }
356
357    return $self->{version};
358}
359
360##############################################################################
361
362=head3 major version
363
364  my $major_version = $pg->major_version;
365
366Returns the PostgreSQL major version number. App::Info::RDBMS::PostgreSQL
367parses the major version number from the system call C<`pg_config --version`>.
368For example, if C<version()> returns "7.1.2", then this method returns "7".
369
370B<Events:>
371
372=over 4
373
374=item info
375
376Executing `pg_config --version`
377
378=item error
379
380Failed to find PostgreSQL version with `pg_config --version`
381
382Unable to parse name from string
383
384Unable to parse version from string
385
386Failed to parse PostgreSQL version parts from string
387
388=item unknown
389
390Enter a valid PostgreSQL major version number
391
392=back
393
394=cut
395
396# This code reference is used by major_version(), minor_version(), and
397# patch_version() to validate a version number entered by a user.
398my $is_int = sub { /^\d+$/ };
399
400sub major_version {
401    my $self = shift;
402    return unless $self->{pg_config};
403    # Load data.
404    $get_version->($self) unless exists $self->{'--version'};
405    # Handle an unknown value.
406    $self->{major} = $self->unknown( key      => 'postgres major version number',
407                                     callback => $is_int)
408      unless $self->{major};
409    return $self->{major};
410}
411
412##############################################################################
413
414=head3 minor version
415
416  my $minor_version = $pg->minor_version;
417
418Returns the PostgreSQL minor version number. App::Info::RDBMS::PostgreSQL
419parses the minor version number from the system call C<`pg_config --version`>.
420For example, if C<version()> returns "7.1.2", then this method returns "2".
421
422B<Events:>
423
424=over 4
425
426=item info
427
428Executing `pg_config --version`
429
430=item error
431
432Failed to find PostgreSQL version with `pg_config --version`
433
434Unable to parse name from string
435
436Unable to parse version from string
437
438Failed to parse PostgreSQL version parts from string
439
440=item unknown
441
442Enter a valid PostgreSQL minor version number
443
444=back
445
446=cut
447
448sub minor_version {
449    my $self = shift;
450    return unless $self->{pg_config};
451    # Load data.
452    $get_version->($self) unless exists $self->{'--version'};
453    # Handle an unknown value.
454    $self->{minor} = $self->unknown( key      => 'postgres minor version number',
455                                     callback => $is_int)
456      unless defined $self->{minor};
457    return $self->{minor};
458}
459
460##############################################################################
461
462=head3 patch version
463
464  my $patch_version = $pg->patch_version;
465
466Returns the PostgreSQL patch version number. App::Info::RDBMS::PostgreSQL
467parses the patch version number from the system call C<`pg_config --version`>.
468For example, if C<version()> returns "7.1.2", then this method returns "1".
469
470B<Events:>
471
472=over 4
473
474=item info
475
476Executing `pg_config --version`
477
478=item error
479
480Failed to find PostgreSQL version with `pg_config --version`
481
482Unable to parse name from string
483
484Unable to parse version from string
485
486Failed to parse PostgreSQL version parts from string
487
488=item unknown
489
490Enter a valid PostgreSQL minor version number
491
492=back
493
494=cut
495
496sub patch_version {
497    my $self = shift;
498    return unless $self->{pg_config};
499    # Load data.
500    $get_version->($self) unless exists $self->{'--version'};
501    # Handle an unknown value.
502    $self->{patch} = $self->unknown( key      => 'postgres patch version number',
503                                     callback => $is_int)
504      unless defined $self->{patch};
505    return $self->{patch};
506}
507
508##############################################################################
509
510=head3 executable
511
512  my $exe = $pg->executable;
513
514Returns the full path to the PostgreSQL server executable, which is named
515F<postgres>.  This method does not use the executable names returned by
516C<search_exe_names()>; those executable names are used to search for
517F<pg_config> only (in C<new()>).
518
519When it called, C<executable()> checks for an executable named F<postgres> in
520the directory returned by C<bin_dir()>.
521
522Note that C<executable()> is simply an alias for C<postgres()>.
523
524B<Events:>
525
526=over 4
527
528=item info
529
530Looking for postgres executable
531
532=item confirm
533
534Path to postgres executable?
535
536=item unknown
537
538Path to postgres executable?
539
540=back
541
542=cut
543
544my $find_exe = sub  {
545    my ($self, $key) = @_;
546    my $exe = $key . (WIN32 ? '.exe' : '');
547    my $meth = "search_$key\_names";
548
549    # Find executable.
550    $self->info("Looking for $key");
551
552    unless ($self->{$key}) {
553        my $bin = $self->bin_dir or return;
554        if (my $exe = $u->first_cat_exe([$self->$meth(), $exe], $bin)) {
555            # We found it. Confirm.
556            $self->{$key} = $self->confirm(
557                key      => "path to $key",
558                prompt   => "Path to $key executable?",
559                value    => $exe,
560                callback => sub { -x },
561                error    => 'Not an executable'
562            );
563        } else {
564            # Handle an unknown value.
565            $self->{$key} = $self->unknown(
566                key      => "path to $key",
567                prompt   => "Path to $key executable?",
568                callback => sub { -x },
569                error    => 'Not an executable'
570            );
571        }
572    }
573
574    return $self->{$key};
575};
576
577for my $exe (@EXES) {
578    no strict 'refs';
579    *{$exe} = sub { shift->$find_exe($exe) };
580    *{"search_$exe\_names"} = sub { @{ shift->{"search_$exe\_names"} } }
581}
582
583*executable = \&postgres;
584
585##############################################################################
586
587=head3 bin_dir
588
589  my $bin_dir = $pg->bin_dir;
590
591Returns the PostgreSQL binary directory path. App::Info::RDBMS::PostgreSQL
592gathers the path from the system call C<`pg_config --bindir`>.
593
594B<Events:>
595
596=over 4
597
598=item info
599
600Executing `pg_config --bindir`
601
602=item error
603
604Cannot find bin directory
605
606=item unknown
607
608Enter a valid PostgreSQL bin directory
609
610=back
611
612=cut
613
614# This code reference is used by bin_dir(), lib_dir(), and so_lib_dir() to
615# validate a directory entered by the user.
616my $is_dir = sub { -d };
617
618sub bin_dir {
619    my $self = shift;
620    return unless $self->{pg_config};
621    unless (exists $self->{bin_dir} ) {
622        if (my $dir = $get_data->($self, '--bindir')) {
623            $self->{bin_dir} = $dir;
624        } else {
625            # Handle an unknown value.
626            $self->error("Cannot find bin directory");
627            $self->{bin_dir} = $self->unknown( key      => 'postgres bin dir',
628                                               callback => $is_dir)
629        }
630    }
631
632    return $self->{bin_dir};
633}
634
635##############################################################################
636
637=head3 inc_dir
638
639  my $inc_dir = $pg->inc_dir;
640
641Returns the PostgreSQL include directory path. App::Info::RDBMS::PostgreSQL
642gathers the path from the system call C<`pg_config --includedir`>.
643
644B<Events:>
645
646=over 4
647
648=item info
649
650Executing `pg_config --includedir`
651
652=item error
653
654Cannot find include directory
655
656=item unknown
657
658Enter a valid PostgreSQL include directory
659
660=back
661
662=cut
663
664sub inc_dir {
665    my $self = shift;
666    return unless $self->{pg_config};
667    unless (exists $self->{inc_dir} ) {
668        if (my $dir = $get_data->($self, '--includedir')) {
669            $self->{inc_dir} = $dir;
670        } else {
671            # Handle an unknown value.
672            $self->error("Cannot find include directory");
673            $self->{inc_dir} = $self->unknown( key      => 'postgres include dir',
674                                               callback => $is_dir)
675        }
676    }
677
678    return $self->{inc_dir};
679}
680
681##############################################################################
682
683=head3 lib_dir
684
685  my $lib_dir = $pg->lib_dir;
686
687Returns the PostgreSQL library directory path. App::Info::RDBMS::PostgreSQL
688gathers the path from the system call C<`pg_config --libdir`>.
689
690B<Events:>
691
692=over 4
693
694=item info
695
696Executing `pg_config --libdir`
697
698=item error
699
700Cannot find library directory
701
702=item unknown
703
704Enter a valid PostgreSQL library directory
705
706=back
707
708=cut
709
710sub lib_dir {
711    my $self = shift;
712    return unless $self->{pg_config};
713    unless (exists $self->{lib_dir} ) {
714        if (my $dir = $get_data->($self, '--libdir')) {
715            $self->{lib_dir} = $dir;
716        } else {
717            # Handle an unknown value.
718            $self->error("Cannot find library directory");
719            $self->{lib_dir} = $self->unknown( key      => 'postgres library dir',
720                                               callback => $is_dir)
721        }
722    }
723
724    return $self->{lib_dir};
725}
726
727##############################################################################
728
729=head3 so_lib_dir
730
731  my $so_lib_dir = $pg->so_lib_dir;
732
733Returns the PostgreSQL shared object library directory path.
734App::Info::RDBMS::PostgreSQL gathers the path from the system call
735C<`pg_config --pkglibdir`>.
736
737B<Events:>
738
739=over 4
740
741=item info
742
743Executing `pg_config --pkglibdir`
744
745=item error
746
747Cannot find shared object library directory
748
749=item unknown
750
751Enter a valid PostgreSQL shared object library directory
752
753=back
754
755=cut
756
757# Location of dynamically loadable modules.
758sub so_lib_dir {
759    my $self = shift;
760    return unless $self->{pg_config};
761    unless (exists $self->{so_lib_dir} ) {
762        if (my $dir = $get_data->($self, '--pkglibdir')) {
763            $self->{so_lib_dir} = $dir;
764        } else {
765            # Handle an unknown value.
766            $self->error("Cannot find shared object library directory");
767            $self->{so_lib_dir} =
768              $self->unknown( key      => 'postgres so directory',
769                              callback => $is_dir)
770        }
771    }
772
773    return $self->{so_lib_dir};
774}
775
776##############################################################################
777
778=head3 configure options
779
780  my $configure = $pg->configure;
781
782Returns the options with which the PostgreSQL server was
783configured. App::Info::RDBMS::PostgreSQL gathers the configure data from the
784system call C<`pg_config --configure`>.
785
786B<Events:>
787
788=over 4
789
790=item info
791
792Executing `pg_config --configure`
793
794=item error
795
796Cannot find configure information
797
798=item unknown
799
800Enter PostgreSQL configuration options
801
802=back
803
804=cut
805
806sub configure {
807    my $self = shift;
808    return unless $self->{pg_config};
809    unless (exists $self->{configure} ) {
810        if (my $conf = $get_data->($self, '--configure')) {
811            $self->{configure} = $conf;
812        } else {
813            # Configure can be empty, so just make sure it exists and is
814            # defined. Don't prompt.
815            $self->{configure} = '';
816        }
817    }
818
819    return $self->{configure};
820}
821
822##############################################################################
823
824=head3 home_url
825
826  my $home_url = $pg->home_url;
827
828Returns the PostgreSQL home page URL.
829
830=cut
831
832sub home_url { "http://www.postgresql.org/" }
833
834##############################################################################
835
836=head3 download_url
837
838  my $download_url = $pg->download_url;
839
840Returns the PostgreSQL download URL.
841
842=cut
843
844sub download_url { "http://www.postgresql.org/mirrors-ftp.html" }
845
846##############################################################################
847
848=head3 search_exe_names
849
850  my @search_exe_names = $app->search_exe_names;
851
852Returns a list of possible names for F<pg_config> executable. By default, only
853F<pg_config> is returned (or F<pg_config.exe> on Win32).
854
855Note that this method is not used to search for the PostgreSQL server
856executable, only F<pg_config>.
857
858=cut
859
860sub search_exe_names {
861    my $self = shift;
862    my $exe = 'pg_config';
863    $exe .= '.exe' if WIN32;
864    return ($self->SUPER::search_exe_names, $exe);
865}
866
867##############################################################################
868
869=head3 search_bin_dirs
870
871  my @search_bin_dirs = $app->search_bin_dirs;
872
873Returns a list of possible directories in which to search an executable. Used
874by the C<new()> constructor to find an executable to execute and collect
875application info. The found directory will also be returned by the C<bin_dir>
876method.
877
878The list of directories by default consists of the path as defined by
879C<< File::Spec->path >>, as well as the following directories:
880
881=over 4
882
883=item $ENV{POSTGRES_HOME}/bin (if $ENV{POSTGRES_HOME} exists)
884
885=item $ENV{POSTGRES_LIB}/../bin (if $ENV{POSTGRES_LIB} exists)
886
887=item /usr/local/pgsql/bin
888
889=item /usr/local/postgres/bin
890
891=item /opt/pgsql/bin
892
893=item /usr/local/bin
894
895=item /usr/local/sbin
896
897=item /usr/bin
898
899=item /usr/sbin
900
901=item /bin
902
903=item C:\Program Files\PostgreSQL\bin
904
905=back
906
907=cut
908
909sub search_bin_dirs {
910    return shift->SUPER::search_bin_dirs,
911      ( exists $ENV{POSTGRES_HOME}
912          ? ($u->catdir($ENV{POSTGRES_HOME}, "bin"))
913          : ()
914      ),
915      ( exists $ENV{POSTGRES_LIB}
916          ? ($u->catdir($ENV{POSTGRES_LIB}, $u->updir, "bin"))
917          : ()
918      ),
919      $u->path,
920      qw(/usr/local/pgsql/bin
921         /usr/local/postgres/bin
922         /usr/lib/postgresql/bin
923         /opt/pgsql/bin
924         /usr/local/bin
925         /usr/local/sbin
926         /usr/bin
927         /usr/sbin
928         /bin),
929      'C:\Program Files\PostgreSQL\bin';
930}
931
932##############################################################################
933
934=head2 Other Executable Methods
935
936These methods function just like the C<executable()> method, except that they
937return different executables. PostgreSQL comes with a fair number of them; we
938provide these methods to provide a path to a subset of them. Each method, when
939called, checks for an executable in the directory returned by C<bin_dir()>.
940The name of the executable must be one of the names returned by the
941corresponding C<search_*_names> method.
942
943The available executable methods are:
944
945=over
946
947=item postgres
948
949=item createdb
950
951=item createlang
952
953=item createuser
954
955=item dropdb
956
957=item droplang
958
959=item dropuser
960
961=item initdb
962
963=item pg_dump
964
965=item pg_dumpall
966
967=item pg_restore
968
969=item postmaster
970
971=item psql
972
973=item vacuumdb
974
975=back
976
977And the corresponding search names methods are:
978
979=over
980
981=item search_postgres_names
982
983=item search_createdb_names
984
985=item search_createlang_names
986
987=item search_createuser_names
988
989=item search_dropd_names
990
991=item search_droplang_names
992
993=item search_dropuser_names
994
995=item search_initdb_names
996
997=item search_pg_dump_names
998
999=item search_pg_dumpall_names
1000
1001=item search_pg_restore_names
1002
1003=item search_postmaster_names
1004
1005=item search_psql_names
1006
1007=item search_vacuumdb_names
1008
1009=back
1010
1011B<Events:>
1012
1013=over 4
1014
1015=item info
1016
1017Looking for executable
1018
1019=item confirm
1020
1021Path to executable?
1022
1023=item unknown
1024
1025Path to executable?
1026
1027=back
1028
1029=cut
1030
10311;
1032__END__
1033
1034=head1 SUPPORT
1035
1036This module is stored in an open L<GitHub
1037repository|http://github.com/theory/app-info/>. Feel free to fork and
1038contribute!
1039
1040Please file bug reports via L<GitHub
1041Issues|http://github.com/theory/app-info/issues/> or by sending mail to
1042L<bug-App-Info@rt.cpan.org|mailto:bug-App-Info@rt.cpan.org>.
1043
1044=head1 AUTHOR
1045
1046David E. Wheeler <david@justatheory.com> based on code by Sam Tregar
1047<sam@tregar.com>.
1048
1049=head1 SEE ALSO
1050
1051L<App::Info|App::Info> documents the event handling interface.
1052
1053L<App::Info::RDBMS|App::Info::RDBMS> is the App::Info::RDBMS::PostgreSQL
1054parent class.
1055
1056L<DBD::Pg|DBD::Pg> is the L<DBI|DBI> driver for connecting to PostgreSQL
1057databases.
1058
1059L<http://www.postgresql.org/> is the PostgreSQL home page.
1060
1061=head1 COPYRIGHT AND LICENSE
1062
1063Copyright (c) 2002-2011, David E. Wheeler. Some Rights Reserved.
1064
1065This module is free software; you can redistribute it and/or modify it under the
1066same terms as Perl itself.
1067
1068=cut
1069