Skip to content

Commit

Permalink
Item9808: automatically populate the schema with META read from saved…
Browse files Browse the repository at this point in the history
… topics. Add an override to allow loading of unregistered META.

git-svn-id: http://svn.foswiki.org/trunk/DBIStoreContrib@17298 0b4bb1d4-4e5a-0410-9cc4-b2b747904278
  • Loading branch information
CrawfordCurrie authored and CrawfordCurrie committed Feb 24, 2014
1 parent 7536be1 commit b323f7c
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 22 deletions.
8 changes: 7 additions & 1 deletion lib/Foswiki/Contrib/DBIStoreContrib/Config.spec
Expand Up @@ -19,4 +19,10 @@ $Foswiki::cfg{Plugins}{DBIStorePlugin}{Enabled} = 0;
# Where to find the PCRE library for SQLite. Only used by SQLite. It is
# installed on Debian Linux using apt-get install sqlite3-pcre
# (or similar on other systems).
$Foswiki::cfg{Extensions}{DBIStoreContrib}{SQLite}{PCRE} = '/usr/lib/sqlite3/pcre.so';
$Foswiki::cfg{Extensions}{DBIStoreContrib}{SQLite}{PCRE} = '/usr/lib/sqlite3/pcre.so';
# **BOOLEAN**
# Set to true to automatically create new tables when unregistered META is
# encountered in topic text. This should not normally be required, as plugins
# should register all META that they create. Note that only META:NAME where
# NAME matches /^[A-Z][A_Z0-9_]+$/ will be loaded.
$Foswiki::cfg{Extensions}{DBIStoreContrib}{AutoloadUnknownMETA} = 0;
65 changes: 47 additions & 18 deletions lib/Foswiki/Contrib/DBIStoreContrib/DBIStore.pm
Expand Up @@ -20,11 +20,12 @@ database (on the other side of DBI).
use strict;
use warnings;

use DBI;
use Foswiki::Meta;
use Error ':try';
use Assert;
use Encode;
use DBI;

use Foswiki::Meta ();
use Foswiki::Contrib::DBIStoreContrib ();

use constant MONITOR => Foswiki::Contrib::DBIStoreContrib::MONITOR;

Expand All @@ -33,7 +34,9 @@ our $db; # singleton instance of this class
our $personality; # personality module for the selected DSN
our ( $CQ, $TEXT );

our @TABLES = keys(%Foswiki::Meta::VALIDATE); # META: types
# META: types. We need to populate this as late as possible, to take
# account of calles to registerMeta
our $TABLES; # META: types

# @ISA not used, as its set by magic, and we don't want to import more functions
# our @ISA = ('Foswiki::Store::Store');
Expand Down Expand Up @@ -91,7 +94,8 @@ sub _connect {
# organising it as a hash keyed on table name e.g. 'FILEATTACHMENT'
# and sub-keyed on attribute name e.g. 'name'
$this->{schema} = {};
foreach my $type (@TABLES) {
$TABLES ||= [ keys(%Foswiki::Meta::VALIDATE) ];
foreach my $type (@$TABLES) {
my @keys;
foreach my $g (qw(require allow other)) {
if ( defined $Foswiki::Meta::VALIDATE{$type}->{$g} ) {
Expand Down Expand Up @@ -167,7 +171,8 @@ SQL

# If it's not a default table, add it to the list of tables
# (unless it's already there).
push( @TABLES, $t ) unless grep { $t } @TABLES;
$TABLES ||= [ keys(%Foswiki::Meta::VALIDATE) ];
push( @$TABLES, $t ) unless grep { $t } @$TABLES;
}

# Create all the base tables in the DB (including all default META: tables) - PRIVATE
Expand Down Expand Up @@ -195,7 +200,8 @@ CREATE TABLE metatypes (
SQL

# Create the tables for each known META: type
foreach my $t (@TABLES) {
$TABLES ||= [ keys(%Foswiki::Meta::VALIDATE) ];
foreach my $t (@$TABLES) {
print STDERR "Creating table for $t\n" if MONITOR;
$this->_createTableForMETA($t);
}
Expand Down Expand Up @@ -267,7 +273,7 @@ sub recordChange {
$this->insert( $args{newmeta} );
}
elsif ( $args{verb} eq 'update' ) {
$this->_update( $args{oldmeta}, $args{newmeta} );
$this->update( $args{oldmeta}, $args{newmeta} );
}
else {

Expand All @@ -284,7 +290,7 @@ sub update {
eval {
$this->_inner_remove($old);
$this->_inner_insert( $new || $old );
$this->{handle}->do('COMMIT');
$this->{handle}->do('COMMIT') if personality()->{requires_COMMIT};
};
if ($@) {
print STDERR "$@\n" if MONITOR;
Expand Down Expand Up @@ -317,11 +323,31 @@ sub _inner_insert {
foreach my $type ( keys %$mo ) {

# Make sure it's registered.
next unless ( defined $Foswiki::Meta::VALIDATE{$type} );

# If it's not default, we may have to create the table
$this->_createTableForMETA($type)
unless personality()->table_exists($type);
next
unless ( defined $Foswiki::Meta::VALIDATE{$type}
|| $Foswiki::cfg{Extensions}{DBIStoreContrib}{AutoloadUnknownMETA}
&& $type =~ /^[A-Z][A-Z0-9_]+$/ );

unless ( personality()->table_exists($type) ) {

if ( !$this->{schema}->{$type} ) {

# registerMeta does not declare the columns, so we have
# to guess them.
# Use these entries as a template for the schema. We
# read *all* entries to maximise the chance of getting all
# columns.
my %attrs;
foreach my $item ( $mo->find($type) ) {
foreach ( keys(%$item) ) {
$attrs{$_} = 1;
}
}
$this->{schema}->{$type} = \%attrs;
print STDERR "Grazed new table $type\n" if MONITOR;
}
$this->_createTableForMETA($type);
}

# Insert this row
my $data = $mo->{$type};
Expand All @@ -347,7 +373,7 @@ sub insert {

eval {
$this->_inner_insert($mo);
$this->{handle}->do('COMMIT');
$this->{handle}->do('COMMIT') if personality()->{requires_COMMIT};
};
if ($@) {
print STDERR "$@\n" if MONITOR;
Expand All @@ -365,8 +391,11 @@ sub _inner_remove {

my $tid = $tids->[0];
print STDERR "\tRemove $tid\n" if MONITOR;
foreach my $table ( 'topic', @TABLES ) {
$this->{handle}->do("DELETE FROM \"$table\" WHERE tid = '$tid'");
$TABLES ||= [ keys(%Foswiki::Meta::VALIDATE) ];
foreach my $table ( 'topic', @$TABLES ) {
if ( personality()->table_exists($table) ) {
$this->{handle}->do("DELETE FROM \"$table\" WHERE tid = '$tid'");
}
}
}

Expand All @@ -376,7 +405,7 @@ sub remove {

eval {
$this->_inner_remove($mo);
$this->{handle}->do('COMMIT');
$this->{handle}->do('COMMIT') if personality()->{requires_COMMIT};
};
if ($@) {
print STDERR "$@\n" if MONITOR;
Expand Down
11 changes: 8 additions & 3 deletions lib/Foswiki/Plugins/DBIStorePlugin.pm
Expand Up @@ -32,18 +32,23 @@ sub initPlugin {
\&Foswiki::Store::QueryAlgorithms::BruteForce::getField;
}

return 1;
}

sub commonTagsHandler {

# Normally preloading only occurs when the DB first connects, which
# only happens when a topic is moved or saved. To short-circuit this,
# the plugin supports the "?dbistore_reset" parameter, which will
# do that chore.
# do that chore. Only admins can call it. It's done this late in
# the pipeline to ensure plugins have had a chance to register META
# requirements.
if ( Foswiki::Func::getRequestObject->param('dbistore_reset')
&& Foswiki::Func::isAnAdmin() )
{
print STDERR "DBIStore resetting\n";
$shim->reset($Foswiki::Plugins::SESSION);
}

return 1;
}

# Store operations that *should* call the relevant store shim functions
Expand Down

0 comments on commit b323f7c

Please sign in to comment.