package Lire::DerivedSchema;

# vim:syntax=perl

use strict;

use base qw/ Lire::DlfSchema /;

use Lire::DataTypes qw/ check_superservice check_xml_name /;
use Lire::Utils qw/check_param check_object_param/;

use Carp;

=pod

=head1 NAME

Lire::DerivedSchema - Defines a schema for which the records are derived from another schema

=head1 SYNOPSIS

  my $schema = Lire::DlfSchema::load_schema( 'www-user_session' );
  my $fields = $schema->fields();

=head1 DESCRIPTION

A Lire::DerivedSchema defines a schema for which the records are
computed by an analyser using the records of a base schema. An example
of a derived schema is the www-user_session schema which computes
session information from the records in the www schema.

=cut

sub new {
    my ( $class, %attr ) = @_;

    check_param( $attr{'id'}, 'id', \&check_xml_name );

    my ( $super) =  $attr{'id'} =~ /^(\w+)-/
      or croak "cannot find superservice in id: '$attr{'id'}'";

    croak "invalid superservice in id: $super"
      unless check_superservice( $super );

    croak "missing base-schema attribute"
      unless exists $attr{'base-schema'};
    my $schema = Lire::DlfSchema::load_schema( $attr{'base-schema'} );
    croak "superservice of base schema doesn't match one in id: ",
      $schema->superservice(), " != $super"
	if $schema->superservice() ne $super;

    croak "base schema cannot be an extended schema: $attr{'base-schema'}"
      if $schema->isa( 'Lire::ExtendedSchema' );

    check_param( $attr{'timestamp'}, 'timestamp' );

    my $self = $class->SUPER::new( 'timestamp'	=> $attr{'timestamp'},
				   'superservice' => $super,
				 );
    $self->{'id'} = $attr{'id'};
    $self->{'base'} = $schema;

    return $self;
}

=pod

=head2 base()

Returns the Lire::DlfSchema object from which this schema's records are
derived.

=cut

sub base {
    return $_[0]->{'base'};
}

=pod

=head2 can_join_schema( $schema ) 

Returns true if $schema can be joined with this schema. For a
DerivedSchema, this will be true only when $schema is an
ExtendedSchema of this schema or this schema's base. It is also
possible to join the DerivedSchema with its base.

=cut

sub can_join_schema {
    my ( $self, $schema ) = @_;

    check_object_param( $schema, 'schema', 'Lire::DlfSchema' );

    return ( $schema eq $self->base()
             || ( $schema->isa( 'Lire::ExtendedSchema' )
                  && ( $schema->base() eq $self
                       || $schema->base() eq $self->base() ) ) );
}

sub module {
    my $self = $_[0];

    my $module = $self->{'module'};
    eval "use $module;";
    croak "error loading $module module: $@\n" if $@;

    my $analyser = eval {
	no strict 'refs';
	$module->new( $self )
    };
    croak "error creating $module instance: $@" if $@;
    croak "$module module isn't of type Lire::DerivedSchema::DerivedRecordsCreator"
      unless UNIVERSAL::isa( $self->{'module'},
			     "Lire::DerivedSchema::DerivedRecordsCreator",
			   );
    return $analyser;
}

sub create_sql_schema {
    my ( $self, $store ) = @_;

    unless ( $store->has_dlf_stream( $self->base()->id() ) ) {
        $self->base()->create_sql_schema( $store );
    }
    $self->SUPER::create_sql_schema( $store );

    $store->_dbh()->do( sprintf( "CREATE TABLE %s ( src_id INT, link_id INT )",
                           $self->sql_table( "", "_links" ) ) );

    $store->_dbh()->do( sprintf( "CREATE INDEX %s ON %s ( src_id )",
                               $self->sql_table( "", "_links_src_id_idx" ),
                               $self->sql_table( "", "_links" ) ) );

    $store->_dbh()->do( sprintf( "CREATE INDEX %s ON %s ( link_id )",
                               $self->sql_table( "", "_links_link_id_idx" ),
                               $self->sql_table( "", "_links" ) ) );

    $store->_dbh()->do( sprintf( q{CREATE TRIGGER %s AFTER DELETE ON %s
BEGIN
    DELETE FROM %s WHERE dlf_id IN ( SELECT src_id FROM %s WHERE src_id = old.dlf_id );
END
},
                                 $self->sql_table( '', '_delete_trigger' ),
                                 $self->base()->sql_table(),
                                 $self->sql_table(),
                                 $self->sql_table( '', '_links' )
                               ) );

    $store->_dbh()->do( sprintf( q{CREATE TRIGGER %s AFTER DELETE ON %s
BEGIN
    DELETE FROM %s WHERE src_id = old.dlf_id;
END
},
                                $self->sql_table( '', '_links_delete_trigger'),
                                $self->sql_table(),
                                $self->sql_table( '', '_links' ) ) );
    return;
}

sub sql_link_insert_query {
    my $self = $_[0];

    return sprintf( 'INSERT INTO %s (src_id, link_id) VALUES(?, ?)',
                    $self->sql_table( '', '_links' ) );
}

# keep perl happy
1;

__END__

=pod

=head1 AUTHOR

Francis J. Lacoste <flacoste@logreport.org>

=head1 VERSION

$Id: DerivedSchema.pm,v 1.17 2004/06/08 13:51:33 flacoste Exp $

=head1 COPYRIGHT

Copyright (C) 2001, 2004 Stichting LogReport Foundation LogReport@LogReport.org

This file is part of Lire.

Lire is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program (see COPYING); if not, check with
http://www.gnu.org/copyleft/gpl.html or write to the Free Software 
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.

=cut
