DBIx-Recordset-0.26/ 0040755 0000000 0000000 00000000000 10126462365 012666 5 ustar root root DBIx-Recordset-0.26/eg/ 0040755 0000000 0000000 00000000000 10126462365 013261 5 ustar root root DBIx-Recordset-0.26/eg/README 0100644 0000000 0000000 00000000557 07757664232 014162 0 ustar root root DBIx::Recordset - Perl extension for DBI recordsets
EXAMPLES
The file search.htm (HTML::Embperl version) and search.pl (CGI.pm version)
contains a short example for a web page, where you can supply a table name
(and a search condition) to view the whole content or a part of a table.
NOTE: To view search.htm it's easier to use your browser then an ascii
editor.
DBIx-Recordset-0.26/eg/search.pl 0100644 0000000 0000000 00000020340 07757664231 015073 0 ustar root root #!perl
use CGI ;
use DBIx::Recordset ;
$DBIx::Recordset::Debug = 0 ;
$q = new CGI ;
print $q -> header ;
print $q -> start_html (title=>'Example for DBIx::Recordset') ;
print '
DBIx::Recordset Example
' ;
### convert form parameter to hash ###
%fdat = map { $_ => $q -> param($_) } $q -> param ;
$fdat{'!IgnoreEmpty'} = 2 ; # Just to make the condition dialog work
if (!defined ($fdat{'!DataSource'}) || !defined ($fdat{'!Table'})|| defined($fdat{'showdsn'}))
{
#### show entry form to select datasource ####
delete $fdat{'showdsn'} ;
@drvs = DBI-> available_drivers ;
print $q -> startform (-method => GET) ;
print "
First of all you have to specify which database and table you want to access and enter\n" ;
print "the user and password (if required)
For the Datasource you have the following Options: \n" ;
print "1.) choose a DBD driver from the list on the left and hit the Show Datasources button,\n" ;
print "then you can select a Datasource below (if your DBD driver supports the data_sources\n" ;
print "method) \n" ;
print "2.) enter the Data Source directly in the text field below
DBIx-Recordset-0.26/TODO 0100644 0000000 0000000 00000002027 07757664231 013370 0 ustar root root
Features
--------
- Add push, pop, shift, unshift for result array (only available in perl5.005)
- Recheck selected values before UPDATE
- Support cursors, to fetch only those data which is absolutly necessary
- ::Hash -> New record, then retrieve autoincrement value -> force reselect?
- unique names for multiple links to same table
- UPDATE/INSERT/DELETE for mulitple tables [Robert 1.1.2000]
- Readd placeholders [Peter Zeltins 2.2.2000]
- Parameter patch [Garrett Goebel 3.2.2000]
- !DebugLevel !DebugFile [Terrence Brannon 2.4.00]
- Do not insert empty numeric fields [Robert 1.5.00]
- arrayref for tabreleation [Jason R.Smith 18.3.01]
- ListTables Oracle [Peter Gordon 8.11.01]
Bugs
----
- INSERT $rc = undef -> $rc = 1 -> mult linked nested objects (ka aktuell)
- invalidate active statements with links
- sometimes errors are not reported when updateing an array, until
Flush is called [Robert 1.5.00]
- Informix: NAME_lc -> NAME [Yevgeniy Furman 21.6.00]
Tests
-----
Doku
----
- !PrimKey needed for Array Update
DBIx-Recordset-0.26/Database.pm 0100644 0000000 0000000 00000133350 10126271603 014723 0 ustar root root
###################################################################################
#
# DBIx::Recordset - Copyright (c) 1997-2000 Gerald Richter / ECOS
#
# You may distribute under the terms of either the GNU General Public
# License or the Artistic License, as specified in the Perl README file.
#
# THIS IS BETA SOFTWARE!
#
# THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# $Id: Database.pm,v 1.18 2001/07/09 19:59:48 richter Exp $
#
###################################################################################
package DBIx::Database::Base ;
use strict 'vars' ;
use vars qw{$LastErr $LastErrstr *LastErr *LastErrstr *LastError $PreserveCase} ;
*LastErr = \$DBIx::Recordset::LastErr ;
*LastErrstr = \$DBIx::Recordset::LastErrstr ;
*LastError = \&DBIx::Recordset::LastError ;
*PreserveCase = \$DBIx::Recordset::PreserveCase;
use Carp qw(confess);
use File::Spec ;
use DBIx::Recordset ;
use Text::ParseWords ;
## ----------------------------------------------------------------------------
##
## savecroak
##
## croaks and save error
##
sub savecroak
{
my ($self, $msg, $code) = @_ ;
$LastErr = $self->{'*LastErr'} = $code || $dbi::err || -1 ;
$LastErrstr = $self->{'*LastErrstr'} = $msg || $DBI::errstr || ("croak from " . caller) ;
#$Carp::extra = 1 ;
#Carp::croak $msg ;
confess($msg);
}
## ----------------------------------------------------------------------------
##
## DoOnConnect
##
## in $cmd sql cmds
##
sub DoOnConnect
{
my ($self, $cmd) = @_ ;
if ($cmd)
{
if (ref ($cmd) eq 'ARRAY')
{
foreach (@$cmd)
{
$self -> do ($_) ;
}
}
elsif (ref ($cmd) eq 'HASH')
{
$self -> DoOnConnect ($cmd -> {'*'}) ;
$self -> DoOnConnect ($cmd -> {$self -> {'*Driver'}}) ;
}
else
{
$self -> do ($cmd) ;
}
}
}
## ----------------------------------------------------------------------------
##
## DBHdl
##
## return DBI database handle
##
sub DBHdl ($)
{
return $_[0] -> {'*DBHdl'} ;
}
## ----------------------------------------------------------------------------
##
## do an non select statement
##
## $statement = statement to do
## \%attr = attribs (optional)
## @bind_valus= values to bind (optional)
## or
## \@bind_valus= values to bind (optional)
## \@bind_types = data types of bind_values
##
sub do($$;$$$)
{
my($self, $statement, $attribs, @params) = @_;
$self -> {'*LastSQLStatement'} = $statement ;
my $ret ;
my $bval ;
my $btype ;
my $dbh ;
my $sth ;
if (@params > 1 && ref ($bval = $params[0]) eq 'ARRAY' && ref ($btype = $params[1]) eq 'ARRAY')
{
if ($self->{'*Debug'} > 1) { local $^W = 0 ; print DBIx::Recordset::LOG "DB: do '$statement' bind_values=<@$bval> bind_types=<@$btype>\n" } ;
$dbh = $self->{'*DBHdl'} ;
$sth = $dbh -> prepare ($statement, $attribs) ;
my $Numeric = $self->{'*NumericTypes'} || {} ;
local $^W = 0 ; # avoid warnings
if (defined ($sth))
{
for (my $i = 0 ; $i < @$bval; $i++)
{
$bval -> [$i] += 0 if (defined ($bval -> [$i]) && defined ($btype -> [$i]) && $Numeric -> {$btype -> [$i]}) ;
#$sth -> bind_param ($i+1, $bval -> [$i], $btype -> [$i]) ;
#$sth -> bind_param ($i+1, $bval -> [$i], $btype -> [$i] == DBI::SQL_CHAR()?DBI::SQL_CHAR():undef ) ;
my $bt = $btype -> [$i] ;
$sth -> bind_param ($i+1, $bval -> [$i], (defined ($bt) && $bt <= DBI::SQL_CHAR())?{TYPE=>$bt}:undef ) ;
}
$ret = $sth -> execute ;
}
}
else
{
print DBIx::Recordset::LOG "DB: do $statement <@params>\n" if ($self->{'*Debug'} > 1) ;
$ret = $self->{'*DBHdl'} -> do ($statement, $attribs, @params) ;
}
print DBIx::Recordset::LOG "DB: do returned " . (defined ($ret)?$ret:'') . "\n" if ($self->{'*Debug'} > 2) ;
print DBIx::Recordset::LOG "DB: ERROR $DBI::errstr\n" if (!$ret && $self->{'*Debug'}) ;
print DBIx::Recordset::LOG "DB: in do $statement <@params>\n" if (!$ret && $self->{'*Debug'} == 1) ;
$LastErr = $self->{'*LastErr'} = $DBI::err ;
$LastErrstr = $self->{'*LastErrstr'} = $DBI::errstr ;
return $ret ;
}
## ----------------------------------------------------------------------------
##
## QueryMetaData
##
## $table = table (multiple tables must be comma separated)
##
sub QueryMetaData($$)
{
my ($self, $table) = @_ ;
$table = lc($table) if (!$PreserveCase) ;
my $meta ;
my $metakey = "$self->{'*DataSource'}//" . $table ;
if (defined ($meta = $DBIx::Recordset::Metadata{$metakey}))
{
print DBIx::Recordset::LOG "DB: use cached meta data for $table\n" if ($self->{'*Debug'} > 2) ;
return $meta
}
my $hdl = $self->{'*DBHdl'} ;
my $drv = $self->{'*Driver'} ;
my $sth ;
my $ListFields = DBIx::Compat::GetItem ($drv, 'ListFields') ;
my $QuoteTypes = DBIx::Compat::GetItem ($drv, 'QuoteTypes') ;
my $NumericTypes = DBIx::Compat::GetItem ($drv, 'NumericTypes') ;
my $HaveTypes = DBIx::Compat::GetItem ($drv, 'HaveTypes') ;
#my @tabs = split (/\s*\,\s*/, $table) ;
my @tabs = quotewords ('\s*,\s*', 1, $table) ;
my $tab ;
my $ltab ;
my %Quote ;
my %Numeric ;
my @Names ;
my @Types ;
my @FullNames ;
my %Table4Field ;
my %Type4Field ;
my $i ;
foreach $tab (@tabs)
{
next if ($tab =~ /^\s*$/) ;
eval {
$sth = &{$ListFields}($hdl, $tab) or carp ("Cannot list fields for $tab ($DBI::errstr)") ;
} ;
next if ($@) ; # ignore any table for which we can't get fields
if ($tab =~ /^"(.*?)"$/)
{ $ltab = $1 ; }
else
{ $ltab = $tab ; }
my $types ;
my $fields = $sth?$sth -> FETCH ($PreserveCase?'NAME':'NAME_lc'):[] ;
my $num = $#{$fields} + 1 ;
if ($HaveTypes && $sth)
{
#print DBIx::Recordset::LOG "DB: Have Types for driver\n" ;
$types = $sth -> FETCH ('TYPE') ;
}
else
{
#print DBIx::Recordset::LOG "DB: No Types for driver\n" ;
# Drivers does not have fields types -> give him SQL_VARCHAR
$types = [] ;
for ($i = 0; $i < $num; $i++)
{ push @$types, DBI::SQL_VARCHAR (); }
# Setup quoting for SQL_VARCHAR
$QuoteTypes = { DBI::SQL_VARCHAR() => 1 } ;
$NumericTypes = { } ;
}
push @Names, @$fields ;
push @Types, @$types ;
$i = 0 ;
foreach (@$fields)
{
$Table4Field{$_} = $ltab ;
$Table4Field{"$ltab.$_"} = $ltab ;
$Type4Field{"$_"} = $types -> [$i] ;
$Type4Field{"$ltab.$_"} = $types -> [$i++] ;
push @FullNames, "$ltab.$_" ;
}
$sth -> finish if ($sth) ;
# Set up a hash which tells us which fields to quote and which not
# We setup two versions, one with tablename and one without
my $col ;
my $fieldname ;
for ($col = 0; $col < $num; $col++ )
{
if ($self->{'*Debug'} > 2)
{
my $n = $$fields[$col] ;
my $t = $$types[$col] ;
print DBIx::Recordset::LOG "DB: TAB = $tab, COL = $col, NAME = $n, TYPE = $t" ;
}
$fieldname = $$fields[$col] ;
if ($$QuoteTypes{$$types[$col]})
{
#print DBIx::Recordset::LOG " -> quote\n" if ($self->{'*Debug'} > 2) ;
$Quote {"$tab.$fieldname"} = 1 ;
$Quote {"$fieldname"} = 1 ;
}
else
{
#print DBIx::Recordset::LOG "\n" if ($self->{'*Debug'} > 2) ;
$Quote {"$tab.$fieldname"} = 0 ;
$Quote {"$fieldname"} = 0 ;
}
if ($$NumericTypes{$$types[$col]})
{
print DBIx::Recordset::LOG " -> numeric\n" if ($self->{'*Debug'} > 2) ;
$Numeric {"$tab.$fieldname"} = 1 ;
$Numeric {"$fieldname"} = 1 ;
}
else
{
print DBIx::Recordset::LOG "\n" if ($self->{'*Debug'} > 2) ;
$Numeric {"$tab.$fieldname"} = 0 ;
$Numeric {"$fieldname"} = 0 ;
}
}
print DBIx::Recordset::LOG "No Fields found for $tab\n" if ($num == 0 && $self->{'*Debug'} > 1) ;
}
print DBIx::Recordset::LOG "No Tables specified\n" if ($#tabs < 0 && $self->{'*Debug'} > 1) ;
$meta = {} ;
$meta->{'*Table4Field'} = \%Table4Field ;
$meta->{'*Type4Field'} = \%Type4Field ;
$meta->{'*FullNames'} = \@FullNames ;
$meta->{'*Names'} = \@Names ;
$meta->{'*Types'} = \@Types ;
$meta->{'*Quote'} = \%Quote ;
$meta->{'*Numeric'} = \%Numeric ;
$meta->{'*NumericTypes'} = $NumericTypes ;
$DBIx::Recordset::Metadata{$metakey} = $meta ;
if (!exists ($meta -> {'*Links'}))
{
my $ltab ;
my $lfield ;
my $metakey ;
my $subnames ;
my $n ;
$meta -> {'*Links'} = {} ;
my $metakeydsn = "$self->{'*DataSource'}//-" ;
my $metakeydsntf = "$self->{'*DataSource'}//-" . ($self->{'*TableFilter'}||'');
my $metadsn = $DBIx::Recordset::Metadata{$metakeydsn} || {} ;
my $tabmetadsn = $DBIx::Recordset::Metadata{$metakeydsntf} || {} ;
my $tables = $tabmetadsn -> {'*Tables'} ;
if (!$tables)
{ # Query the driver, which tables are available
my $ListTables = DBIx::Compat::GetItem ($drv, 'ListTables') ;
if ($ListTables)
{
my @tabs = &{$ListTables}($hdl) or $self -> savecroak ("Cannot list tables for $self->{'*DataSource'} ($DBI::errstr)") ;
my @stab ;
my $stab ;
my $tabfilter = $self -> {'*TableFilter'} || '.' ;
foreach (@tabs)
{
s/^[^a-zA-Z0-9_.]// ;
s/[^a-zA-Z0-9_.]$// ;
if ($_ =~ /(^|\.)$tabfilter/i)
{
@stab = split (/\./);
$stab = $PreserveCase?(pop @stab):lc (pop @stab) ;
$tables -> {$stab} = $_ ;
}
}
$tabmetadsn -> {'*Tables'} = $tables ;
if ($self->{'*Debug'} > 3)
{
my $t ;
foreach $t (keys %$tables)
{ print DBIx::Recordset::LOG "DB: Found table $t => $tables->{$t}\n" ; }
}
}
else
{
$tabmetadsn -> {'*Tables'} = {} ;
}
$DBIx::Recordset::Metadata{$metakeydsn} = $metadsn ;
$DBIx::Recordset::Metadata{"$metakeydsn$self->{'*TableFilter'}"} = $tabmetadsn if ($self->{'*TableFilter'}) ;
}
if ($#tabs <= 0)
{
my $fullname ;
my $tabfilter = $self -> {'*TableFilter'} ;
my $fullltab ;
my $tableshort = $table ;
if ($tabfilter && ($table =~ /^$tabfilter(.*?)$/))
{
$tableshort = $1 ;
}
foreach $fullname (@FullNames)
{
my ($ntab, $n) = split (/\./, $fullname) ;
my $prefix = '' ;
my $fullntab = $ntab ;
if ($tabfilter && ($ntab =~ /^$tabfilter(.*?)$/))
{
$ntab = $1 ;
}
if ($n =~ /^(.*?)__(.*?)$/)
{
$prefix = "$1__" ;
$n = $2 ;
}
my @part = split (/_/, $n) ;
my $tf = $tabfilter || '' ;
for (my $i = 0; $i < $#part; $i++)
{
$ltab = join ('_', @part[0..$i]) ;
$lfield = join ('_', @part[$i + 1..$#part]) ;
next if (!$ltab) ;
if (!$tables -> {$ltab} && $tables -> {"$tf$ltab"})
{ $fullltab = "$tabfilter$ltab" }
else
{ $fullltab = $ltab }
if ($tables -> {$fullltab})
{
$metakey = $self -> QueryMetaData ($fullltab) ;
$subnames = $metakey -> {'*Names'} ;
if (grep (/^$lfield$/i, @$subnames))
{ # setup link
$meta -> {'*Links'}{"-$prefix$ltab"} = {'!Table' => $fullltab, '!LinkedField' => $lfield, '!MainField' => "$prefix$n", '!MainTable' => $fullntab} ;
print DBIx::Recordset::LOG "Link found for $ntab.$prefix$n to $ltab.$lfield\n" if ($self->{'*Debug'} > 2) ;
#my $metakeyby = "$self->{'*DataSource'}//$ltab" ;
#my $linkedby = $DBIx::Recordset::Metadata{$metakeyby} -> {'*Links'} ;
my $linkedby = $metakey -> {'*Links'} ;
my $linkedbyname = "\*$prefix$tableshort" ;
$linkedby -> {$linkedbyname} = {'!Table' => $fullntab, '!MainField' => $lfield, '!LinkedField' => "$prefix$n", '!LinkedBy' => $fullltab, '!MainTable' => $fullltab} ;
#$linkedby -> {"-$tableshort"} = $linkedby -> {$linkedbyname} if (!exists ($linkedby -> {"-$tableshort"})) ;
}
last ;
}
}
}
}
else
{
foreach $ltab (@tabs)
{
next if (!$ltab) ;
$metakey = $self -> QueryMetaData ($ltab) ;
my $k ;
my $v ;
my $lbtab ;
my $links = $metakey -> {'*Links'} ;
while (($k, $v) = each (%$links))
{
if (!$meta -> {'*Links'}{$k})
{
$meta -> {'*Links'}{$k} = { %$v } ;
print DBIx::Recordset::LOG "Link copied: $k\n" if ($self->{'*Debug'} > 2) ;
}
}
}
}
}
return $meta ;
}
###################################################################################
package DBIx::Database ;
use strict 'vars' ;
use vars (
'%DBDefault', # DB Shema default für alle Tabellen
'@DBSchema', # DB Shema definition
'$LastErr',
'$LastErrstr',
'*LastErr',
'*LastErrstr',
'*LastError',
'$PreserveCase',
'@ISA') ;
@ISA = ('DBIx::Database::Base') ;
*LastErr = \$DBIx::Recordset::LastErr ;
*LastErrstr = \$DBIx::Recordset::LastErrstr ;
*LastError = \&DBIx::Recordset::LastError ;
*PreserveCase = \$DBIx::Recordset::PreserveCase;
use Carp ;
## ----------------------------------------------------------------------------
##
## connect
##
sub connect
{
my ($self, $password) = @_ ;
my $hdl = $self->{'*DBHdl'} = DBI->connect($self->{'*DataSource'}, $self->{'*Username'}, $password, $self->{'*DBIAttr'}) or $self -> savecroak ("Cannot connect to $self->{'*DataSource'} ($DBI::errstr)") ;
$LastErr = $self->{'*LastErr'} = $DBI::err ;
$LastErrstr = $self->{'*LastErrstr'} = $DBI::errstr ;
$self->{'*MainHdl'} = 1 ;
$self->{'*Driver'} = $hdl->{Driver}->{Name} ;
if ($self->{'*Driver'} eq 'Proxy')
{
$self->{'*DataSource'} =~ /dsn\s*=\s*dbi:(.*?):/i ;
$self->{'*Driver'} = $1 ;
print DBIx::Recordset::LOG "DB: Found DBD::Proxy, take compability entrys for driver $self->{'*Driver'}\n" if ($self->{'*Debug'} > 1) ;
}
print DBIx::Recordset::LOG "DB: Successfull connect to $self->{'*DataSource'} \n" if ($self->{'*Debug'} > 1) ;
my $cmd ;
if ($hdl && ($cmd = $self -> {'*DoOnConnect'}))
{
$self -> DoOnConnect ($cmd) ;
}
return $hdl ;
}
## ----------------------------------------------------------------------------
##
## new
##
## creates a new DBIx::Database object. This object fetches all necessary
## meta information from the database for later use by DBIx::Recordset objects.
## Also it builds a list of links between the tables.
##
##
## $data_source = Driver/DB/Host
## $username = Username (optional)
## $password = Password (optional)
## \%attr = Attributes (optional)
## $saveas = Name for this DBIx::Database object to save
## The name can be used in Get, or as !DataSource for DBIx::Recordset
## $keepopen = keep connection open to use in further DBIx::Recordset setups
## $tabfilter = regex which tables should be used
##
sub new
{
my ($class, $data_source, $username, $password, $attr, $saveas, $keepopen, $tabfilter, $doonconnect, $reconnect) = @_ ;
if (ref ($data_source) eq 'HASH')
{
my $p = $data_source ;
($data_source, $username, $password, $attr, $saveas, $keepopen, $tabfilter, $doonconnect, $reconnect) =
@$p{('!DataSource', '!Username', '!Password', '!DBIAttr', '!SaveAs', '!KeepOpen', '!TableFilter', '!DoOnConnect', '!Reconnect')} ;
}
$LastErr = undef ;
$LastErrstr = undef ;
my $metakey ;
my $self ;
if (!($data_source =~ /^dbi:/i))
{
$metakey = "-DATABASE//$1" ;
$self = $DBIx::Recordset::Metadata{$metakey} ;
$self->{'*DBHdl'} = undef if ($reconnect) ;
$self -> connect ($password) if ($keepopen && !defined ($self->{'*DBHdl'})) ;
return $self ;
}
if ($saveas)
{
$metakey = "-DATABASE//$saveas" ;
if (defined ($self = $DBIx::Recordset::Metadata{$metakey}))
{
$self->{'*DBHdl'} = undef if ($reconnect) ;
$self -> connect ($password) if ($keepopen && !defined ($self->{'*DBHdl'})) ;
return $self ;
}
}
$self = {
'*Debug' => $DBIx::Recordset::Debug,
'*DataSource' => $data_source,
'*DBIAttr' => $attr,
'*Username' => $username,
'*TableFilter' => $tabfilter,
'*DoOnConnect' => $doonconnect,
} ;
bless ($self, $class) ;
my $hdl ;
$self->{'*DBHdl'} = undef if ($reconnect) ;
if (ref ($data_source) and eval { $data_source->isa('DBI::db') } )
{
$self->{'*DBHdl'} = $data_source;
}
else
{
}
if (!defined ($self->{'*DBHdl'}))
{
$hdl = $self->connect ($password) ;
}
else
{
$LastErr = $self->{'*LastErr'} = undef ;
$LastErrstr = $self->{'*LastErrstr'} = undef ;
$hdl = $self->{'*DBHdl'} ;
print DBIx::Recordset::LOG "DB: Use already open dbh for $self->{'*DataSource'}\n" if ($self->{'*Debug'} > 1) ;
}
$DBIx::Recordset::Metadata{"$self->{'*DataSource'}//*"} ||= {} ; # make sure default table is defined
my $drv = $self->{'*Driver'} ;
my $metakeydsn = "$self->{'*DataSource'}//-" ;
my $metakeydsntf = "$self->{'*DataSource'}//-" . ($self->{'*TableFilter'}||'');
my $metadsn = $DBIx::Recordset::Metadata{$metakeydsn} || {} ;
my $tabmetadsn = $DBIx::Recordset::Metadata{$metakeydsntf} || {} ;
my $tables = $tabmetadsn -> {'*Tables'} ;
if (!$tables)
{ # Query the driver, which tables are available
my $ListTables = DBIx::Compat::GetItem ($drv, 'ListTables') ;
if ($ListTables)
{
my @tabs = &{$ListTables}($hdl) ; # or $self -> savecroak ("Cannot list tables for $self->{'*DataSource'} ($DBI::errstr)") ;
my @stab ;
my $stab ;
$tabfilter ||= '.' ;
foreach (@tabs)
{
s/^[^a-zA-Z0-9_.]// ;
s/[^a-zA-Z0-9_.]$// ;
if ($_ =~ /(^|\.)$tabfilter/i)
{
@stab = split (/\./);
$stab = $PreserveCase?(pop @stab):lc (pop @stab) ;
$tables -> {$stab} = $_ ;
}
}
$tabmetadsn -> {'*Tables'} = $tables ;
if ($self->{'*Debug'} > 2)
{
my $t ;
foreach $t (keys %$tables)
{ print DBIx::Recordset::LOG "DB: Found table $t => $tables->{$t}\n" ; }
}
}
else
{
$tabmetadsn -> {'*Tables'} = {} ;
}
$DBIx::Recordset::Metadata{$metakeydsn} = $metadsn ;
$DBIx::Recordset::Metadata{$metakeydsntf} = $tabmetadsn ;
}
my $tab ;
my $x ;
while (($tab, $x) = each (%{$tables}))
{
$self -> QueryMetaData ($tab) ;
}
$DBIx::Recordset::Metadata{$metakey} = $self if ($metakey) ;
# disconnect in case we are running in a Apache/mod_perl startup file
if (defined ($self->{'*DBHdl'}) && !$keepopen)
{
$self->{'*DBHdl'} -> disconnect () ;
undef $self->{'*DBHdl'} ;
print DBIx::Recordset::LOG "DB: Disconnect from $self->{'*DataSource'} \n" if ($self->{'*Debug'} > 1) ;
}
return $self ;
}
## ----------------------------------------------------------------------------
##
## Get
##
## $name = Name of DBIx::Database obecjt you what to get
##
sub Get
{
my ($class, $saveas) = @_ ;
my $metakey ;
$metakey = "-DATABASE//$saveas" ;
return $DBIx::Recordset::Metadata{$metakey} ;
}
## ----------------------------------------------------------------------------
##
## TableAttr
##
## get and/or set and attribute for an specfic table
##
## $table = Name of table(s)
## $key = key
## $value = value
##
sub TableAttr
{
my ($self, $table, $key, $value) = @_ ;
$table = lc($table) if (!$PreserveCase) ;
my $meta ;
my $metakey = "$self->{'*DataSource'}//$table" ;
if (!defined ($meta = $DBIx::Recordset::Metadata{$metakey}))
{
$self -> savecroak ("Unknown table $table in $self->{'*DataSource'}") ;
}
# set new value if wanted
return $meta -> {$key} = $value if (defined ($value)) ;
# only return value
return $meta -> {$key} if (exists ($meta -> {$key})) ;
# check if there is a default value
$metakey = "$self->{'*DataSource'}//*" ;
return undef if (!defined ($meta = $DBIx::Recordset::Metadata{$metakey})) ;
return $meta -> {$key} ;
}
## ----------------------------------------------------------------------------
##
## TableLink
##
## get and/or set an link description for an table
##
## $table = Name of table(s)
## $key = linkname
## $value = ref to hash with link description
##
sub TableLink
{
my ($self, $table, $key, $value) = @_ ;
$table = lc($table) if (!$PreserveCase) ;
my $meta ;
my $metakey = "$self->{'*DataSource'}//$table" ;
if (!defined ($meta = $DBIx::Recordset::Metadata{$metakey}))
{
$self -> savecroak ("Unknown table $table in $self->{'*DataSource'}") ;
}
return $meta -> {'*Links'} if (!defined ($key)) ;
return $meta -> {'*Links'} -> {$key} = $value if (defined ($value)) ;
return $meta -> {'*Links'} -> {$key} ;
}
## ----------------------------------------------------------------------------
##
## MetaData
##
## get/set metadata for a given table
##
## $table = Name of table
## $metadata = meta data to set
##
sub MetaData
{
my ($self, $table, $metadata, $clear) = @_ ;
$table = lc($table) if (!$PreserveCase) ;
my $meta ;
my $metakey = "$self->{'*DataSource'}//$table" ;
if (!defined ($meta = $DBIx::Recordset::Metadata{$metakey}))
{
$self -> savecroak ("Unknown table $table in $self->{'*DataSource'}") ;
}
return $meta if (!defined ($metadata) && !$clear) ;
return $DBIx::Recordset::Metadata{$metakey} = $metadata ;
}
## ----------------------------------------------------------------------------
##
## AllTables
##
## return reference to hash which keys contains all tables of that datasource
##
sub AllTables
{
my $self = shift ;
my $metakeydsn = "$self->{'*DataSource'}//-" . ($self->{'*TableFilter'} || '') ;
my $metadsn = $DBIx::Recordset::Metadata{$metakeydsn} || {} ;
return $metadsn -> {'*Tables'} ;
}
## ----------------------------------------------------------------------------
##
## AllNames
##
## return reference to array of all names in all tables
##
## $table = Name of table
##
sub AllNames
{
my ($self, $table) = @_ ;
$table = lc($table) if (!$PreserveCase) ;
my $meta ;
my $metakey = "$self->{'*DataSource'}//$table" ;
if (!defined ($meta = $DBIx::Recordset::Metadata{$metakey}))
{
$self -> savecroak ("Unknown table $table in $self->{'*DataSource'}") ;
}
return $meta -> {'*Names'} ;
}
## ----------------------------------------------------------------------------
##
## AllTypes
##
## return reference to array of all types in all tables
##
## $table = Name of table
##
sub AllTypes
{
my ($self, $table) = @_ ;
$table = lc($table) if (!$PreserveCase) ;
my $meta ;
my $metakey = "$self->{'*DataSource'}//$table" ;
if (!defined ($meta = $DBIx::Recordset::Metadata{$metakey}))
{
$self -> savecroak ("Unknown table $table in $self->{'*DataSource'}") ;
}
return $meta -> {'*Types'} ;
}
## ----------------------------------------------------------------------------
##
## DESTROY
##
## do cleanup
##
sub DESTROY
{
my $self = shift ;
my $orgerr = $@ ;
local $@ ;
eval
{
if (defined ($self->{'*DBHdl'}))
{
$self->{'*DBHdl'} -> disconnect () ;
undef $self->{'*DBHdl'} ;
}
} ;
$self -> savecroak ($@) if (!$orgerr && $@) ;
warn $@ if ($orgerr && $@) ;
}
## ---------------------------------------------------------------------------------
##
## Datenbank Erzeugen
##
## in $dbschema Schema file or ARRAY ref
## in $shema schema name (Oracle)
## in $user user to grant rights to
## in $setpriv resetup privileges
## in $alterconstraints resetup constraints (-1 to drop containts)
##
sub CreateTables
{
#my $DataSource = shift ;
#my $setupuser = shift ;
#my $setuppass = shift ;
#my $tabprefix = shift ;
my $db = shift ;
my $dbschema = shift ;
my $shema = shift ;
my $user = shift ;
my $setpriv = shift ;
my $alterconstraints = shift ;
my $DBSchemaRef ;
print "\nDatenbanktabellen anlegen/aktualisierien:\n" ;
if (ref ($dbschema) eq 'ARRAY')
{
$DBSchemaRef = $dbschema ;
}
else
{
open FH, $dbschema or die "Schema nicht gefunden ($dbschema) ($!)" ;
{
local $/ = undef ;
my $shema = ;
$shema =~ /^(.*)$/s ; # untaint
$shema = $1 ;
eval $shema ;
die "Fehler in $dbschema: $@" if ($@) ;
}
close FH ;
$DBSchemaRef = \@DBSchema ;
}
#my $db = DBIx::Database -> new ({'!DataSource' => "$DataSource",
# '!Username' => $setupuser,
# '!Password' => $setuppass,
# '!KeepOpen' => 1,
# '!TableFilter' => $tabprefix}) ;
#
#die DBIx::Database->LastError . "; Datenbank muß bereits bestehen" if (DBIx::Database->LastError) ;
#
my $dbh = $db -> DBHdl ;
local $dbh -> {RaiseError} = 0 ;
local $dbh -> {PrintError} = 0 ;
my $tables = $db -> AllTables ;
my $tab ;
my $tabname ;
my $type ;
my $typespec ;
my $size ;
my $public = defined ($user) && $db -> {'*Username'} ne $user ;
my $drv = $db->{'*Driver'} ;
my $tabprefix = $db -> {'*TableFilter'} ;
my $trans = DBIx::Compat::GetItem ($drv, 'CreateTypes') ;
$trans = {} if (!$trans) ;
my $createseq = DBIx::Compat::GetItem ($drv, 'CreateSeq') ;
my $createpublic = $public && DBIx::Compat::GetItem ($drv, 'CreatePublic') ;
my $candropcolumn = DBIx::Compat::GetItem ($drv, 'CanDropColumn') ;
my $i ;
my $field ;
my $cmd ;
foreach $tab (@$DBSchemaRef)
{
my $newtab = 0 ;
my $newseq = 0 ;
my $hasseq = 0 ;
my %tabdef = (%DBDefault, %$tab, %{$tab -> {'!For'} -> {$drv} || {}}) ;
$tabname = "$tabprefix$tabdef{'!Table'}" ;
my $init = $tabdef{'!Init'} ;
my $grant = (defined ($user) && $db -> {'*Username'} ne $user)?$tabdef{'!Grant'}:undef ;
my $constraint ;
my $constraints = $tabdef{'!Constraints'} ;
my $default = $tabdef{'!Default'} ;
my $pk = $tabdef{'!PrimKey'} ;
my $index= $tabdef{'!Index'} ;
my $c ;
my $ccmd ;
my $cname ;
my $cval ;
my $ncnt ;
if ($tables -> {$tabname})
{
printl ("$tabname", LL, "vorhanden\n") ;
my $fields = $tabdef{'!Fields'} ;
my $dbfields = $db -> AllNames ($tabname) ;
my %dbfields = map { $_ => 1 } @$dbfields ;
my $lastfield ;
for ($i = 0; $i <= $#$fields; $i+= 2)
{
$field = lc ($fields -> [$i]) ;
$typespec = $fields -> [$i+1] ;
$hasseq = 1 if ($createseq && $typespec eq 'counter') ;
$ccmd = '' ;
$ncnt = 0 ;
if ($constraints && ($constraint = $constraints -> {$field}))
{
$cname = "${tabname}_$field" ;
for ($c = 0 ; $c < $#$constraint; $c+=2)
{
if ($constraint -> [$c] eq '!Name')
{
$cname = $tabprefix . $constraint -> [$c+1] ;
$ncnt = 0 ;
next ;
}
$ncnt++ ;
$cval = $constraint -> [$c+1] || $constraint -> [$c] ;
$cval =~ s#REFERENCES\s+(.*?)\s*\(#REFERENCES $tabprefix$1 (#i ;
$ccmd .= " CONSTRAINT $cname" . ( $ncnt >1?$ncnt:'') . " $cval" ;
}
}
if (!$dbfields{$field})
{
printl (" Add $field", LL) ;
$newseq = 1 if ($createseq && $typespec eq 'counter') ;
if ($typespec =~ /^(.*?)\s*\((.*?)\)(.*?)$/)
{
$type = $trans->{$1}?$trans->{$1}:$1 . "($2) $3" ;
}
else
{
$type = $typespec ;
$type = $trans -> {$typespec} if ($trans -> {$typespec}) ;
}
$cmd = "ALTER TABLE $tabname ADD $field $type $ccmd" . ($lastfield?" AFTER $lastfield":'') ;
$db -> do ($cmd) ;
die "Fehler beim Erstellen des Feldes $tabname.$field:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
print "ok\n" ;
if ($init || $default)
{
printl (" $field initialisieren", LL) ;
$db -> MetaData ($tabname, undef, 1) ;
my $rs = DBIx::Recordset -> Setup ({'!DataSource' => $db, '!Table' => $tabname, '!PrimKey' => $tabdef{'!PrimKey'}}) ;
die "Fehler beim Setup von Tabelle $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
my $rec ;
if ($default && defined ($default -> {$field}))
{
$$rs -> Update ({$field, $default -> {$field}}, "$field is null") ;
die "Fehler beim Update in Tabelle $tabname:\n" . $$rs -> LastSQLStatement . "\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
}
if ($init)
{
foreach $rec (@$init)
{
$$rs -> Update ({$field, $rec -> {$field}}, {$pk => $rec -> {$pk}}) ;
die "Fehler beim Update in Tabelle $tabname:\n" . $$rs -> LastSQLStatement . "\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
}
}
print "ok\n" ;
}
}
elsif ($alterconstraints && $ccmd)
{
printl (" Alter Constraint $field", LL) ;
$ccmd = '' ;
$ncnt = 0 ;
if ($constraints && ($constraint = $constraints -> {$field}))
{
$cname = "${tabname}_$field" ;
for ($c = 0 ; $c < $#$constraint; $c+=2)
{
if ($constraint -> [$c] eq '!Name')
{
$cname = $tabprefix . $constraint -> [$c+1] ;
$ncnt = 0 ;
next ;
}
$ncnt++ ;
$ccmd = " CONSTRAINT $cname" . ( $ncnt>1?$ncnt:'') ;
$cmd = "ALTER TABLE $tabname DROP $ccmd" ;
$db -> do ($cmd) ;
#die "Fehler beim Erstellen des Feldes $tabname.$field:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
if ($alterconstraints > 0)
{
$cval = $constraint -> [$c] ;
if (lc ($cval) eq 'null' || lc ($cval) eq 'not null')
{
$cmd = "ALTER TABLE $tabname MODIFY $field $ccmd $cval" ;
}
else
{
$cval .= " ($field) " . $constraint -> [$c+1] ;
$cval =~ s#REFERENCES\s+(.*?)\s*\(#REFERENCES $tabprefix$1 (#i ;
$cmd = "ALTER TABLE $tabname ADD $ccmd $cval" ;
}
$db -> do ($cmd) ;
die "Fehler beim Ändern des Constraints des Feldes $tabname.$field:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
}
}
}
print "ok\n" ;
}
$dbfields{$field} = 2 ;
}
if ($candropcolumn)
{
while (($field, $i) = each (%dbfields))
{
if ($i == 1)
{
printl (" Drop $field", LL) ;
$cmd = "ALTER TABLE $tabname DROP $field" ;
$db -> do ($cmd) ;
die "Fehler beim Entfernen des Feldes $tabname.$field:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
print "ok\n" ;
}
}
}
}
else
{
printl ("$tabname erstellen", LL) ;
my $cmd = "CREATE TABLE $tabname (" ;
$newtab = 1 ;
my $fields = $tabdef{'!Fields'} ;
for ($i = 0; $i <= $#$fields; $i+= 2)
{
$field = lc($fields -> [$i]) ;
$typespec = $fields -> [$i+1] ;
$hasseq = $newseq = 1 if ($createseq && $typespec eq 'counter') ;
if ($typespec =~ /^(.*?)\s*\((.*?)\)(.*?)$/)
{
$type = $trans -> {$1}?$trans -> {$1}:$1 . "($2) $3" ;
}
else
{
$type = $typespec ;
$type = $trans -> {$typespec} if ($trans -> {$typespec}) ;
}
$ccmd = '' ;
$ncnt = 0 ;
if ($constraints && ($constraint = $constraints -> {$field}))
{
$cname = "${tabname}_$field" ;
for ($c = 0 ; $c < $#$constraint; $c+=2)
{
if ($constraint -> [$c] eq '!Name')
{
$cname = $tabprefix . $constraint -> [$c+1] ;
$ncnt = 0 ;
next ;
}
$ncnt++ ;
$cval = $constraint -> [$c+1] || $constraint -> [$c] ;
$cval =~ s#REFERENCES\s+(.*?)\s*\(#REFERENCES $tabprefix$1 (#i ;
$ccmd .= " CONSTRAINT $cname" . ( $ncnt >1?$ncnt:'') . " $cval" ;
}
}
$cmd .= "$field $type $ccmd" ;
$cmd .= ($i == $#$fields - 1?' ':', ') ;
}
$cmd .= ", PRIMARY KEY ($tabdef{'!PrimKey'})" if ($tabdef{'!PrimKey'}) ;
$cmd .= ')' ;
$db -> do ($cmd) ;
die "Fehler beim Erstellen der Tabelle $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
print "ok\n" ;
if ($init)
{
printl ("$tabname initialisieren", LL) ;
my $rs = DBIx::Recordset -> Setup ({'!DataSource' => $db, '!Table' => $tabname, '!PrimKey' => $tabdef{'!PrimKey'}}) ;
die "Fehler beim Setup von Tabelle $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
my $rec ;
foreach $rec (@$init)
{
my %dat ;
if ($default)
{
%dat = (%$default, %$rec) ;
}
else
{
%dat = %$rec ;
}
$$rs -> Insert (\%dat) ;
die "Fehler beim Insert in Tabelle $tabname:\n" . $$rs -> LastSQLStatement . "\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
}
print "ok\n" ;
}
}
if ($index)
{
printl ("$tabname index erstellen", LL) ;
my $i ;
for ($i = 0; $i <= $#$index; $i+= 2)
{
my $field = lc($index -> [$i]) ;
my $name = "${tabname}_${field}_ndx" ;
my $attr = $index -> [$i+1] ;
if (ref($attr) eq 'HASH')
{
$name = "$tabprefix$attr->{Name}" ;
$attr = $attr -> {Attr} ;
}
my $cmd = "CREATE $attr INDEX $name ON $tabname ($field)" ;
$db -> do ($cmd) ;
die "Fehler beim Erstellen des Indexes für $field:\n$cmd\n" . DBIx::Database->LastError if ($newtab && DBIx::Database->LastError) ;
}
print "ok\n" ;
}
if ($grant && ($newtab || $setpriv))
{
if ($createpublic)
{
printl ("public synonym für $tabname erstellen", LL) ;
if ($setpriv && !$newtab)
{
my $cmd = "DROP PUBLIC SYNONYM $tabname " ;
$db -> do ($cmd) ;
}
my $cmd = "CREATE PUBLIC SYNONYM $tabname FOR $shema.$tabname" ;
$db -> do ($cmd) ;
die "Fehler beim Erstellen von public Synonym $tabname:\n$cmd\n" . DBIx::Database->LastError if ($newtab && DBIx::Database->LastError) ;
print "ok\n" ;
}
printl ("$tabname Berechtigungen setzen", LL) ;
if ($setpriv && !$newtab)
{
my $cmd = "REVOKE all ON $tabname FROM $user" ;
$db -> do ($cmd) ;
warn "Fehler beim Entziehen der Berechtigungen für Tabelle $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
}
$cmd = 'GRANT ' . join (',', @$grant) . " ON $tabname TO $user" ;
$db -> do ($cmd) ;
die "Fehler beim Setzen der Berechtigungen für Tabelle $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
print "ok\n" ;
}
if ($hasseq)
{
$tabname = "${tabname}_seq" ;
if ($newseq)
{
printl ("$tabname erstellen", LL) ;
my $cmd = "CREATE SEQUENCE $tabname " ;
$db -> do ($cmd) ;
die "Fehler beim Erstellen von Sequenz $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
print "ok\n" ;
}
if ($grant && ($newseq || $setpriv))
{
if ($createpublic)
{
printl ("public synonym für $tabname erstellen", LL) ;
if ($setpriv && !$newseq)
{
my $cmd = "DROP PUBLIC SYNONYM $tabname " ;
$db -> do ($cmd) ;
}
my $cmd = "CREATE PUBLIC SYNONYM $tabname FOR $shema.$tabname" ;
$db -> do ($cmd) ;
die "Fehler beim Erstellen von public Synonym $tabname:\n$cmd\n" . DBIx::Database->LastError if ($newseq && DBIx::Database->LastError) ;
print "ok\n" ;
}
printl ("$tabname Berechtigungen setzen", LL) ;
if ($setpriv && !$newseq)
{
my $cmd = "REVOKE all ON $tabname FROM $user" ;
$db -> do ($cmd) ;
warn "Fehler beim Entziehen der Berechtigungen für Tabelle $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
}
$cmd = "GRANT select ON $tabname TO $user" ;
$db -> do ($cmd) ;
die "Fehler beim Setzen der Berechtigungen für Tabelle $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
print "ok\n" ;
}
}
}
}
## ---------------------------------------------------------------------------------
##
## Datenbank Tabellen entfernen
##
## in $shema schema name (Oracle)
## in $user user to revoke rights from
##
sub DropTables
{
#my $DataSource = shift ;
#my $setupuser = shift ;
#my $setuppass = shift ;
#my $tabprefix = shift ;
my $db = shift ;
my $shema = shift ;
my $user = shift ;
print "\nDatenbank Tabellen entfernen:\n" ;
#my $db = DBIx::Database -> new ({'!DataSource' => "$DataSource",
# '!Username' => $setupuser,
# '!Password' => $setuppass,
# '!KeepOpen' => 1,
# '!TableFilter' => $tabprefix}) ;
#
#die DBIx::Database->LastError . "; Datenbank muß bereits bestehen" if (DBIx::Database->LastError) ;
my $tables = $db -> AllTables ;
my $tab ;
my $tabname ;
my @seq ;
my $cmd ;
my $public = defined ($user) && $db -> {'*Username'} ne $user ;
my $drv = $db->{'*Driver'} ;
my $tabprefix = $db -> {'*TableFilter'} ;
my $createseq = DBIx::Compat::GetItem ($drv, 'CreateSeq') ;
my $createpublic = $public && DBIx::Compat::GetItem ($drv, 'CreatePublic') ;
foreach $tabname (keys %$tables)
{
printl ("$tabname entfernen", LL) ;
if ($createpublic)
{
my $cmd = "DROP PUBLIC SYNONYM $tabname " ;
$db -> do ($cmd) ;
}
#push @seq, $tabname if ($createseq && $typespec eq 'counter') ;
$cmd = "DROP TABLE $tabname" ;
$db -> do ($cmd) ;
$db -> MetaData ($tabname, undef, 1) ;
$tables -> {$tabname} = 0 ;
die "Fehler beim Entfernen der Tabelle $tabname:\n$cmd\n" . DBIx::Database->LastError if (DBIx::Database->LastError) ;
print "ok\n" ;
if ($createseq)
{
$tabname = "${tabname}_seq" ;
#printl ("$tabname erstellen", LL) ;
my $cmd = "DROP SEQUENCE $tabname " ;
$db -> do ($cmd) ;
if ($createpublic)
{
my $cmd = "DROP PUBLIC SYNONYM $tabname " ;
$db -> do ($cmd) ;
}
}
}
}
## ---------------------------------------------------------------------------------
##
## Output with fixed length
##
## in $txt Text
## in $length Length
## in $txt2 Weiterer Text
##
sub printl
{
my ($txt, $length, $txt2) = @_ ;
print $txt, ' ' x ($length - length($txt)), ' ', $txt2 ;
} ;
###################################################################################
1;
__END__
=pod
=head1 NAME
DBIx::Database / DBIx::Recordset - Perl extension for DBI recordsets
=head1 SYNOPSIS
use DBIx::Database;
=head1 DESCRIPTION
See perldoc DBIx::Recordset for an description.
=head1 AUTHOR
G.Richter (richter@dev.ecos.de)
DBIx-Recordset-0.26/Recordset/ 0040755 0000000 0000000 00000000000 10126462365 014620 5 ustar root root DBIx-Recordset-0.26/Recordset/FileSeq.pm 0100644 0000000 0000000 00000010532 07757664231 016520 0 ustar root root
###################################################################################
#
# DBIx::Recordset - Copyright (c) 1997-2000 Gerald Richter / ECOS
#
# You may distribute under the terms of either the GNU General Public
# License or the Artistic License, as specified in the Perl README file.
#
# THIS IS BETA SOFTWARE!
#
# THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# $Id: FileSeq.pm,v 1.4 2000/06/26 05:16:18 richter Exp $
#
###################################################################################
package DBIx::Recordset::FileSeq ;
use strict 'vars' ;
use Cwd ;
## ----------------------------------------------------------------------------
##
## new
##
## creates a new DBIx::Recordset::FileSeq object.
##
## $dir = Directory which holds the sequences
##
sub new
{
my ($class, $dummy, $dir, $min, $max) = @_ ;
mkdir $dir, 0755 or die "Cannot create $dir ($!)" if (!-e $dir) ;
die "$dir is not a directory" if (!-d $dir) ;
my $self = {
'*Debug' => $DBIx::Recordset::Debug,
'*Dir' => Cwd::abs_path ($dir),
'*DefaultMin' => $min || 1,
'*DefaultMax' => $max || '',
} ;
bless ($self, $class) ;
$self -> ReadCounter ;
return $self ;
}
## ----------------------------------------------------------------------------
##
## ReadCounter
##
## read current counters form filesystem
##
##
sub ReadCounter
{
my $self = shift ;
my %counter ;
my %max ;
opendir DH, $self -> {'*Dir'} or die "Cannot open directory $self->{'*Dir'} ($!)" ;
while ($_ = readdir DH)
{
if (/seq\.(.*?)\.(\d*?)\.(\d+)$/)
{
$counter{$1}=$3 ;
$max{$1}=$2 ;
}
}
$self -> {'*Counter'} = \%counter ;
$self -> {'*Max'} = \%max ;
}
## ----------------------------------------------------------------------------
##
## NextVal
##
## get next value from counter
##
## in $name = counter name
##
sub NextVal
{
my ($self, $name) = @_ ;
my $dir = $self -> {'*Dir'} ;
my $lastcnt ;
local $^W = 0 ;
while (1)
{
my $cnt = $self -> {'*Counter'}{$name} ;
my $max = $self -> {'*Max'}{$name} ;
if (!defined ($cnt))
{
$cnt = $self->{'*DefaultMin'} ;
$max = $self->{'*DefaultMax'} ;
open FH, ">$dir/seq.$name.$max.$cnt" or die "Cannot create seq.$name..1 ($!)" ;
close FH ;
}
my $cnt1 = $cnt + 1 ;
die "Max count reached for Sequence $name" if ($max ne '' && $cnt1 > $max) ;
if (rename ("$dir/seq.$name.$max.$cnt", "$dir/seq.$name.$max.$cnt1"))
{
$self -> {'*Counter'}{$name} = $cnt1 ;
return $cnt ;
}
my $lastcnt = $cnt ;
$self -> ReadCounter ;
die "Problems updating Sequence $name (File $dir/seq.$name.$max.$cnt)" if ($lastcnt == $self -> {'*Counter'}{$name} ) ;
}
}
1;
__END__
=pod
=head1 NAME
DBIx::Recordset::FileSeq - Sequence generator in Filesystem
=head1 SYNOPSIS
use DBIx::Recordset::FileSeq ;
$self = DBIx::Recordset::FileSeq (undef, '/tmp/seq', $min, $max) ;
$val1 = $self -> NextVal ('foo') ;
$val2 = $self -> NextVal ('foo') ;
$val3 = $self -> NextVal ('bar') ;
=head1 DESCRIPTION
DBIx::Recordset::FileSeq generates unique numbers. State is kept in the
filesystem. With the new constructor you sepcify the directory
where the state is kept. (First parameter is a dummy values, that will
receive the database handle from DBIx::Recordset, but you don't need it
when you use it without DBIx::Recordset). Optionaly you can give a min and
a max values, which will be used for new sequences.
With B you can get the next value
for the sequence of the given name.
The state if kept by haveing a file with the name
seq...
Each time the sequnce value increments the file is renamed. If the
if a numeric value the new value is checked against and NextVal
dies if the sequnce value increment above max.
=head1 AUTHOR
G.Richter (richter@dev.ecos.de)
=head1 SEE ALSO
=item DBIx::Recordset
=cut
DBIx-Recordset-0.26/Recordset/DBSeq.pm 0100644 0000000 0000000 00000007426 07757664231 016136 0 ustar root root
###################################################################################
#
# DBIx::Recordset - Copyright (c) 1997-2000 Gerald Richter / ECOS
#
# You may distribute under the terms of either the GNU General Public
# License or the Artistic License, as specified in the Perl README file.
#
# THIS IS BETA SOFTWARE!
#
# THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#
# $Id: DBSeq.pm,v 1.5 2000/06/26 05:16:18 richter Exp $
#
###################################################################################
package DBIx::Recordset::DBSeq ;
use strict 'vars' ;
## ----------------------------------------------------------------------------
##
## new
##
## creates a new DBIx::Recordset::DBSeq object.
##
## $dbh = Database handle
## $table = table where to keep sequences
##
sub new
{
my ($class, $dbh, $table, $min, $max) = @_ ;
my $self = {
'*Debug' => $DBIx::Recordset::Debug,
'*dbh' => $dbh,
'*table' => $table,
'*DefaultMin' => $min || 1,
'*DefaultMax' => $max || 'NULL',
} ;
bless ($self, $class) ;
return $self ;
}
## ----------------------------------------------------------------------------
##
## NextVal
##
## get next value from counter
##
## in $name = counter name
##
sub NextVal
{
my ($self, $name) = @_ ;
my $dbh = $self -> {'*dbh'} ;
$dbh -> do ("lock table $self->{'*table'} write") or die "Cannot lock $self->{'*table'} ($DBI::errstr)" ;
my $sth = $dbh -> prepare ("select cnt,maxcnt from $self->{'*table'} where name=?") or die "Cannot prepare select for $self->{'*table'} ($DBI::errstr)" ;
$sth -> execute ($name) ;
my $row = $sth -> fetchrow_arrayref ;
my $cnt ;
my $max ;
if (!$row)
{
$cnt = $self->{'*DefaultMin'} ;
$max = $self->{'*DefaultMax'} ;
my $cnt1 = $cnt + 1 ;
$dbh -> do ("insert into $self->{'*table'} (name,cnt,maxcnt) values ('$name',$cnt1,$max)") or die "Cannot insert $self->{'*table'} ($DBI::errstr)" ;
}
else
{
$cnt = $row -> [0] ;
die "Max count reached for sequence $name" if (defined ($row->[1]) && $cnt+1 > $row->[1]) ;
$dbh -> do ("update $self->{'*table'} set cnt=cnt+1 where name='$name'") or die "Cannot update $self->{'*table'} ($DBI::errstr)" ;
}
$dbh -> do ("unlock table") or die "Cannot unlock $self->{'*table'} ($DBI::errstr)" ;
return $cnt ;
}
1;
__END__
=pod
=head1 NAME
DBIx::Recordset::DBSeq - Sequence generator in DBI database
=head1 SYNOPSIS
use DBIx::Recordset::DBSeq ;
$self = DBIx::Recordset::DBSeq ($dbh, 'sequences', $min, $max) ;
$val1 = $self -> NextVal ('foo') ;
$val2 = $self -> NextVal ('foo') ;
$val3 = $self -> NextVal ('bar') ;
=head1 DESCRIPTION
DBIx::Recordset::FileSeq generates unique numbers. State is kept in the
one table of a database accessable via DBI. With the new constructor you
give an open database handle and sepcify the the table where state should be kept.
Optionaly you can give a min and
a max values, which will be used for new sequences.
With B you can get the next value
for the sequence of the given name.
The table must created in the following form:
create table
(
name varchar (32),
cnt integer,
maxcnt integer,
primary key name
) ;
If the sequence value reaches the maxcnt value, NextVal will die with an
error message. If maxcnt contains C there is no limit.
=head1 AUTHOR
G.Richter (richter@dev.ecos.de)
=head1 SEE ALSO
=item DBIx::Recordset
=cut
DBIx-Recordset-0.26/README 0100644 0000000 0000000 00000007742 07757664232 013572 0 ustar root root
DBIx::Recordset - Perl extension for DBI recordsets
Copyright (c) 1997-2001 Gerald Richter / ECOS
You may distribute under the terms of either the GNU General Public
License or the Artistic License, as specified in the Perl README file.
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
OVERVIEW
DBIx::Recordset is a perl module for abstraction and simplification of
database access.
The goal is to make standard database access (select/insert/update/delete)
easier to handle and independend of the underlying DBMS. Special attention is
made on web applications to make it possible to handle the state-less access
and to process the posted data of formfields, but DBIx::Recordset is not
limited to web applications.
The main features of DBIx::Recordset are:
- it has a compact interface, normaly only one function call is necessary
for setup and data retrival/inseration/deletion
- it takes care about type conversion and quoting
- it is able to access/modify tables via arrays and hashs
- it can automaticly create sub-objects for tables which are logical linked
together
- it can automatily create joins based on logical links
- it has input/output filters on a per field/per type basis
- it can create WHERE expression from a hash, which is especially usefull in
a cgi environement, where you can simply pass all paramters posted to your cgi
script to DBIx::Recordset and DBIx::Recordset creates an corresponding SELECT.
- it can create previous/next buttons for html output
- it works together with HTML::Embperl for easily genration of HTML output
- it has an own database abtraction class DBIx::Compat which gives all the
necessary information, so that DBIx::Recordset is able to work with
different database systems
- The new class DBIx::Database is able to retrieve and store meta infomation
of the database in a centralised location, which can be used for later
setup. This is also usefull when running under mod_perl, because you can do
all the setup and configuration work at webserver startup time, speeding up
your scripts when a actual request is processed.
DBIx::Recordset use the DBI API to access the database, so it should work with
every database for which a DBD driver is available (see also DBIx::Compat)
For more information look at perldoc DBIx::Recordset.
An introduction to DBIx::Recordset can be view with perldoc Intrors.pod.
The introduction can also be viewed online at
http://perl.apache.org/embperl/Intrors.pod.cont.html
INSTALLATION
As usual start with
perl Makefile.PL
The makefile tries to detecd your installed DBD drivers and asks you
for a database for performing the tests. The database must exist before
the make test can run!
Now you are ready todo a
make
and
make test
if make test runs ok, do a make install and you are ready. If not you should
check if a entry for your DBD driver exists in the file Compat.pm.
If not create one. For more information how todo this look at
perldoc Compat
Currently there are entries for
- DBD::mSQL
- DBD::mysql
- DBD::Pg
- DBD::Solid
- DBD::ODBC
- DBD::Oracle (requires DBD::Oracle 0.60 or higher)
- DBD::Sybase
- DBD::CSV
- DBD::Informix
- DBD::InterBase
If you create your own entry please send it to me, so I can include it
in the next release.
After changing the Compat.pm rerun make test to make sure it works!
If you don't get make test to work for you, please send me the file
test.log (Normaly it should be enought to send the part for the test,
that fails) along with the versions of DBI and the DBD driver you are using.
EXAMPLES
The perldoc DBIx::Recordset contains a lot of examples for function calls.
The directory eg/ contains examples for HTML::Embperl and CGI.pm
Also you can look at test.pl, which should use most aspects of DBIx::Recordset
SUPPORT
As far as possible for me, support will be available directly from me or via the
DBI Users mailinglist.
DBIx-Recordset-0.26/Changes 0100755 0000000 0000000 00000045034 10126462311 014156 0 ustar root root
0.26 29. Sept 2004
- fix "Can't upgrade that kind of scalar", so it now runs with Perl5.8.x
- remove backticks around table names returned at least from newer versions
of DBI::mysql and DBD::ODBC
0.25 30 Dec. 2003
2003-12-07 U-MOKSHA\metaperl
* Recordset.pm : added support for direct Query input.
2003-12-02 U-MOKSHA\metaperl
* test.pl (DoTest): all tests now pass successfully for Postgres
and SQLite
2003-11-26 U-MOKSHA\metaperl
* Recordset.pm (BuildFields): when building the FROM clause for
LEFT JOINs Recordset placed parentheses around it, like this:
SELECT
dbixrs1.id,dbixrs1.name,dbixrs1.value1,dbixrs1.addon,dbixrs2.value2 FROM (dbixrs1 left join dbixrs2 on dbixrs1.id = dbixrs2.id) WHERE dbixrs1.id IN (2, 5, 10);
but this caused a syntax error in sqlite, so a new leftjoin type
of 4 was introduced to support LEFT JOIN with no parentheses
around the join
* added support to DBIx::Database so that it would accept a DBI
database handle. the entire test suite for DBD::Pg now passes.
* added the '$makesql' flag to Search() so that generated SQL can
be returned instead of executed. It will remain undocumented until
it is supported for Update(), Delete(), and Insert(). It was added
in order that the SQL generated for the test "Order, Group,
Append" would pass on Postgres which finds the generated SQL
invalid.
- Make sure filters given by name override filters given by type.
0.24 10. July 2001
- Added Code to DBIx::Database to Create/Modify/Drop tables
See CreateTables/DropTables
- *fieldname can take an array ref, to specify different
operators for multiple values which should compared to the same
field. This is handy for selecting a range.
- Filters are correctly apply if a arrayref with multiple value
are passed to a SELECT.
- Reset error code and string in DBIx::Database -> new
- If an array of values id passed in for one field and the operator
is '=' now the IN sql operator is used, instead of a set of '='.
- Set Postgres type 1005 to not numeric. Spotted by Michael Maruka.
- Ignore errors in ListFields when retrieving metadata for a table.
- Statement handle is closed as soon as possible, to avoid out of
cursors situations.
- new parameter $expr allow to group multiple sub expressions in
a sql where, therefore allowing more complex conditions.
- Added new parameter !MergeFunc which allow to specify a function
that is called, when multiple records with the same key are found
in a DBIx::Recordset::Hash object.
- Added some code to handle table- and fieldnames which include
spaces and special charaters. This is still experimental.
- Fixed problem with !TableFilter setup
- Set correct brackets when creating an left outer join. This is necessary
for some database to join more the two tables.
0.23 22. Sept 2000
- separated DBIx base classes in different files, so they are visible and
searchable by CPAN
0.22 22. Sept 2000
- Fixed a problem that !PrimKey was not correctly set when
TableAttr('!Serial') was set.
- call $sth -> finish when attempt to read after the end of
a fetched set of records to avoid problems with to many
open cursors.
- backlinks of automaticly deteced links are now prefixed
with a star ('*') instead of one dash ('-') to avoid conflicts.
Also the prefix__ is used in backlinks now. Also if there is a
prefix like foo__ it is included in the backlink.
NOTE: This may break some software, but this step
is neccessary for uniqueness. Otherwise there are situations
where it can't be predict, which is the destination of
a given link.
- Fix incorrect SQL expression when an empty array is given as
parameter to an WHERE expression.
- Added method Reset to set the current record to the initial
state, so a call to Next returns the first record.
- If DBIx::Database -> new cannot handle a table it will only log
a warning, but won't die anymore.
- Added new Parameter !PreFetch and !Expires which can be used
when tieing a hash, which will prefetch the data from the
database and storing it in memory, so many accesses to these
keys will be much faster. It is also possible to give an
expiration time (or function) after which the data is
refetched.
0.21 21. Juni 2000
- Fixed problem with wrong StartRecordNo, which will also cause
problems with prev and next. Spotted by Alexander Siegel.
- Fixed problem that FETCHSIZE also returns one to much.
Spotted by Robert.
- Fixed the $last attribute of Search. Spotted by Roman Maeder.
- Fixed a bug in FETCHSIZE spotted by Robert.
- Allow different Filters for \\field and field in select.
- Fixed a problem with bind_param and LONGVARCHAR together
with DBD::ODBC
- Applied patch from Rob McMillin to the documentaion, which
corrects my bad english.
- DBIx::Recordset now supports serials fields. It is able
to insert a value from a sequence into a record upon insert
and return the value (for databases which supports sequences)
or simply return the value of last serial field (for databases
which does support serial/autoincrment fields)
- For DBMS with does not support sequences, there are two new
classes (DBIx::Recordset::DBSeq/FileSeq) which emulates sequences
in a DB table or the filesystem (see !SeqClass parameter)
- Execute now retrievs always the correct record after an INSERT
when a serial fields is specified. (and serial fields are supported
by the DBMS)
- Documented the $where parameter, which allows to pass a literal
SQL WHERE expresseion. (See SYNOPSIS)
- Added the $values parameter, so you can now pass parameters which
should be bind to the placeholders in the expression given
with $where. This allows more complex WHERE expressions to be setup.
- Wrote a short SYNOSIS sections, which gives some examples to make
it easier to start with DBIx::Recordset.
- Multiple values for a single field could now be also passed as
array ref.
- !TableFilter parameter is now always used as prefix, so only
thoses tables which starts with the prefix given in !TableFilter
are deteced by DBIx::Database.
- Removed the error message that zero record are updated instead
of one, because this were sometimes not correct. Spotted by
Roman Maeder.
- Fixed a problem which occurs when you access a linked table
for the first time (e.g. $set{-to}{foo}). Spotted by
Andrej Mikus.
- Removed a lot of -w warnings.
0.20 6. Jan 2000
- Fixed a problem with perl 5.004_04. Spotted by Paul J. Schinder.
- Added entry for DBD::Informix in Compat.pm send by Kelly Peet
- Added outer join syntax for DBD::Informix
- undef in !Filter passed to Recordset constructor overrides
filter-function for TableAttr.
- Fixed a problem with PrevNextForm and MoreRecord spotted by
Richard Chen and David M. Davission.
- Fixed some problems with eg/search.pl with help from
Richard Chen.
- Added date and time datatype to PostgreSQL quoting with help
from James Bishop.
- Added LastError function which returns the last error message
and code. This solves problems where the DBI object is already
out of scope and the $DBI::errstr isn't available anymore.
- Now all setup parameters (the ones starting with !), can also
be preset via DBIx::Database -> TableAttr.
- Fix a problem, that an error that sets $@ inside of DESTROY
will clear out an earlier error message. Based on a patch from
Dean Brettle.
- !Filter can take a thrid argument, which will cause
DBIx::Recordset to always execute the Input Filter
- Fixed a problem inside Flush, which will report sometime
'Not a scalar reference..'
- DBIx::Recordset handle uppercase field and tablesnames correctly
now. This is important to work with Oracle.
- New Parameter !TableFilter allows you to specify only a subset
of tables from the database/tablespace to work with
- !TableFilter is taken in account as prefix when searching
for links
- Added NeedNullInCreate for ODBC to work with MS SQL Server
- Recordset Object didn't get the DBHandle from Database object if
specified as !DataSource. Now it does. (Database object must
be created with !KeepOpen)
- !Filter can take rqINSERT or rqUPDATE as Argument after subref,
which causes the Filter to always execute on INSERT and/or UPDATE,
regardless, if there is a value for the field or not.
- Added methods TableName, TableNameWithoutFilter, TableFilter and
PrimKey.
- Support multiple Database object with different !TableFilter.
- !LongNames is now also supported for SELECTs which select not all,
but a specified number of fields (i.e. fields are ne '*').
- DBIx::Database TableLink method, returns all Links for a given
table when no linkname is given.
- Added more tests to ensure that new feature do not break old ones.
- removed call to non existing method $dbh->begin and call
$dbh->commit and $dbh->rollback only when AutoCommit is off, so
the DBIx::Recordset transaction methods, can be called regardless, if
the DB supports transactions or not. Patch from Alexander Smishlajev.
- make clean removes test.log and make realclean remove privious
configuration. Patch from Alexander Smishlajev.
- !DataSource can be a DBI database handle. Patch from Alexander Smishlajev.
- New method Dirty check if there is at leat one dirty row in a recordset.
Patch from Alexander Smishlajev.
- DROP test tables at end of test. Patch from Alexander Smishlajev.
- DBIx::Database now makes an connect to the database if KeepOpen is set
and the object is taken from an already stored one.
- Flush will call finish on open statement handles to avoid statement
handles that stay open to long.
- Add new parameter !DoOnConnect, which lets you specify any SQL statement
that should be execute after each connect.
- PrevNextForm could now take a hash ref as parameter and
addtionaly generate first, last and goto buttons
- Documentation updates.
- Added patch from Klaus Reger to avoid warning about undefined value
in DESTROY.
- Added more datatypes to quote for Pg and Oracle. Patch from Klaus Reger.
- Undef values in where expression are transfored to "is null" operators,
also when using placeholders.
- DBIx::Recordset now uses always bind_param, to avoid problems with quoting
and to speed up things. This means DBD drivers which does not support
placesholdes will not work anymore with DBIx::Recordset, but all
DBD drivers I know, supports now placesholder, so there should be no problem.
This also remove an potetial insecure eval form the BuildWhere function.
- DBIx::Recordset::Flush will update all dirty rows, also if some of them
can't be written to the
database. Patch from Alexander Smishlajev.
- DBIx::Recordset::Row::Flush make sure that exactly one row is updated. Based
on a patch from Alexander Smishlajev.
- By setting $FetchsizeWarn to zero, you can now make DBIx::Recordset return
the value of $sth -> rows, when the FETCHSIZE method is called by Perl.
This feature is still experimental, because I don't what different DBMS
returns in $sth -> rows, after an SELECT. Patch from Robert.
- By setting $PreserveCase, you can cause DBIx::Recordset to _not_ translate all
names to lowercase. Per default DBIx::Recordset translates all names (tablenames/
fieldnames) to lowercase to let application work with different DBMS.
- Updated docs of DBIx::Compat.
- If the DBMS supports it, DBIx::Recordset will use the LIMIT/OFFSET keywords if you
only want to fetch a limited number of row from the database (for parameters
$start and $max). Patch from Robert.
- Added an indroduction to DBIx::Recordset (based on my talk on the 1999 Perl conference)
which is viewable with perldoc Intrors.pod
0.19 30 Mar 1999
- changed order of group by and order by in select. Spotted by
Roman Maeder.
- DBIx::Recordset automaticly tried to determinated links between
tables (see also DBIx::Database)
- DBIx::Recordset will only insert a new record in the DB if
a record contains any data. This avoids inserting a record
that is only created when try to read an non existent record.
- Added new attribute !WriteMode which lets you specify which
write operation to the db are permited and which are not.
- Added new attribute '!LinkName' which will automaticly select
additional fields from links tables (DBMS must support LEFT JOIN)
- Fixed a problem that occurs in cleanup when you use muliple
nested links. (while (($k, $v) = each (%h)) is not reentrant!!)
- Execute will not do a Search after INSERT/UPDATE/DELETE when an
error has occured, so the error message is not overwritten
- use croak instead of die
- SQLDelete accepts empty WHERE, which will delete the whole table
- Added new object DBIx::Database which will parse the metadata of
the database and tried to automaticly determinate links between tables.
All the information is stored for later access by DBIx::Recordset
objects. This makes setup of a DBIx::Recordset object much faster,
especially when you use it in the startup file of your web server.
Also this gives you the possibility to speficy table attributes
only once at startup time.
- New method Links return the links of a DBIx::Recordset object
- New method Link4Field return the link of the specified field if any.
- tied hash now also support DELETE and CLEAR operation on a table.
- Insert now skips undef values, because there is no need to insert
NULL values.
- Update now set undef to NULL, even for database that does not use
placeholders.
- Added Filters. Filters allows you to specify an input and/or an
output function which transforms the correspondig fields before
input/output. This could for example be used to transform database
specific datatypes (e.g. date) to and from a common format, or just
to a human readable format.
- Fieldnames which are given to Update and Insert maybe prefixed with a
\ to avoid any transformation. Suggested by Frank Ridderbusch.
- Update and Insert converts numeric values to vaild numeric format, e.g.
'foo' will be converted to 0 when inserting in an integer field. Use
'\name' => 'foo' to avoid this conversion.
- 'null' and 'not null' now allowed as unary operators in where expression.
Suggested by Malcolm Cook.
- An value of undef is now allowed as key for the hash access. This is
usefull when your primary key is an autoincrement value. Then you can
add data with undef as key and call Flush to write out the record.
- automaticly finish a statement handle, when all data is fetched to
avoid unneccesaary open statement handles.
- Make all the new features are working with DBD::Orcale. Many thanks
to John Tobey for his help.
- Adapted Compat.pm entries for DBD::Sybase. Help and testing from
Malcolm Cook and Aaron Ross.
- $DBIx::Recordset::Debug now allows more values, so you can better
control what's being logged.
- Added !LongNames parameter which forces the hash keys to the form
table.field.
- Added methods for begin/commit/rollback to make sure DBIx::Recordset
internal data are correctly updated. Suggested by John Tobey.
0.18 3 Nov 1998
- Added !TabJoin parameter to support LEFT/RIGTH/INNER JOIN
syntax. Spotted by Roman Maeder.
- Added $group parameter to support GROUP BY
- Added $append parameter to append arbitrary data to the end
of an SELECT statement.
- Added !IgnoreEmpty which gives you the possiblity to
specify how DBIx::Recordset handles fields with undef value,
or empty strings. This may helpfull in a CGI enironement.
NOTE: with a value of 2 DBIx::Recordset behaves like versions
before 0.18
- fixed problem with handling of undef and zero values.
undef is now treated as SQL NULL and zero is handled
correctly. See also !IgnoreEmpty.
- Added entry for DBD::Oracle from Malcolm Cook
- fixed a bug in PrevNextForm spotted by David Crook
- The first Next after a sucessfull search returns now
the first row and not the second one, spotted by Malcom Cook.
- fixed a bug so that now linked tables a correctly flushed if modified
0.17 8 Oct 1998
- DBIx::Recordset now can handle subtables. This means if you have
one tables which has field which is an key for another table
you are able to access the second table via $set{first}{subfield}
DBIx::Recordset handles this by creating a special link field,
which is actually a new Recordset object.
Look at "Working with multiple tables" in the documentation.
- You can now call the method add to get an empty new record and
then simply put data in the current record
- Metadata is cached, that means if you open the same table a
second time DBIx::Recordset does not query the driver again
for the metadata, but uses the cached values instead.
- The Flush methods returns now undef on error, otherwise a true
value
- Adapted the data types for newer mSQL drivers in DBIx::Compat
- Added documentation about working with mulitple tables
- Added documentation about debugging and DBIx::Recordset logfile
- DBD::CSV works together with DBIx::Recordset
0.16 26 Aug 1998
- Reworked memory management
1.) DBIx::Recordset will be correctly destroyed and the statement handles
released, if the object goes out of scope. (You don't need
DBIx::Recordset::Undef anymore to destroy an object)
2.) Worked around a problem that perl will reference an object when a
reference to it is return from the each operator. This causes reference count
of the object not going to zero and therfore the object was not destroyed
when it goes out of scope.
- Ignore DBD::File for tests because it's just a base class
- Added support for drivers which does not have field types
($sth -> {TYPE})
- Fixed return values from Select/Update/Insert/Search/Execute so
they return undef on error. Spotted by Gary Ashton-Jones.
- Documentation update
0.15 31 Jul 1998
- Apdapt STORE method of tied array to perl5.005. DBIx::Recordset works
now correctly with perl5.004 and perl5.005
- Update can now handle updates of the primary keys correctly
- Insert can be done by calling Setup and then write values to the
array
- Update works now without an primary key
- Adapted Compat.pm to the new values in $sth -> {TYPE} of DBD::mSQL
in 1.19_19 and higher. Spotted by Ray Zimmerman,
0.14 1 May 1998
- first public release
DBIx-Recordset-0.26/Intrors.pod 0100644 0000000 0000000 00000041530 07757664230 015045 0 ustar root root =pod
=head1 Embperl and DBIx::Recordset
This introduction gives an overview how to use I together
with I. Since there are only a few I specific
things herein, it should be also usefull for non I users.
=head2 Overview
It is often very difficult to layout and design the output of normal CGI
scripts, because you are dealing with
HTML-sourcecode which spans multiple prints, and it isn't possible to use
some sort of HTML-editor. Embperl takes a different approach to this problem.
With Embperl, you can build your HTML-pages
with any tool you like, and you can embed fragments of code directly
in the page. This makes it much easier for
non-programmers to use, because they are able to use
their usual tools and they see the fragments of code as normal text.
This indroduction will deal with the Perl Modules I and
I, with a focus on database access.
=head2 Embperl
In brief, the purpose of Embperl is to execute code that is
embedded in HTML-pages as the page is requested from the server.
There are two ways to do this with Embperl. The first way is to
embed the code between [- and -] tags. This will cause
Embperl to execute the code and remove it from the source before sending
the page. The second way is to use [+ and +] as the delimiter, in which case
the code will be executed and the result of the execution is send to the
browser in place of the code.
All database access is done via the module I, which simplifies
a lot of common tasks when accessing a database via DBI.
=head1 Basic Example
The following example shows the basic functions of both modules. It shows the
contents of a table whose name is passed as a parameter:
To show the contents of the table C you may call it with:
http://www.domain.com/path/to/example1.htm?!Table=address
All query parameters are placed in the hash C<%fdat> by Embperl. In
our example, C<$fdat{'!Table'}> would contain the value C.
Additionally, Embperl replaces the code between C<[+> and C<+]> with the
result, so the headline of the page would be 'Contents of table "address"'.
The following [- -] block will be executed by Embperl. No trace of it will show up
in the page that is sent to the browser. The first line sets the database which should be
accessed. The syntax is the same as for the DBI connect call. If you omit the line, you
must additionally send the databasename as a query parameter - but for security reasons,
that isn't a very good idea.
=head2 Search
Next we call the method C of I, where we have the choice between
the object and the class-method. This applies to a lot of other methods as well.
When we call it as a class method, as we do in our example, it constructs a new I
object and uses the passed parameters to query the database. It's also possible to divide
these two steps and call C to first construct the object and then
C with this object to execute the Search. In the example above, we do
not pass any query parameters -- so C will return the contents of the
whole table. (I converts the call internally to the SQL
statement C