File Coverage

blib/lib/Wiki/Toolkit/Setup/Pg.pm
Criterion Covered Total %
statement 15 96 15.6
branch 0 46 0.0
condition 0 21 0.0
subroutine 5 11 45.4
pod 2 2 100.0
total 22 176 12.5


line stmt bran cond sub pod time code
1             package Wiki::Toolkit::Setup::Pg;
2              
3 2     2   1864 use strict;
  2         4  
  2         71  
4              
5 2     2   10 use vars qw( @ISA $VERSION $SCHEMA_VERSION );
  2         5  
  2         116  
6              
7 2     2   445 use Wiki::Toolkit::Setup::Database;
  2         5  
  2         78  
8              
9             @ISA = qw( Wiki::Toolkit::Setup::Database );
10             $VERSION = '0.11';
11              
12 2     2   13 use DBI;
  2         4  
  2         65  
13 2     2   10 use Carp;
  2         5  
  2         2868  
14              
15             $SCHEMA_VERSION = $VERSION*100;
16              
17             my $create_sql = {
18             10 => {
19             schema_info => [ qq|
20             CREATE TABLE schema_info (
21             version integer NOT NULL default 0
22             )
23             |, qq|
24             INSERT INTO schema_info VALUES (10)
25             | ],
26              
27             node => [ qq|
28             CREATE SEQUENCE node_seq
29             |, qq|
30             CREATE TABLE node (
31             id integer NOT NULL DEFAULT NEXTVAL('node_seq'),
32             name varchar(200) NOT NULL DEFAULT '',
33             version integer NOT NULL default 0,
34             text text NOT NULL default '',
35             modified timestamp without time zone default NULL,
36             moderate boolean NOT NULL default '0',
37             CONSTRAINT pk_id PRIMARY KEY (id)
38             )
39             |, qq|
40             CREATE UNIQUE INDEX node_name ON node (name)
41             | ],
42              
43             content => [ qq|
44             CREATE TABLE content (
45             node_id integer NOT NULL,
46             version integer NOT NULL default 0,
47             text text NOT NULL default '',
48             modified timestamp without time zone default NULL,
49             comment text NOT NULL default '',
50             moderated boolean NOT NULL default '1',
51             verified timestamp without time zone default NULL,
52             verified_info text NOT NULL default '',
53             CONSTRAINT pk_node_id PRIMARY KEY (node_id,version),
54             CONSTRAINT fk_node_id FOREIGN KEY (node_id) REFERENCES node (id)
55             )
56             | ],
57              
58             internal_links => [ qq|
59             CREATE TABLE internal_links (
60             link_from varchar(200) NOT NULL default '',
61             link_to varchar(200) NOT NULL default ''
62             )
63             |, qq|
64             CREATE UNIQUE INDEX internal_links_pkey ON internal_links (link_from, link_to)
65             | ],
66              
67             metadata => [ qq|
68             CREATE TABLE metadata (
69             node_id integer NOT NULL,
70             version integer NOT NULL default 0,
71             metadata_type varchar(200) NOT NULL DEFAULT '',
72             metadata_value text NOT NULL DEFAULT '',
73             CONSTRAINT fk_node_id FOREIGN KEY (node_id) REFERENCES node (id)
74             )
75             |, qq|
76             CREATE INDEX metadata_index ON metadata (node_id, version, metadata_type, metadata_value)
77             | ]
78             },
79             11 => {
80             schema_info => [ qq|
81             CREATE TABLE schema_info (
82             version integer NOT NULL default 0
83             )
84             |, qq|
85             INSERT INTO schema_info VALUES (11)
86             | ],
87              
88             node => [ qq|
89             CREATE SEQUENCE node_seq
90             |, qq|
91             CREATE TABLE node (
92             id integer NOT NULL DEFAULT NEXTVAL('node_seq'),
93             name varchar(200) NOT NULL DEFAULT '',
94             version integer NOT NULL default 0,
95             text text NOT NULL,
96             modified timestamp without time zone default NULL,
97             moderate boolean NOT NULL default '0',
98             CONSTRAINT pk_id PRIMARY KEY (id)
99             )
100             |, qq|
101             CREATE UNIQUE INDEX node_name ON node (name)
102             | ],
103              
104             content => [ qq|
105             CREATE TABLE content (
106             node_id integer NOT NULL,
107             version integer NOT NULL default 0,
108             text text NOT NULL,
109             modified timestamp without time zone default NULL,
110             comment text,
111             moderated boolean NOT NULL default '1',
112             verified timestamp without time zone default NULL,
113             verified_info text,
114             CONSTRAINT pk_node_id PRIMARY KEY (node_id,version),
115             CONSTRAINT fk_node_id FOREIGN KEY (node_id) REFERENCES node (id)
116             )
117             | ],
118              
119             internal_links => [ qq|
120             CREATE TABLE internal_links (
121             link_from varchar(200) NOT NULL default '',
122             link_to varchar(200) NOT NULL default ''
123             )
124             |, qq|
125             CREATE UNIQUE INDEX internal_links_pkey ON internal_links (link_from, link_to)
126             | ],
127              
128             metadata => [ qq|
129             CREATE TABLE metadata (
130             node_id integer NOT NULL,
131             version integer NOT NULL default 0,
132             metadata_type varchar(200) NOT NULL DEFAULT '',
133             metadata_value text NOT NULL,
134             CONSTRAINT fk_node_id FOREIGN KEY (node_id) REFERENCES node (id)
135             )
136             |, qq|
137             CREATE INDEX metadata_index ON metadata (node_id, version, metadata_type, metadata_value)
138             | ]
139             },
140             };
141              
142             my %upgrades = (
143             '10_to_11' => [
144             qq|
145             ALTER TABLE node ALTER COLUMN text DROP DEFAULT
146             |, qq|
147             ALTER TABLE content ALTER COLUMN text DROP DEFAULT
148             |, qq|
149             ALTER TABLE content ALTER COLUMN comment DROP DEFAULT
150             |, qq|
151             ALTER TABLE content ALTER COLUMN comment DROP NOT NULL
152             |, qq|
153             ALTER TABLE content ALTER COLUMN verified_info DROP DEFAULT
154             |, qq|
155             ALTER TABLE content ALTER COLUMN verified_info DROP NOT NULL
156             |, qq|
157             ALTER TABLE metadata ALTER COLUMN metadata_value DROP DEFAULT
158             |, qq|
159             UPDATE schema_info SET version = 11
160             |,
161             ],
162             );
163              
164             =head1 NAME
165              
166             Wiki::Toolkit::Setup::Pg - Set up tables for a Wiki::Toolkit store in a Postgres database.
167              
168             =head1 SYNOPSIS
169              
170             use Wiki::Toolkit::Setup::Pg;
171             Wiki::Toolkit::Setup::Pg::setup($dbname, $dbuser, $dbpass, $dbhost);
172              
173             Omit $dbhost if the database is local.
174              
175             =head1 DESCRIPTION
176              
177             Set up a Postgres database for use as a Wiki::Toolkit store.
178              
179             =head1 FUNCTIONS
180              
181             =over 4
182              
183             =item B
184              
185             use Wiki::Toolkit::Setup::Pg;
186             Wiki::Toolkit::Setup::Pg::setup($dbname, $dbuser, $dbpass, $dbhost);
187              
188             or
189              
190             Wiki::Toolkit::Setup::Pg::setup( $dbh );
191              
192             You can either provide an active database handle C<$dbh> or connection
193             parameters.
194              
195             If you provide connection parameters the following arguments are
196             mandatory -- the database name, the username and the password. The
197             username must be able to create and drop tables in the database.
198              
199             The $dbhost argument is optional -- omit it if the database is local.
200              
201             B If a table that the module wants to create already exists,
202             C will leave it alone. This means that you can safely run this
203             on an existing L database to bring the schema up to date
204             with the current L version. If you wish to completely start
205             again with a fresh database, run C first.
206              
207             =cut
208              
209             sub setup {
210 0     0 1   my @args = @_;
211 0           my $dbh = _get_dbh( @args );
212 0           my $disconnect_required = _disconnect_required( @args );
213 0   0       my $wanted_schema = _get_wanted_schema( @args ) || $SCHEMA_VERSION;
214              
215             die "No schema information for requested schema version $wanted_schema\n"
216 0 0         unless $create_sql->{$wanted_schema};
217              
218             # Check whether tables exist
219             my $sql = "SELECT tablename FROM pg_tables
220             WHERE tablename in ("
221 0           . join( ",", map { $dbh->quote($_) } keys %{$create_sql->{$wanted_schema}} ) . ")";
  0            
  0            
222 0 0         my $sth = $dbh->prepare($sql) or croak $dbh->errstr;
223 0           $sth->execute;
224 0           my %tables;
225 0           while ( my $table = $sth->fetchrow_array ) {
226 0 0         exists $create_sql->{$wanted_schema}->{$table} and $tables{$table} = 1;
227             }
228              
229             # Do we need to upgrade the schema of existing tables?
230             # (Don't check if no tables currently exist)
231 0           my $upgrade_schema;
232 0 0         if(scalar keys %tables > 0) {
233 0           $upgrade_schema = Wiki::Toolkit::Setup::Database::get_database_upgrade_required($dbh,$wanted_schema);
234             } else {
235 0           print "Skipping schema upgrade check - no tables found\n";
236             }
237              
238             # Set up tables if not found
239 0           foreach my $required ( reverse sort keys %{$create_sql->{$wanted_schema}} ) {
  0            
240 0 0         if ( $tables{$required} ) {
241 0           print "Table $required already exists... skipping...\n";
242             } else {
243 0           print "Creating table $required... done\n";
244 0           foreach my $sql ( @{ $create_sql->{$wanted_schema}->{$required} } ) {
  0            
245 0 0         $dbh->do($sql) or croak $dbh->errstr;
246             }
247             }
248             }
249              
250             # Do the upgrade if required
251 0 0         if($upgrade_schema) {
252 0           print "Upgrading schema: $upgrade_schema\n";
253 0           my @updates = @{$upgrades{$upgrade_schema}};
  0            
254 0           foreach my $update (@updates) {
255 0 0         if(ref($update) eq "CODE") {
    0          
256 0           &$update($dbh);
257             } elsif(ref($update) eq "ARRAY") {
258 0           foreach my $nupdate (@$update) {
259 0           $dbh->do($nupdate);
260             }
261             } else {
262 0           $dbh->do($update);
263             }
264             }
265             }
266              
267             # Clean up if we made our own dbh.
268 0 0         $dbh->disconnect if $disconnect_required;
269             }
270              
271             =item B
272              
273             use Wiki::Toolkit::Setup::Pg;
274              
275             # Clear out all Wiki::Toolkit tables from the database.
276             Wiki::Toolkit::Setup::Pg::cleardb($dbname, $dbuser, $dbpass, $dbhost);
277              
278             or
279              
280             Wiki::Toolkit::Setup::Pg::cleardb( $dbh );
281              
282             You can either provide an active database handle C<$dbh> or connection
283             parameters.
284              
285             If you provide connection parameters the following arguments are
286             mandatory -- the database name, the username and the password. The
287             username must be able to drop tables in the database.
288              
289             The $dbhost argument is optional -- omit it if the database is local.
290              
291             Clears out all L store tables from the database. B
292             that this will lose all your data; you probably only want to use this
293             for testing purposes or if you really screwed up somewhere. Note also
294             that it doesn't touch any L search backend tables; if you
295             have any of those in the same or a different database see
296             L or L, depending on
297             which search backend you're using.
298              
299             =cut
300              
301             sub cleardb {
302 0     0 1   my @args = @_;
303 0           my $dbh = _get_dbh( @args );
304 0           my $disconnect_required = _disconnect_required( @args );
305              
306 0           print "Dropping tables... ";
307             my $sql = "SELECT tablename FROM pg_tables
308             WHERE tablename in ("
309 0           . join( ",", map { $dbh->quote($_) } keys %{$create_sql->{$SCHEMA_VERSION}} ) . ")";
  0            
  0            
310 0           foreach my $tableref (@{$dbh->selectall_arrayref($sql)}) {
  0            
311 0 0         $dbh->do("DROP TABLE $tableref->[0] CASCADE") or croak $dbh->errstr;
312             }
313              
314 0           $sql = "SELECT relname FROM pg_statio_all_sequences
315             WHERE relname = 'node_seq'";
316 0           foreach my $seqref (@{$dbh->selectall_arrayref($sql)}) {
  0            
317 0 0         $dbh->do("DROP SEQUENCE $seqref->[0]") or croak $dbh->errstr;
318             }
319              
320 0           print "done\n";
321              
322             # Clean up if we made our own dbh.
323 0 0         $dbh->disconnect if $disconnect_required;
324             }
325              
326             sub _get_dbh {
327             # Database handle passed in.
328 0 0 0 0     if ( ref $_[0] and ref $_[0] eq 'DBI::db' ) {
329 0           return $_[0];
330             }
331              
332             # Args passed as hashref.
333 0 0 0       if ( ref $_[0] and ref $_[0] eq 'HASH' ) {
334 0           my %args = %{$_[0]};
  0            
335 0 0         if ( $args{dbh} ) {
336 0           return $args{dbh};
337             } else {
338 0           return _make_dbh( %args );
339             }
340             }
341              
342             # Args passed as list of connection details.
343 0           return _make_dbh(
344             dbname => $_[0],
345             dbuser => $_[1],
346             dbpass => $_[2],
347             dbhost => $_[3],
348             );
349             }
350              
351             sub _get_wanted_schema {
352             # Database handle passed in.
353 0 0 0 0     if ( ref $_[0] and ref $_[0] eq 'DBI::db' ) {
354 0           return undef;
355             }
356              
357             # Args passed as hashref.
358 0 0 0       if ( ref $_[0] and ref $_[0] eq 'HASH' ) {
359 0           my %args = %{$_[0]};
  0            
360 0           return $args{wanted_schema};
361             }
362             }
363              
364             sub _disconnect_required {
365             # Database handle passed in.
366 0 0 0 0     if ( ref $_[0] and ref $_[0] eq 'DBI::db' ) {
367 0           return 0;
368             }
369              
370             # Args passed as hashref.
371 0 0 0       if ( ref $_[0] and ref $_[0] eq 'HASH' ) {
372 0           my %args = %{$_[0]};
  0            
373 0 0         if ( $args{dbh} ) {
374 0           return 0;
375             } else {
376 0           return 1;
377             }
378             }
379              
380             # Args passed as list of connection details.
381 0           return 1;
382             }
383              
384             sub _make_dbh {
385 0     0     my %args = @_;
386 0           my $dsn = "dbi:Pg:dbname=$args{dbname}";
387 0 0         $dsn .= ";host=$args{dbhost}" if $args{dbhost};
388             my $dbh = DBI->connect($dsn, $args{dbuser}, $args{dbpass},
389 0 0         { PrintError => 1, RaiseError => 1,
390             AutoCommit => 1 } )
391             or croak DBI::errstr;
392 0           return $dbh;
393             }
394              
395             =back
396              
397             =head1 ALTERNATIVE CALLING SYNTAX
398              
399             As requested by Podmaster. Instead of passing arguments to the methods as
400              
401             ($dbname, $dbuser, $dbpass, $dbhost)
402              
403             you can pass them as
404              
405             ( { dbname => $dbname,
406             dbuser => $dbuser,
407             dbpass => $dbpass,
408             dbhost => $dbhost
409             }
410             )
411              
412             or indeed as
413              
414             ( { dbh => $dbh } )
415              
416             Note that's a hashref, not a hash.
417              
418             =head1 AUTHOR
419              
420             Kake Pugh (kake@earth.li).
421              
422             =head1 COPYRIGHT
423              
424             Copyright (C) 2002-2004 Kake Pugh. All Rights Reserved.
425             Copyright (C) 2006-2008 the Wiki::Toolkit team. All Rights Reserved.
426              
427             This module is free software; you can redistribute it and/or modify it
428             under the same terms as Perl itself.
429              
430             =head1 SEE ALSO
431              
432             L, L, L
433              
434             =cut
435              
436             1;