root/wiki-toolkit/trunk/lib/Wiki/Toolkit/Setup/Pg.pm

Revision 444, 15.4 KB (checked in by dom, 4 years ago)

missing out verified_info

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