discourse-legacysite-perl/site/glist/lib/GT/Session/TempTable.pm
2024-06-17 21:49:12 +10:00

311 lines
9.1 KiB
Perl

package GT::Session::TempTable;
# ===============================================================
# Pragmas
use strict;
use vars qw| $ATTRIBS @ISA $ERRORS |;
# Internal nodules
use GT::Base;
use GT::SQL;
use GT::MD5 qw| md5_hex |;
# Global variable init
@ISA = qw| GT::Base |;
$ATTRIBS = {
id => undef,
tb => undef,
def_path => '',
db => undef,
set_name => 'Set_Sessions',
create_session => undef,
delete_session => undef,
seconds => 60*60,
sid => ''
};
$ERRORS = {
'NODB' => "No GT::SQL object, need to set 'db' or 'def_path'",
'NOCS' => "No session creation hook specified",
'CSNOTCODE' => "Session creation hook is not a coderef",
'NOSID' => "No session ID",
'BADDATA' => "Invalid data in session: '%s'. Reason: '%s'",
'CLASSFUNC' => "This is a class function only.",
'INVALIDSESSION'=> "Invalid session id: '%s'.",
'BADARGS' => "Invalid arguments: %s",
};
sub install {
#-------------------------------------------------------------------------------
# creates the controller table
#
my $self = shift;
my $DB = $self->_db();
my $c = $DB->creator( $self->{set_name} );
$c->cols(
ID => { pos => 1, type => 'INT', not_null => 1, unsigned => 1, regex => '^d+$' },
SessID => { pos => 2, type => 'CHAR', size => 100, not_null => 1 },
SessTable => { pos => 3, type => 'CHAR', size => 100, not_null => 1 },
Timestmp => { pos => 4, type => 'TIMESTAMP', time_check => 1 }
);
$c->pk('ID');
$c->ai('ID');
$c->create('force');
$c->set_defaults();
$c->save_schema();
}
sub uninstall {
#-------------------------------------------------------------------------------
# drops the controller table along with all the
#
my $self = shift;
my $DB = $self->_db() or return;
my $err = 1;
# drop all the associated temp tables...,
eval {
my $tb = $DB->table( $self->{set_name} );
my $sth = $tb->select( [ 'SessTable' ] );
while ( my $aref = $sth->fetchrow_arrayref() ) {
my $table_name = $aref->[0];
eval {
my $e = $DB->editor( $table_name );
$e->drop_table("remove") or die "Can't drop table";
};
$@ and $err = undef;
}
# now drop the master control table
my $e = $DB->editor( $self->{set_name});
$e->drop_table("remove") or die "Can't drop table";
};
return $@ ? undef : 1;
}
sub new_set {
#-------------------------------------------------------------------------------
# creates a new temp table
#
my $self = shift;
my $create_session = ( ref $_[0] eq 'CODE' ? shift : $self->{create_session} ) or return $self->error( 'NOCS', 'WARN' );
my $sid = $self->{id} or return $self->error( 'NOSID', 'WARN' );
my $DB = $self->_db();
my $Session = $DB->table( $self->{set_name} );
# create a new sesson
my $table_name = generate_session_id();
my $newid = $Session->add({ SessTable => $table_name, SessID => $sid }) or return;
# create the new table, extra parameters are passed into the create_session sub procedure
if ( my $result = &{$create_session}( $DB, $table_name, $newid, @_ ) ) {
my $tbl = $DB->table( $table_name );
return wantarray ? ( $tbl, $newid ) : $tbl;
}
else {
$Session->delete($newid);
return;
}
}
sub get_set {
#-------------------------------------------------------------------------------
# returns a table reference to the sethandle
#
my $self = shift;
my $set_id = shift or return;
my $sid = $self->{id} or return $self->error( 'NOSID', 'WARN' );
my $DB = $self->_db();
my $Session = $DB->table( $self->{set_name} ) or return;
my $sth = $Session->select({ ID => $set_id, SessID => $sid }) or return undef;
my $href = $sth->fetchrow_hashref() or return undef;
$href->{Timestmp} = \'NOW()';
$Session->update( $href );
if ( my $table_name = $href->{'SessTable'} ) {
my $tbl = $DB->table( $table_name );
return $tbl;
}
else {
return;
}
}
sub list_sets {
#-------------------------------------------------------------------------------
# returns a hashref of ID => tablenames, of tables that the current session ID owns
#
my $self = shift;
my $DB = $self->_db();
my $sid = $self->{id} or return $self->error( 'NOSID', 'WARN' );
my $Session = $DB->table( $self->{set_name} ) or return;
my $sth = $Session->select({ SessID => $sid }, [ 'ID', 'SessTable' ]);
my $list = {};
while ( my $aref = $sth->fetchrow_arrayref() ) {
my ( $id, $sesstable ) = @{$aref};
$list->{$id} = $sesstable;
}
return $list;
}
sub delete {
#-------------------------------------------------------------------------------
# deletes all sets associated with the session
#
my $self = shift;
my $DB = $self->_db();
my $Session = $DB->table( $self->{set_name} ) or return;
my $sid = ( shift || $self->{id} ) or return $self->error( 'NOSID', 'WARN' );
my $sth = $Session->select({ SessID => $sid },['SessTable']);
# delete all created temp tables
while ( my $aref = $sth->fetchrow_arrayref() ) {
my $tbl_name = $aref->[0];
eval {
my $e = $DB->editor($tbl_name);
$e->drop_table( "remove" );
}
}
$Session->delete({ SessID => $sid });
# cheap workaround
shift or $self->GT::Session::SQL::delete();
}
sub delete_set {
#-------------------------------------------------------------------------------
# deletes a single set
#
my $self = shift;
my $set_id = shift;
my $DB = $self->_db();
my $Session = $DB->table( $self->{set_name} ) or return;
my $sid = $self->{id} or return $self->error( 'NOSID', 'WARN' );
my $sth = $Session->select( { ID => $set_id, SessID => $sid }, [ 'SessTable' ] ) or return;
my $aref = $sth->fetchrow_arrayref() or return;
if ($aref->[0]) {
my $e = $DB->editor($aref->[0]);
$e->drop_table();
$Session->delete( { ID => $set_id } );
}
}
sub cleanup {
#-------------------------------------------------------------------------------
my $self = shift;
my $seconds = shift || $self->{seconds};
if ($seconds == 0) {
$self->debug ("Cleanup not erasing anything, seconds set to 0.") if ($self->{_debug});
return;
}
my $DB = $self->_db() or return;
my $tb = $DB->table( $self->{set_name} );
defined $seconds or ($seconds = 3600);
my $new_sec = time - $seconds;
my @time = localtime ($new_sec);
my $date_str = sprintf ("%4d-%02d-%02d %02d:%02d:%02d",
$time[5] + 1900, $time[4] + 1, $time[3], $time[2], $time[1], $time[0]);
my $sth = $tb->select( GT::SQL::Condition->new('Timestmp', '<', $date_str), [ 'SessID' ] ) or return $self->error ($GT::SQL::error);
while ( my $aref = $sth->fetchrow_arrayref() ) {
$self->delete( $aref->[0], 1 );
}
$tb->delete (GT::SQL::Condition->new ('Timestmp', '<', $date_str)) or return $self->error ($GT::SQL::error);
return 1;
}
sub _db {
#-------------------------------------------------------------------------------
# returns a database handle
#
my $self = shift;
if ( my $db = $self->{'db'} ) {
return $db;
}
elsif ( my $def_path = $self->{'def_path'} ) {
$db = GT::SQL->new( $def_path );
return $db;
}
else {
$self->error( 'NODB', 'FATAL' );
}
}
sub generate_session_id {
# ---------------------------------------------------------------
# Generates a session id.
#
return md5_hex( time . $$ . rand (16000) );
}
1;
__END__
=head1 NAME
GT::Session::TempTable - A session management module, subclassing GT::Session::SQL providing temp table support
=head1 SYNOPSIS
Create a session:
my $session = new GT::Session::TempTable({
db => GT::SQL->new( '/path/to/defs' ),
def_path => '/path/to/defs',
create_session => \&create_table_sub
});
Create temp table controller table. (do once before using this module)
$session->initial_create();
Create a new temp table:
my ( $GT_SQL_Table_ref, $tmp_id ) = $session->new_set();
Get the GT::SQL::Table ref to a previous table:
my $GT_SQL_Table_ref = $session->get_set( $tmp_id );
List all the sets for current session:
my $href = $session->list_sets();
Save data with the session:
$session->data ("Save this information!");
Load a session.
my $session = new GT::Session::TempTable ( $id ) or die "Can't load session: '$id'."
Delete a session:
$session->delete();
Delete a table set:
$session->delete_set( $tmp_id );
Cleanup old sessions, takes argument of number of seconds old.
$session->cleanup ( 5000 );
=cut