DBIx-Recordset-0.26/0040755000000000000000000000000010126462365012666 5ustar rootrootDBIx-Recordset-0.26/eg/0040755000000000000000000000000010126462365013261 5ustar rootrootDBIx-Recordset-0.26/eg/README0100644000000000000000000000055707757664232014162 0ustar rootrootDBIx::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.pl0100644000000000000000000002034007757664231015073 0ustar rootroot#!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 "\n" ; print "\n" ; print "\n" ; print "
Available DBD drivers
\n" ; print $q -> scrolling_list (-name=>"driver", -values=>\@drvs, -size=>7) ; 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

\n" ; @dsns = DBI->data_sources ($fdat{driver}) if ($fdat{driver}) ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; $q -> param (-name=>'$max', -value=>5) if (!$fdat{'$max'}) ; print " \n" ; print "\n" ; print "
Datasource:\n" ; # fixup for drivers which does not support the data_sources method @dsns = () if ($dsns[0] =~ /HASH/ ) ; # fixup for mSQL/mysql driver for ($i = 0; $i <= $#dsns; $i++) { $dsns[$i] =~ s/^DBI/dbi/ ; } if ($#dsns >= 0) { print $q -> popup_menu (-name=>"!DataSource", -size=>"1", -value=>\@dsns) ; } else { print $q -> textfield (-name=>"!DataSource", -size=>20) ; print "Datasource list not available, enter DSN manual" ; } print "
Table:" , $q -> textfield (-name=>"!Table", -size=>"20"), "
User:" , $q -> textfield (-name=>"!Username", -size=>"20"), "
Password:" , $q -> password_field (-name=>"!Password", -size=>"20"), "
Rows Per Page:" , $q -> textfield (-name=>'$max', -size=>5), "
\n" ; print "

\n" ; print $q -> submit (-value=>"Show Datasources", -name=>"showdsn") ; print $q -> submit (-value=>"Show whole table", -name=>"show") ; print $q -> submit (-value=>"Specify condition", -name=>"cond") ; print $q -> reset (-name=>"Reset") ; print $q -> endform ; } elsif (defined ($fdat{'cond'})) { #### enter a search condition ##### delete $fdat{'cond'}; ### setup recordset ### $set = DBIx::Recordset -> SetupObject (\%fdat) ; ### get the names of all fields ### $names = $set -> AllNames () if ($set) ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; if ($DBI::errstr) { print "\n" ; print " \n" ; print " \n" ; print "\n" ; } print "
Datasource:\n" ; print $fdat{"!DataSource"} ; print "
Table:$fdat{'!Table'}
User:$fdat{'!Username'}
Rows Per Page:$fdat{'$max'}
ERROR:" , $DBI::errstr , "

\n" ; if ($set) { print $q -> startform (-method => GET) ; print " \n" ; print " \n" ; print " \n" ; print " \n" ; print " \n" ; print " \n" ; foreach $n (@$names) { print " \n" ; print " \n" ; print " \n" ; print " \n" ; print " \n" ; } print "
FieldnameOperatorValue
$n", $q -> textfield (-name=>"\*$n", -size=>5), "", $q -> textfield (-name=>$n, -size=>20), "
\n" ; print "

\n" ; print $q -> hidden (-name=>'!DataSource', -value=>$fdat{'!DataSource'}) ; print $q -> hidden (-name=>'!Table', -value=>$fdat{'!Table'}) ; print $q -> hidden (-name=>'!Username', -value=>$fdat{'!Username'}) ; print $q -> hidden (-name=>'!Password', -value=>$fdat{'!Password'}) ; print $q -> hidden (-name=>'$max', -value=>$fdat{'$max'}) ; print $q -> hidden (-name=>'driver', -value=>$fdat{'driver'}) ; print $q -> submit (-value=>"Start search", -name=>"search") ; print $q -> submit (-value=>"Change Datasource", -name=>"showdsn") ; print $q -> reset (-name=>"Reset") ; print $q -> endform ; } } else { #### show query result #### ### setup object and do the query ### *set = DBIx::Recordset -> Search (\%fdat) ; ### get fieldnames of query ### $names = $set -> Names if ($set) ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; if ($DBI::errstr) { print "\n" ; print " \n" ; print " \n" ; print "\n" ; } if ($set) { print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "\n" ; print " \n" ; print " \n" ; print "\n" ; print "
Datasource:\n" ; print $fdat{"!DataSource"} ; print "
Table:$fdat{'!Table'}
User:$fdat{'!Username'}
Rows Per Page:$fdat{'$max'}
ERROR:" , $DBI::errstr , "
Current Start Row:" , $set -> StartRecordNo , "
SQL Statement:" , $set -> LastSQLStatement , "

\n" ; print "\n" ; print " \n" ; foreach $n (@$names) { print " \n" ; } print " \n" ; $row = 0 ; while ($r = $set[$row++]) { print " \n" ; foreach $n (@$names) { print " \n" ; } print " \n" ; } print "
$n
$$r{lc($n)}
\n" ; print $set -> PrevNextForm ('<>', \%fdat) ; print $q -> startform (-method => GET) ; while (($k, $v) = each (%fdat)) { if ($k ne 'refresh' && $k ne 'search' && $k ne 'showdsn' && $k ne 'cond') { print $q -> hidden (-name=>$k, -value=>$v) ; } } print "

\n" ; print $q ->submit (-value=>"Refresh", -name=>"refresh") ; print $q -> submit (-value=>"Specify condition", -name=>"cond") ; print $q -> submit (-value=>"Change Datasource", -name=>"showdsn") ; print $q -> endform ; } } ### cleanup ### DBIx::Recordset::Undef ('set') ; print $q -> end_html ; DBIx-Recordset-0.26/eg/search.htm0100644000000000000000000001142707757664232015257 0ustar rootroot Example for DBIx::Recordset

DBIx::Recordset Example

[- use DBIx::Recordset ; -][$if !defined ($fdat{'!DataSource'}) || !defined ($fdat{'!Table'})|| defined($fdat{'showdsn'}) $][- delete $fdat{'showdsn'}-]

[-@drvs = DBI->available_drivers ; -]Available DBD drivers
First of all you have to specify which database and table you want to access and enter the user and password (if required)

For the Datasource you have the following Options:
1.) choose a DBD driver from the list on the left and hit the Show Datasources button, then you can select a Datasource below (if your DBD driver supports the data_sources method)
2.) enter the Data Source directly in the text field below

[- @dsns = DBI->data_sources ($fdat{driver}) if ($fdat{driver}) ; @dsns = () if ($dsns[0] =~ /HASH/ ) ;-]

Datasource: [$if $#dsns >= 0 $][$else$] Datasource list not available, enter DSN manual [$endif$]
Table:
User:
Password:
Rows Per Page: [- $fdat{'$max'} ||= 5 -]

[$elsif defined ($fdat{'cond'}) $][-delete $fdat{'cond'}; $set = DBIx::Recordset -> SetupObject (\%fdat) ; $names = $set -> AllNames () if ($set) ; -]

[$if $DBI::errstr $] ERROR: [+ $DBI::errstr +][$else$]

DataSource: [+ $fdat{'!DataSource'} +]
Table: [+ $fdat{'!Table'} +]
User: [+ $fdat{'!User'} +]
Start Row: [+ $set -> StartRecordNo +]
SQL Statement: [+ $set -> LastSQLStatement +]

 

Fieldname Operator Value
[+ $$names[$row] +]

[$hidden$]

[$endif$][$else$][- *set = DBIx::Recordset -> Search (\%fdat) ; -] [- $names = $set -> Names () if ($set) ; -]

[$if $DBI::errstr $] ERROR: [+ $DBI::errstr +][$else$]

DataSource: [+ $fdat{'!DataSource'} +]
Table: [+ $fdat{'!Table'} +]
User: [+ $fdat{'!User'} +]
Start Row: [+ $set -> StartRecordNo +]
SQL Statement: [+ $set -> LastSQLStatement +]
[+ $$names[$col] +]
[+ $set[$row]{lc($$names[$col])} +]

[+ $set -> PrevNextForm ('<<Previous Records', 'Next Records>>', \%fdat) +]

[$hidden$]

[$endif$][$endif$]

[- DBIx::Recordset::Undef ('set') ; -]

DBIx-Recordset-0.26/TODO0100644000000000000000000000202707757664231013370 0ustar rootroot 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.pm0100644000000000000000000013335010126271603014723 0ustar rootroot ################################################################################### # # 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/0040755000000000000000000000000010126462365014620 5ustar rootrootDBIx-Recordset-0.26/Recordset/FileSeq.pm0100644000000000000000000001053207757664231016520 0ustar rootroot ################################################################################### # # 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.pm0100644000000000000000000000742607757664231016136 0ustar rootroot ################################################################################### # # 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/README0100644000000000000000000000774207757664232013572 0ustar rootroot 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/Changes0100755000000000000000000004503410126462311014156 0ustar rootroot 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.pod0100644000000000000000000004153007757664230015045 0ustar rootroot=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:

Contents of table "[+ $fdat{'!Table'} +]"

[- use DBIx::Recordset ; $fdat{'!DataSource'} = 'dbi:mysql:test' ; *set = DBIx::Recordset -> Search(\%fdat) ; $names = $set -> Names ; -] [- $rec = $set[$row] -]
[+ $names -> [$col] +]
[+ $rec -> {$names->[$col]} +]
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 command. I will generate the following SQL-query: SELECT * FROM address WHERE town='Berlin' ; The programmer doesn't have to pay attention to datatypes or quoting, this is done automatically by I. Also, complex queries are easy to implement: if, for example, the user wants to be able to search for a name or for a town, it would be possible to use the following form:
If the user enters "Richter" to the input field and presses the submit button, the following SQL-query will be generated: SELECT * FROM address WHERE name='Richter' OR town='Richter' ; Just by varying the parameters, it is possible to create simple or complex queries. In this way, you can use the same page with different parameters to create different sorts of queries. =head1 Multiple tables Until now, we only have worked with one table. In real life, you often have to deal with mulitple tables. For this reason, I helps you to reduce the expense associated with dealing with multiple tables. The simplest way to do this is to use the parameters C and C to tell I to create an SQL-join between two or more tables. This will link the tables together and the result looks just like one great table. More interesting is the possibility to create B<"links">. As an example, we'll take the same table we used above and divide it into two tables: one table for the names and one table for the towns. As a link we add an id-field. If the fields are following some naming convention, I is able to find this link automatically. If fields are named in another way, you have to tell I manually how the tables belong together. Table name: firstname, name, town_id Table town: id, town Here, every name has exactly one town and every town has a number of names assigned. With a simple modification of our first example, we could get the same result as above (except that we are now dealing with two tables instead of one): [- use DBIx::Recordset ; $db = DBIx::Database -> new ('dbi:mysql:test') ; $db -> TableAttr ('town', '!NameField', 'town') ; $fdat{'!DataSource'} = $db ; $fdat{'!LinkName'} = 3 ; *set = DBIx::Recordset -> Search(\%fdat) ; $names = $set -> Names ; -] And the request would be: http://www.domain.com/path/to/example2.htm?!Table=name =head2 DBIx::Database The new thing here is the C object. It gathers meta-information about the database and stores it for later use. Because of the names of the fields the object can detect that the field C in the table C points to field C in the table C. Additionally, we tell the C object which column(s) contain the human-readable name of the table C. These initialisations only have to be executed once. If you use I, for example, you should be able to move these lines into a common startup file. Also new is the parameter C. It tells I to return the human-readable name (in our example, C) instead of the field which links the two tables together (C in our example). Internally, I generates an SQL-join, so there is only one C [$if $set -> Link4Field($name) $] [### Link to other table -> generate HTML link ###] [- $link = $set -> Link($set -> Link4Field($name)) -] Show record from table '[+ $link -> {'!Table'} +]' [$endif$] [### Buttons for the different actions, the "name" attribute determinates ###] [### which action should be taken ###] [$endif$] [$endif$] When you first request this page, it will show the contents of the preset table. Alternatively, you can supply a tablename with the parameter CC. The link, which is shown at the bottom of the page, leads you to an imput form. There, you can fill in one or more fields and press the B button. This invokes the page itself and C will be instructed by the parameter C<=s>C (Name of the button "Search") to retrieve all records which match the entered values. If the query finds more then one record, a table with all records found will be shown. If there are more records than specified by the parameter C<$max>, only C<$max> records are displayed. If this is the case, the C method adds a "Previous" and a "Next" button to the page, allowing you to browse through the whole table. In the example above, we assume that every table has a primary key, which is passed to I by the line C<$fdat{'!PrimKey'} = 'id' ;>. The column which contains this primary key will be displayed as an HTML link containing the parameters to execute a search for just this record. As you can see in example4.htm, this can be used to display a form which includes some of the data from the found record (see below). Columns which are links to other tables will also be shown with an HTML-link. A click on that link will open the linked table or record. If the search only selects one record, the same form is shown, but with the data from the record filled in. Now it's possible to change the content. The changes are written to the database when you press the button B (parameter C<=update>). A new, empty form could be shown with the button B (parameter C<=empty>) and if you have written data into this empty form, you can add it as a new record with the B button (parameter C<=insert>). Last but not least, there is a B button (parameter C<=delete>). In all of these cases, the content of the form is sent to the page itself, and the C method at the start of the page executes the desired action. More comments can be found inside the source ([# #] blocks). DBIx-Recordset-0.26/test.pl0100644000000000000000000037230107773277732014225 0ustar rootroot# Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. use strict ; use vars qw{ *set1 *set2 *set3 *set4 *set5 *set6 *set7 *set8 *set9 *set10 *set11 *set12 *set13 *set14 *set15 *set16 *set17 *set18 *set19 *set20 *set1_ *set20c *set13h *set13h2 %set15h @TestData @TestFields %TestCheck %hTestFields1 %hTestIds1 @TestSetup @TestIds @Table $Driver $DSN $User $Password @drivers %Drivers $dbh $drv %errcnt $err $rc $contcnt $lasttest $errors $fatal $loaded $Join $SQLJoin $CreateNULL $EmptyIsNull *rs $rs @rs %rs $nocleanup $QuoteIdentifier} ; use Data::Dumper; BEGIN { $| = 1; $fatal = 1 ; print "\nLoading... "; } END { print "not ok 1\n" unless $loaded ; print "\nTest terminated with fatal error! Look at test.log\n" if ($fatal) ; } use DBIx::Recordset ; use DBIx::Recordset::FileSeq ; use DBIx::Recordset::DBSeq ; $loaded = 1; print "ok\n"; ######################### End of black magic. my $configfile = 'test/Config.pl' ; ################################################# sub printlog { print $_[0] ; print LOG $_[0] ; } sub printlogf { my $txt = shift ; if (!$txt) { $txt = " - $contcnt " ; $contcnt++ ; } else { $lasttest = $txt ; $contcnt = 2 ; } printlog ($txt . '... ' . (' ' x (35 - length ($txt)))) ; } sub sigwarn { my $msg = shift ; print LOG "WARN: $msg\n" ; } $SIG{__WARN__} = \&sigwarn ; ################################################# sub Check { my ($ids, $fields, $set, $idfield, $hash) = @_ ; my $id ; my $field ; my $i ; my $n ; my $is ; my $should ; my %setid ; my $dat ; my $v ; my $k ; local $^W = 0 ; print LOG "IDS EXPECTED: @$ids\n" ; $idfield ||= 'id' ; if (($dat = $$set[0]) && defined ($hash)) { $n = $#$fields + 1 ; $i = 0 ; $v = $$dat{$idfield} ; print LOG "Check Hash $idfield = $v : $$hash{$idfield}\n" ; while (($k, $v) = each (%$dat)) { $i++ ; print LOG "Field: $k Array: $v Hash: $$hash{$k}\n" ; if ($v ne $$hash{$k}) { printlog "ERROR in $lasttest\n" ; printlog "Field: $k Array: $v Hash: $$hash{$k}\n" ; $errors++ ; return 1 ; } } if ($i != $n) { printlog "ERROR in $lasttest\n" ; printlog "Wrong number of fields in ::Row (get $i, expected $n)\n" ; $errors++ ; return 1 ; } $i = 0 ; while (($k, $v) = each (%$hash)) { $i++ ; if ($v ne $$dat{$k}) { printlog "ERROR in $lasttest\n" ; printlog "Field: $k Array: $$dat{$k} Hash: $v\n" ; $errors++ ; return 1 ; } } if ($i != $n) { printlog "ERROR in $lasttest\n" ; printlog "Wrong number of fields in ::CurrRow (get $i, expected $n)\n" ; $errors++ ; return 1 ; } } $i = 0 ; $n = $#$ids + 1 ; while ($dat = $$set[$i]) { # print LOG "\tV "; $v = $$dat{$idfield} ; # print LOG " <$v> "; $v =~ s/^(.*?)\s*$/$1/ ; # print LOG " <$v> \n"; $setid{$v} = $i ; print LOG "idfield =$idfield;$v;$i; \n" ; print LOG "CHK-DAT:" ; while (($k, $v) = each (%$dat)) { $v ||= '' ; print LOG "$k=$v; " ; } #print "$idfield = $$dat{$idfield} = $i\n" ; $i++ ; print LOG "\n" ; } #print "get $i, expected $n\n" ; if ($i < $n) { printlog "ERROR in $lasttest\n" ; printlog "Got too few rows (got $i, expected $n)\n" ; $errors++ ; return 1 ; } if ($i > $n) { printlog "ERROR in $lasttest\n" ; printlog "Got too many rows (got $i, expected $n)\n" ; $errors++ ; return 1 ; } foreach $id (@$ids) { $dat = $$set[$setid{$id}] ; print LOG "id =$id;$setid{$id};dat = " . Dumper($dat) . "\n" ; foreach $field (@$fields) { if (exists ($TestCheck{$id}{$field})) { $should = $TestCheck{$id}{$field} ; print LOG "should-bound-a via $id and $field\n" ; } else { $should = $TestCheck{$TestCheck{$id}{'id'}}{$field} ; print LOG "should-bound-b\n" ; } if (!defined ($should) && !$EmptyIsNull) { $should = 'NULL' ; print LOG "should-bound-c\n" ; } if (defined ($$dat{$field}) || $EmptyIsNull) { $$dat{$field} =~ /^(.*?)\s*$/ ; $is = $1 ; print LOG "\$is = $1 because \$dat->{$field} = $$dat{$field}\n"; } else { $is = 'NULL' ; } print LOG "CHK-OK-a?: $idfield = $id; $field = <$is>; Should = <$should>\n" ; if ($should ne $is) { printlog "ERROR in $lasttest\n" ; printlog "$idfield = $id\n" ; printlog "The field named $field\n" ; printlog "has value $is\n" ; printlog "When it should have value $should\n" ; $errors++ ; return 1 ; } } } return 0 ; } ################################################# sub CheckField { my ($name, $is, $should) = @_ ; if (defined ($is) || $EmptyIsNull) { $is =~ /^(.*?)\s*$/ ; $is = $1 ; } else { $is = 'NULL' ; } $should = 'NULL' if (!defined ($should) && !$EmptyIsNull) ; print LOG "CHK-OK-b?: $name = <$is>; Should = <$should>\n" ; if ($should ne $is) { printlog "ERROR in $lasttest\n" ; printlog "Field = $name\n" ; printlog "Is = $is\n" ; printlog "Should = $should\n" ; $errors++ ; return 1 ; } return 0 ; } ################################################# sub AddTestRow { my ($tabno, $dat, $key) = @_ ; my $ex = 0 ; my $id ; my $v ; my $k ; $key ||= 'id' ; $id = undef ; $ex = exists ($$dat{$key}) ; if ($ex) { $id = $$dat{$key} ; } else { $id = $$dat{"*$key"} ; } $id =~ s/\'(.*?)\'/$1/ ; while (($k, $v) = each (%$dat)) { if (defined ($v) && $v eq 'NULL') { $v = undef ; } else { $v =~ s/\'(.*?)\'/$1/ if ($v) ; } $TestCheck{$id}{$k} = $v ; print LOG "TEST-DAT: Table $Table[$tabno] \$TestCheck{$id}{$k} = " . ($v || '') . "\n" ; if ($ex) { #$hTestFields{$k} = 1 ; $hTestFields1{$k} = 1 ; } } #if ($ex) { #$hTestIds{$id} = 1 ; $hTestIds1{$id} = 1 ; } delete $$dat{"*$key"} ; { local $^W= 0 ; my @names = map { ($_ =~ /\s/)?"\"$_\"":$_ } keys(%$dat) ; $k = join (',', @names) ; $v = join (',', values(%$dat)) ; } my $t = $Table[$tabno] ; $t = ($t =~ /\s/)?"\"$t\"":$t ; push (@TestSetup, "INSERT INTO $t ($k) VALUES ($v)") if ($v && $k) ; } sub AddTestRowAndId { my ($tabno, $dat, $key) = @_ ; my $id ; local %hTestIds1 ; local %hTestFields1 ; AddTestRow ($tabno, $dat, $key) ; foreach $id (@{$TestIds[$tabno]}) { $hTestIds1{$id} = 1 ; } my @ids = keys %hTestIds1 ; $TestIds[$tabno] = \@ids ; } sub DelTestRowAndId { my ($tabno, $id) = @_ ; my $tid ; delete $TestCheck{$id} ; local %hTestIds1 ; foreach $tid (@{$TestIds[$tabno]}) { $hTestIds1{$tid} = 1 if ($tid ne $id) ; } my @ids = keys %hTestIds1 ; $TestIds[$tabno] = \@ids ; } ################################################# sub AddTestData { my ($tabno, $key) = @_ ; my $dat ; local %hTestIds1 ; local %hTestFields1 ; my $ex = 0 ; $key ||= 'id' ; foreach $dat (@{$TestData[$tabno]}) { AddTestRow ($tabno, $dat, $key) ; } my @ids = keys %hTestIds1 ; $TestIds[$tabno] = \@ids ; my @fld = keys %hTestFields1 ; $TestFields[$tabno] = \@fld ; } ################################################# sub DropTestTables { my ($_dbh, @tlist) =@_; return unless ($dbh and @tlist); foreach (@tlist) { if ($QuoteIdentifier) { if (!$_dbh->do( "DROP TABLE \"$_\"")) { $_dbh->do( 'DROP TABLE "'. uc ($_) . '"') ; } } else { $_dbh->do( "DROP TABLE $_"); } }; print LOG '-- Dropped ', join(', ', @tlist), "\n" ; } ################################################# sub DoTest { $Driver = $_[0] ; $DSN = $_[1] ; $User = $_[2] ; $Password = $_[3] ; $Join = DBIx::Compat::GetItem ($Driver, 'SupportJoin') ; $SQLJoin = DBIx::Compat::GetItem ($Driver, 'SupportSQLJoin') ; $CreateNULL = DBIx::Compat::GetItem ($Driver, 'NeedNullInCreate') ; $EmptyIsNull= DBIx::Compat::GetItem ($Driver, 'EmptyIsNull') ; $QuoteIdentifier= DBIx::Compat::GetItem ($Driver, 'QuoteIdentifier') ; @Table = ('dbixrs1', 'dbixrs2', 'dbixrs3', 'dbixrs4', 'dbix_rs5', 'dbix_rs6', 'dbixseq', 'dbixrsdel') ; push @Table, 'DBIXRS 8' if ($QuoteIdentifier) ; $errors = 0 ; printlog "\nUsing the following parameters for testing:\n" ; printlog " DBD-Driver: $Driver\n" ; printlog " Database: $DSN\n" ; printlog " User: " . ($User || '') . "\n" ; printlog " Password: " . ($Password || '') . "\n" ; my $t ; for $t (@Table) { printlog " Table: $t\n" ; } #printlog "host: $Host\n" ; printlog "\n" ; $dbh = DBI->connect ("$DSN",$User, $Password) or die "Cannot connect to $DSN ($DBI::errstr)" ; printlog " Driver does not support joins, skiping tests with multiple tables\n\n" if (!$Join) ; no strict ; printlog " DBI-Version: " . $DBI::VERSION . "\n" ; printlog " DBD-Version: " . ${"DBD\:\:$Driver\:\:VERSION"} . "\n\n" ; use strict ; printlogf "Creating the testtables"; print LOG "\n--------------------\n" ; @TestSetup = ( "CREATE TABLE $Table[0] ( id INT $CreateNULL, name CHAR (20) $CreateNULL, value1 INT $CreateNULL, addon " . ($Driver eq 'mysql'?'TEXT':'CHAR(20)') . " $CreateNULL)", "CREATE TABLE $Table[1] ( id INTEGER $CreateNULL, name2 VARCHAR(20) $CreateNULL, value2 INTEGER $CreateNULL, $Table[3]_id INTEGER $CreateNULL)", "CREATE TABLE $Table[2] ( value1 INTEGER $CreateNULL, txt " . ($Driver eq 'mysql'?'TEXT':'CHAR(20)') . " $CreateNULL )", "CREATE TABLE $Table[3] ( id INTEGER $CreateNULL, typ CHAR(20) $CreateNULL)", "CREATE TABLE $Table[4] ( id INTEGER $CreateNULL, txt5 CHAR(20) $CreateNULL, up__rs5_id INTEGER $CreateNULL, a__rs6_id INTEGER $CreateNULL, b__rs6_id INTEGER $CreateNULL)", "CREATE TABLE $Table[5] ( id INTEGER $CreateNULL, txt6 CHAR(20) $CreateNULL)", "CREATE TABLE $Table[6] ( name varchar (32) $CreateNULL, cnt INTEGER $CreateNULL, maxcnt integer)", "CREATE TABLE $Table[7] ( id integer, dbixrsdel_id integer)", ) ; push @TestSetup, "CREATE TABLE \"$Table[8]\" ( id integer, \"id 2\" integer)" if ($QuoteIdentifier) ; @TestData = ( [ { 'id' => 1 , 'name' => "'First Name'", 'value1' => 9991, 'addon' => "'Is'" }, { 'id' => 2 , 'name' => "'Second Name'", 'value1' => 9992, 'addon' => "'it'" }, { 'id' => 3 , 'name' => "'Third Name'", 'value1' => 9993, 'addon' => "'it ok?'" }, { 'id' => 4 , 'name' => "'Fourth Name'", 'value1' => 9994, 'addon' => "'Or not??'" }, { 'id' => 5 , 'name' => "'Fivth Name'", 'value1' => 9995, 'addon' => "'Is'" }, { 'id' => 6 , 'name' => "'Sixth Name'", 'value1' => 9996, 'addon' => "'it'" }, { 'id' => 7 , 'name' => "'Seventh Name'", 'value1' => 9997, 'addon' => "'it ok?'" }, { 'id' => 8 , 'name' => "'Eighth Name'", 'value1' => 9998, 'addon' => "'Or not??'" }, { 'id' => 9 , 'name' => "'Ninth Name'", 'value1' => 9999, 'addon' => "'Is'" }, { 'id' => 10, 'name' => "'Tenth Name'", 'value1' => 99910, 'addon' => "'it'" }, { 'id' => 11, 'name' => "'Eleventh Name'", 'value1' => 99911, 'addon' => "'it ok?'" }, { 'id' => 12, 'name' => "'Twelvth Name'", 'value1' => 99912, 'addon' => "''" }, { 'id' => 13, 'name' => "'Thirdteenth Name'", 'value1' => 'NULL', 'addon' => 'NULL' }, { 'id' => 14, 'name' => "'Fourteenth Name'", 'value1' => 0, 'addon' => 'NULL' }, { 'id' => 15, 'name' => "15", 'value1' => 15, 'addon' => 'NULL' }, { 'id' => 16, 'name' => "15", 'value1' => 15, 'addon' => 'NULL' }, { 'id' => 17, 'name' => 1, 'value1' => 2, 'addon' => 'NULL' }, { 'id' => 18, 'name' => 3, 'value1' => 42, 'addon' => '42' }, { 'id' => 19, 'name' => 2, 'value1' => 42, 'addon' => '42' }, { 'id' => 20, 'name' => 2, 'value1' => 3, 'addon' => '42' }, ], [ { 'id' => 1 , 'name2' => "'First Name in Tab2'", 'value2' => 29991, "$Table[3]_id" => 1 }, { 'id' => 2 , 'name2' => "'Second Name in Tab2'", 'value2' => 29992, "$Table[3]_id" => 2 }, { 'id' => 3 , 'name2' => "'Third Name in Tab2'", 'value2' => 29993, "$Table[3]_id" => 3 }, { 'id' => 4 , 'name2' => "'Fourth Name in Tab2'", 'value2' => 29994, "$Table[3]_id" => 4 }, ], [ { '*id' => 1 , 'txt' => "'First Item (9991 )'", 'value1' => 9991, }, { '*id' => 2 , 'txt' => "'Second Item (9992 )'", 'value1' => 9992, }, { '*id' => 3 , 'txt' => "'Third Item (9993 )'", 'value1' => 9993, }, { '*id' => 4 , 'txt' => "'Fourth Item (9994 )'", 'value1' => 9994, }, { '*id' => 5 , 'txt' => "'Fivth Item (9995 )'", 'value1' => 9995, }, { '*id' => 6 , 'txt' => "'Sixth Item (9996 )'", 'value1' => 9996, }, { '*id' => 7 , 'txt' => "'Seventh Item (9997 )'", 'value1' => 9997, }, { '*id' => 8 , 'txt' => "'Eighth Item (9998 )'", 'value1' => 9998, }, { '*id' => 9 , 'txt' => "'Ninth Item (9999 )'", 'value1' => 9999, }, { '*id' => 10, 'txt' => "'Tenth Item (99910)'", 'value1' => 99910,}, { '*id' => 11, 'txt' => "'Eleventh Item(99911)'", 'value1' => 99911,}, { '*id' => 12, 'txt' => "'Twelvth Item (99912)'", 'value1' => 99912,}, ], [ { 'id' => 1 , 'typ' => "'First item Type 1'" }, { 'id' => 1 , 'typ' => "'First item Type 2'" }, { 'id' => 1 , 'typ' => "'First item Type 3'" }, { 'id' => 2 , 'typ' => "'Second item Type 1'" }, { 'id' => 2 , 'typ' => "'Second item Type 2'" }, { 'id' => 2 , 'typ' => "'Second item Type 3'" }, { 'id' => 2 , 'typ' => "'Second item Type 4'" }, { 'id' => 3 , 'typ' => "'Third item Type 1'" }, # { 'id' => 4 , 'typ' => "'Fours item Type 1'" }, ], [ { 'id' => 1 , 'txt5' => "'1 in Tab5'", "up__rs5_id" => 'NULL', "a__rs6_id" => 1, "b__rs6_id" => 1 }, { 'id' => 2 , 'txt5' => "'2 in Tab5'", "up__rs5_id" => 1, "a__rs6_id" => 2, "b__rs6_id" => 1 }, { 'id' => 3 , 'txt5' => "'3 in Tab5'", "up__rs5_id" => 1, "a__rs6_id" => 3, "b__rs6_id" => 1 }, { 'id' => 4 , 'txt5' => "'4 in Tab5'", "up__rs5_id" => 1, "a__rs6_id" => 4, "b__rs6_id" => 1 }, { 'id' => 5 , 'txt5' => "'5 in Tab5'", "up__rs5_id" => 2, "a__rs6_id" => 5, "b__rs6_id" => 1 }, { 'id' => 6 , 'txt5' => "'6 in Tab5'", "up__rs5_id" => 2, "a__rs6_id" => 6, "b__rs6_id" => 1 }, { 'id' => 7 , 'txt5' => "'7 in Tab5'", "up__rs5_id" => 3, "a__rs6_id" => 7, "b__rs6_id" => 1 }, { 'id' => 8 , 'txt5' => "'8 in Tab5'", "up__rs5_id" => 4, "a__rs6_id" => 8, "b__rs6_id" => 1 }, { 'id' => 9 , 'txt5' => "'9 in Tab5'", "up__rs5_id" => 4, "a__rs6_id" => 9, "b__rs6_id" => 1 }, ], [ { 'id' => 1 , 'txt6' => "'1 in Tab6'", }, { 'id' => 2 , 'txt6' => "'2 in Tab6'", }, { 'id' => 3 , 'txt6' => "'3 in Tab6'", }, { 'id' => 4 , 'txt6' => "'4 in Tab6'", }, { 'id' => 5 , 'txt6' => "'5 in Tab6'", }, { 'id' => 6 , 'txt6' => "'6 in Tab6'", }, { 'id' => 7 , 'txt6' => "'7 in Tab6'", }, { 'id' => 8 , 'txt6' => "'8 in Tab6'", }, { 'id' => 9 , 'txt6' => "'9 in Tab6'", }, ], [], [ { id => 1, dbixrsdel_id => 2 }, { id => 2, dbixrsdel_id => 3 }, { id => 3, dbixrsdel_id => 4 }, { id => 4, dbixrsdel_id => 1 }, ], ) ; $TestData[8] = [ { 'id' => 1 , 'id 2' => 12, }, { 'id' => 2 , 'id 2' => 13 }, ] if ($QuoteIdentifier) ; my $i ; for ($i = 0; $i <= $#Table; $i++) { AddTestData ($i) if ($i != 3 && $TestData[$i]) ; } AddTestData (3, 'typ') ; #@AllTestIds = keys %hTestIds ; #@AllTestFields = keys %hTestFields ; my %count = (); my $element ; foreach $element (@{$TestFields[0]}) { $count{$element}++ } foreach $element (@{$TestFields[1]}) { $count{$element}++ } my @TestFields0_1 = keys %count ; #goto skip1 ; my $st ; my $rc ; print LOG "--- \"Table does not exist\" warnings may appear here. Please ignore.\n" ; DropTestTables($dbh, @Table); foreach $st (@TestSetup) { $rc = $dbh -> do ($st) ; print LOG "$st ->($rc)\n" ; if (!$rc && $st =~ /^\S/) { die "Cannot do $st ($DBI::errstr)" ; } } skip1: #$dbh->commit () ; # $dbh->disconnect ; # or die "Cannot disconnect from $DSN ($DBI::errstr)" ; # undef $dbh ; printlog "ok\n"; ######################################################################################### # # Start Tests # $errors = 0 ; # --------------------- printlogf "Setup Object for $Table[0]"; print LOG "\n--------------------\n" ; $set1 = DBIx::Recordset->New ($dbh, $Table[0], $User, $Password) or die "not ok\n" ; tie @set1, 'DBIx::Recordset', $set1 ; tie %set1, 'DBIx::Recordset::CurrRow', $set1 ; printlog "ok\n"; printlogf "SQLSelect All"; print LOG "\n--------------------\n" ; $set1 -> SQLSelect () or die "not ok ($DBI::errstr)" ; Check ($TestIds[0], $TestFields[0], \@set1, undef, \%set1) or print "ok\n" ; #$^W = 1 ; DBIx::Recordset::Undef ('set1') ; # --------------------- printlogf "Setup Object for $Table[1]"; print LOG "\n--------------------\n" ; $set2 = tie @set2, 'DBIx::Recordset', { '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[1]} or die "not ok ($DBI::errstr)" ; tie %set2, 'DBIx::Recordset::CurrRow', $set2 ; printlog "ok\n"; printlogf "SQLSelect All"; print LOG "\n--------------------\n" ; $set2 -> SQLSelect () or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set2, undef, \%set2) or print "ok\n" ; DBIx::Recordset::Undef ('set2') ; # --------------------- if ($Join) { printlogf "Setup Object for $Table[0], $Table[1]"; print LOG "\n--------------------\n" ; $set3 = DBIx::Recordset->New ($dbh, "$Table[0], $Table[1]", $User, $Password) or die "not ok\n" ; tie @set3, 'DBIx::Recordset', $set3 ; tie %set3, 'DBIx::Recordset::CurrRow', $set3 ; printlog "ok\n"; printlogf "SQLSelect All"; print LOG "\n--------------------\n" ; if ($Driver eq 'mSQL') { my @f ; my $f ; my $fl ; foreach $fl (@{$TestFields[0]}) { push @f, "$Table[0].$fl" ; } foreach $fl (@{$TestFields[1]}) { push @f, "$Table[1].$fl" ; } $f = join (',', @f) ; $set3 -> SQLSelect ("$Table[0].id=$Table[1].id", $f) or die "not ok ($DBI::errstr)" ; } else { $set3 -> SQLSelect ("$Table[0].id=$Table[1].id") or die "not ok ($DBI::errstr)" ; } Check ($TestIds[1], \@TestFields0_1, \@set3) or print "ok\n" ; DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Setup Object for $Table[0], $Table[2]"; print LOG "\n--------------------\n" ; $set4 = tie @set4, 'DBIx::Recordset', { '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0], $Table[2]"} or die "not ok ($DBI::errstr)" ; tie %set4, 'DBIx::Recordset::CurrRow', $set4 ; printlog "ok\n"; printlogf "SQLSelect All"; print LOG "\n--------------------\n" ; if ($Driver eq 'mSQL') { $set4 -> SQLSelect ("$Table[0].value1=$Table[2].value1", "$Table[0].id, $Table[0].name, $Table[2].txt") or die "not ok ($DBI::errstr)" ; } else { $set4 -> SQLSelect ("$Table[0].value1=$Table[2].value1", "id, name, txt") or die "not ok ($DBI::errstr)" ; } Check ($TestIds[2], ['id', 'name', 'txt'], \@set4) or print "ok\n" ; DBIx::Recordset::Undef ('set4') ; # --------------------- printlogf "Setup Object for $Table[0], $Table[3]"; print LOG "\n--------------------\n" ; $set5 = DBIx::Recordset->New ($dbh, "$Table[0], $Table[3]", $User, $Password) or die "not ok\n" ; tie @set5, 'DBIx::Recordset', $set5 ; tie %set5, 'DBIx::Recordset::CurrRow', $set5 ; printlog "ok\n"; printlogf "SQLSelect All"; print LOG "\n--------------------\n" ; if ($Driver eq 'mSQL') { $set5 -> Select ("$Table[0].id=$Table[3].id", "$Table[0].name, $Table[3].typ") or die "not ok ($DBI::errstr)" ; } else { $set5 -> Select ("$Table[0].id=$Table[3].id", "name, typ") or die "not ok ($DBI::errstr)" ; } Check ($TestIds[3], ['name', 'typ'], \@set5, 'typ') or print "ok\n" ; DBIx::Recordset::Undef ('set5') ; } # if ($Join) # --------------------- printlogf "Setup Object for $Table[0]"; print LOG "\n--------------------\n" ; $set1 = tie @set1, 'DBIx::Recordset', { '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0]} or die "not ok ($DBI::errstr)" ; tie %set1, 'DBIx::Recordset::CurrRow', $set1 ; printlog "ok\n"; # --------------------- printlogf "Select id (where as hash)"; print LOG "\n--------------------\n" ; $set1 -> Select ({'id'=>2, '$operator'=>'='}) or die "not ok ($DBI::errstr)" ; Check ([2], $TestFields[0], \@set1, undef, \%set1) or print "ok\n" ; # --------------------- printlogf "Select id (where as string)"; print LOG "\n--------------------\n" ; $set1 -> Select ('id=4') or die "not ok ($DBI::errstr)" ; Check ([4], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select name"; print LOG "\n--------------------\n" ; $set1 -> Select ({name => 'Third Name', '$operator'=>'='}) or die "not ok ($DBI::errstr)" ; Check ([3], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- if ($Join) { printlogf "Select $Table[0].name"; print LOG "\n--------------------\n" ; $set1 -> Select ({"$Table[0].name" => 'Fourth Name', '$operator'=>'='}) or die "not ok ($DBI::errstr)" ; Check ([4], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select $Table[1].name2 id=id"; print LOG "\n--------------------\n" ; *set1_ = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0], $Table[1]", '!Fields' => "$Table[0].id, $Table[0].name, $Table[0].value1, $Table[0].addon", "'$Table[1].name2" => 'Second Name in Tab2', "\\$Table[0].id" => "$Table[1].id", '$operator'=>'='}) or die "not ok ($DBI::errstr)" ; Check ([2], $TestFields[0], \@set1_) or print "ok\n" ; # --------------------- printlogf "Select $Table[1].value2 id=id"; print LOG "\n--------------------\n" ; $set1_ -> Select ({"\#$Table[1].value2" => '29993', "\\$Table[0].id" => "$Table[1].id", '$operator' => '='}) or die "not ok ($DBI::errstr)" ; Check ([3], $TestFields[0], \@set1_) or print "ok\n" ; DBIx::Recordset::Undef ('set1_') ; } # --------------------- printlogf "Select multiply values"; print LOG "\n--------------------\n" ; $set1 -> Select ({name => "Second Name\tFirst Name", '$operator' => '='}) or die "not ok ($DBI::errstr)" ; Check ([1,2], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select multiply values array ref"; print LOG "\n--------------------\n" ; $set1 -> Select ({name => ["Second Name", "Third Name"], '$operator' => '='}) or die "not ok ($DBI::errstr)" ; Check ([2, 3], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select multiply values & operators"; print LOG "\n--------------------\n" ; $set1 -> Select ({id => [5,7], '*id' => ['>=', '<='], '$valueconj' => 'and'}) or die "not ok ($DBI::errstr)" ; Check ([5, 6, 7], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select \$valuesplit"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => "9991 9992\t9993", '$valuesplit' => ' |\t', '$operator' => '='}) or die "not ok ($DBI::errstr)" ; Check ([1,2,3], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select multiply fields 1"; print LOG "\n--------------------\n" ; $set1 -> Select ({'+name&value1' => "9992", '$operator' => '='}) or die "not ok ($DBI::errstr)" ; Check ([2], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select multiply fields 2"; print LOG "\n--------------------\n" ; $set1 -> Select ({'+name&value1' => 15, '$operator' => '='}) or die "not ok ($DBI::errstr)" ; Check ($Driver eq 'CSV'?[3]:[15, 16], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select multiply fields & values"; print LOG "\n--------------------\n" ; $set1 -> Select ({'+name&value1' => "2\t3", '$operator' => '='}) or die "not ok ($DBI::errstr)" ; Check ($Driver eq 'CSV'?[1,2]:[17,19,19,20], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- $set1 -> Search ({id => 1,name => 'First Name',addon => 'Is'}) or die "not ok ($DBI::errstr)" ; printlogf "MoreRecords yes"; print LOG "\n--------------------\n" ; if (!$set1 -> MoreRecords) { printlog "ERROR in $lasttest: MoreRecords returns false\n" ; $errors++ ; } else { print "ok\n" ; } # --------------------- printlogf "Search"; print LOG "\n--------------------\n" ; Check ([1], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Search"; print LOG "\n--------------------\n" ; $set1 -> Search ({id => 1,name => 'First Name',addon => 'Is'}) or die "not ok ($DBI::errstr)" ; Check ([1], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "MoreRecords no"; print LOG "\n--------------------\n" ; if ($set1 -> MoreRecords) { printlog "ERROR in $lasttest: MoreRecords returns true\n" ; $errors++ ; } else { print "ok\n" ; } # --------------------- if ($Driver ne 'CSV') { printlogf "Search with subexpr"; print LOG "\n--------------------\n" ; $set1 -> Search ({'$expr' => { id => 2, '*id' => '>' }, name => 'S', '*name' => '>'}) or die "not ok ($DBI::errstr)" ; Check ([3, 6, 7, 10, 12, 13], $TestFields[0], \@set1) or print "ok\n" ; } # --------------------- printlogf "Search first two"; print LOG "\n--------------------\n" ; $set1 -> Search ({'$start'=>0,'$max'=>2, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; Check ([1,2], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "MoreRecords with \$max"; print LOG "\n--------------------\n" ; if ($set1 -> MoreRecords) { printlog "ERROR in $lasttest: MoreRecords returns true\n" ; $errors++ ; } else { print "ok\n" ; } # --------------------- printlogf "MoreRecords(1) with \$max "; print LOG "\n--------------------\n" ; if (!$set1 -> MoreRecords(1)) { printlog "ERROR in $lasttest: MoreRecords returns false\n" ; $errors++ ; } else { print "ok\n" ; } # --------------------- printlogf "MoreRecords with \$max 2"; print LOG "\n--------------------\n" ; if ($set1 -> MoreRecords) { printlog "ERROR in $lasttest: MoreRecords returns true\n" ; $errors++ ; } else { print "ok\n" ; } # --------------------- printlogf "New Search for more"; print LOG "\n--------------------\n" ; *set6 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '$start' => 2, '$max' => 2, '$next' => 1, '$order' => 'id', "id" => "1\t2\t3\t4\t5\t6" }) or die "not ok ($DBI::errstr)" ; Check ([5,6], $TestFields[0], \@set6) or print "ok\n" ; # --------------------- printlogf "MoreRecords(1) with \$max no"; print LOG "\n--------------------\n" ; if ($set6 -> MoreRecords(1)) { printlog "ERROR in $lasttest: MoreRecords returns true\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set6') ; # --------------------- printlogf "New Search for more 2"; print LOG "\n--------------------\n" ; *set6 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '$start' => 2, '$max' => 2, '$next' => 1, '$order' => 'id', "id" => "1\t2\t3\t4\t5\t6" }) or die "not ok ($DBI::errstr)" ; my @data ; push @data, $set6[0] ; push @data, $set6[1] ; push @data, $set6[2] ; push @data, $set6[2] ; push @data, $set6[2] ; Check ([5,6], $TestFields[0], \@data) or print "ok\n" ; # --------------------- printlogf "MoreRecords(1) with \$max no 2"; print LOG "\n--------------------\n" ; if ($set6 -> MoreRecords(1)) { printlog "ERROR in $lasttest: MoreRecords returns true\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set6') ; # --------------------- printlogf "Search next ones"; print LOG "\n--------------------\n" ; $set1 -> Search ({'$start'=>0,'$max'=>2, '$next'=>1, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; Check ([3,4], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Search prevs one"; print LOG "\n--------------------\n" ; $set1 -> Search ({'$start'=>2,'$max'=>1, '$prev'=>1, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; Check ([2], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Search last ones"; print LOG "\n--------------------\n" ; $set1 -> Search ({'$start'=>5,'$max'=>5, '$next'=>1, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; Check ([11, 12, 13, 14, 15], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Search goto last"; print LOG "\n--------------------\n" ; $set1 -> Search ({'$start'=>5,'$max'=>5, '$last'=>1, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; Check ([16, 17, 18, 19, 20], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- if ($Driver eq 'mSQL') { printlogf "Select NULL values"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => undef}) or die "not ok ($DBI::errstr)" ; Check ([13], $TestFields[0], \@set1) or print "ok\n" ; } else { printlogf "Select NULL values"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => 'xyz', '*value1' => 'is null'}) or die "not ok ($DBI::errstr)" ; Check ([13], $TestFields[0], \@set1) or print "ok\n" ; if ($Driver ne 'CSV') { #--------------------- printlogf "Select NOT NULL values"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => 'xyz', '*value1' => 'is not null'}) or die "not ok ($DBI::errstr)" ; Check ([(1..12), (14..20)], $TestFields[0], \@set1) or print "ok\n" ; } } #--------------------- printlogf "Select empty values"; print LOG "\n--------------------\n" ; if (!$EmptyIsNull) { $set1 -> Select ({addon => ''}) or die "not ok ($DBI::errstr)" ; Check ([12], $TestFields[0], \@set1) or print "ok\n" ; } else { printlog "skipping test on this plattform\n" ; } # --------------------- printlogf "Select 0"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => 0}) or die "not ok ($DBI::errstr)" ; Check ([14], $TestFields[0], \@set1) or print "ok\n" ; DBIx::Recordset::Undef ('set1') ; # --------------------- if ($Driver ne 'Sybase') { *set1 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '!IgnoreEmpty' => 1}) or die "not ok ($DBI::errstr)" ; printlogf "Select NULL values Ig-1"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => undef}) or die "not ok ($DBI::errstr)" ; Check ($TestIds[0], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select empty values Ig-1"; print LOG "\n--------------------\n" ; if (!$EmptyIsNull) { $set1 -> Select ({addon => ''}) or die "not ok ($DBI::errstr)" ; Check ([12], $TestFields[0], \@set1) or print "ok\n" ; } else { printlog "skipping test on this plattform\n" ; } # --------------------- printlogf "Select 0 Ig-1"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => 0}) or die "not ok ($DBI::errstr)" ; Check ([14], $TestFields[0], \@set1) or print "ok\n" ; DBIx::Recordset::Undef ('set1') ; # --------------------- *set1 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '!IgnoreEmpty' => 2}) or die "not ok ($DBI::errstr)" ; printlogf "Select NULL values Ig-2"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => undef}) or die "not ok ($DBI::errstr)" ; Check ($TestIds[0], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select empty values Ig-2"; print LOG "\n--------------------\n" ; $set1 -> Select ({addon => ''}) or die "not ok ($DBI::errstr)" ; Check ($TestIds[0], $TestFields[0], \@set1) or print "ok\n" ; # --------------------- printlogf "Select 0 Ig-2"; print LOG "\n--------------------\n" ; $set1 -> Select ({value1 => 0}) or die "not ok ($DBI::errstr)" ; Check ([14], $TestFields[0], \@set1) or print "ok\n" ; DBIx::Recordset::Undef ('set1') ; } # --------------------- if ($Join) { my $t0 ; my $t2 ; if ($Driver eq 'mSQL') { $t0 = "$Table[0]." ; $t2 = "$Table[2]." ; } else { $t0 = '' ; $t2 = '' ; } printlogf "New Search"; print LOG "\n--------------------\n" ; *set6 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0], $Table[2]", '!TabRelation' => "$Table[0].value1=$Table[2].value1", '!Fields' => "$t0\lid, $t0\lname, $t2\ltxt", "$t0\lid" => "2\t4" }) or die "not ok ($DBI::errstr)" ; Check ([2,4], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; # --------------------- printlogf "Search cont"; print LOG "\n--------------------\n" ; $set6 -> Search ({"$t0\lname" => "Fourth Name" }) or die "not ok ($DBI::errstr)" ; Check ([4], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; # --------------------- printlogf "Search \$operator <"; print LOG "\n--------------------\n" ; $set6 -> Search ({"$t0\lid" => 3, '$operator' => '<' }) or die "not ok ($DBI::errstr)" ; Check ([1,2], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; # --------------------- printlogf "Search *id *name"; print LOG "\n--------------------\n" ; $set6 -> Search ({"$t0\lid" => 4, "$t0\lname" => 'Second Name', "\*$t0\lid" => '<', "\*$t0\lname" => '<>' }) or die "not ok ($DBI::errstr)" ; Check ([1,3], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; # --------------------- printlogf "Search \$conj or"; print LOG "\n--------------------\n" ; $set6 -> Search ({"$t0\lid" => 2, "$t0\lname" => 'Fourth Name', "\*$t0\lid" => '<', "\*$t0\lname" => '=', '$conj' => 'or' }) or die "not ok ($DBI::errstr)" ; Check ([1,4], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; # --------------------- printlogf "Search multfield *"; print LOG "\n--------------------\n" ; $set6 -> Search ({"+$t0\lid|$t0\ldbixrs1.value1" => "7\t9991", "$t0\lname" => 'Fourth Name', "\*$t0\lid" => '<', "\*$t0\lvalue1" => '=', "\*$t0\lname" => '<>', '$conj' => 'and' }) or die "not ok ($DBI::errstr)" ; Check ([1,2,3,5,6,7,8,9,10,11,12], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; # --------------------- printlogf "Search \$compconj"; print LOG "\n--------------------\n" ; $set6 -> Search ({"+$t0\lid|$t0\laddon" => "6\t42", "$t0\lname" => 'Fourth Name', "\*$t0\lid" => '>', "\*$t0\laddon" => '<>', "\*$t0\lname" => '=', '$compconj' => 'and', '$conj' => 'or' }) or die "not ok ($DBI::errstr)" ; if (!$EmptyIsNull) { Check ([4,7,8,9,10,11,12], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; } else { Check ([1,3,4,5,7,8,9,10,11], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; } # --------------------- printlogf "Order, Group, Append"; print LOG "\n--------------------\n" ; $set6 -> Search ({ id => 5, '$order' => 'id', '$group' => 'name', '$append' => ';;', '$makesql' => 1 }) ; { #my $should = 'SELECT id, name, txt FROM dbixrs1, dbixrs3 WHERE (dbixrs1.value1=dbixrs3.value1) and ( ((id = 5))) GROUP BY name ORDER BY id ;;' ; my $should = 'SELECT id, name, txt FROM dbixrs1, dbixrs3 WHERE (dbixrs1.value1=dbixrs3.value1) and ( ( (id = ?))) GROUP BY name ORDER BY id ;; ' ; # if ($set6 -> {'*Placeholders'}) ; $should = 'SELECT dbixrs1.id, dbixrs1.name, dbixrs3.txt FROM dbixrs1, dbixrs3 WHERE (dbixrs1.value1=dbixrs3.value1) and ( ((id = 5))) GROUP BY name ORDER BY id ;;' if ($Driver eq 'mSQL') ; my $is = $set6 -> LastSQLStatement ; if ($is ne $should) { print "ERROR in $lasttest: SQL Statement wrong\n" ; print LOG "Is: $is\n" ; print LOG "Should: $should\n" ; $errors++ ; } else { print "ok\n" ; } } DBIx::Recordset::Undef ('set6') ; # --------------------- if ($SQLJoin == 1) { printlogf "Search with JOIN"; print LOG "\n--------------------\n" ; *set6 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0], $Table[2]", '!TabJoin' => "$Table[0] LEFT JOIN $Table[2] on ($Table[0].value1=$Table[2].value1)", '!Fields' => "$t0\lid, $t0\lname, $t2\ltxt", "$t0\lid" => "1\t4" }) or die "not ok ($DBI::errstr)" ; Check ([1,4], ['id', 'name', 'txt'], \@set6) or print "ok\n" ; } DBIx::Recordset::Undef ('set6') ; # --------------------- printlogf "New Search id_typ"; print LOG "\n--------------------\n" ; *set7 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0], $Table[3]", '!TabRelation' => "$Table[0].id=$Table[3].id", '!Fields' => "$Table[0].name, $Table[3].typ"}) or die "not ok ($DBI::errstr)" ; Check ($TestIds[3], ['name', 'typ'], \@set7, 'typ') or print "ok\n" ; DBIx::Recordset::Undef ('set7') ; # --------------------- printlogf "!LongNames with !Fields"; print LOG "\n--------------------\n" ; *set7 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0], $Table[3]", '!TabRelation' => "$Table[0].id=$Table[3].id", '!LongNames' => 1, '!Fields' => "$Table[0].id, $Table[0].name, typ"}) or die "not ok ($DBI::errstr)" ; my $names = $set7 -> Names ; my $e = $errors ; if ($#$names != 2) { printlog "ERROR in $lasttest: wrong number of names ($#$names)\n" ; $errors++ ; } elsif ($names -> [0] ne "$Table[0].id" || $names -> [1] ne "$Table[0].name" || $names -> [2] ne "$Table[3].typ") { printlog "ERROR in $lasttest: names not ok (@$names)\n" ; $errors++ ; } print "ok\n" if ($e == $errors) ; DBIx::Recordset::Undef ('set7') ; # --------------------- printlogf "!LongNames without !Fields"; print LOG "\n--------------------\n" ; *set7 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0], $Table[3]", '!TabRelation' => "$Table[0].id=$Table[3].id", '!LongNames' => 1, }) or die "not ok ($DBI::errstr)" ; $names = $set7 -> Names ; $e = $errors ; if ($#$names != 5) { printlog "ERROR in $lasttest: wrong number of names ($#$names)\n" ; $errors++ ; } elsif ($names -> [0] ne "$Table[0].id" || $names -> [1] ne "$Table[0].name" || $names -> [2] ne "$Table[0].value1" || $names -> [3] ne "$Table[0].addon" || $names -> [4] ne "$Table[3].id" || $names -> [5] ne "$Table[3].typ") { printlog "ERROR in $lasttest: names not ok (@$names)\n" ; $errors++ ; } print "ok\n" if ($e == $errors) ; DBIx::Recordset::Undef ('set7') ; } # --------------------- if ($QuoteIdentifier) { printlogf "Quoted Identifiers"; print LOG "\n--------------------\n" ; *set1 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "\"$Table[8]\"", }) or die "not ok ($DBI::errstr)" ; Check ([1, 2], $TestFields[8], \@set1) or print "ok\n" ; DBIx::Recordset::Undef ('set1') ; } # --------------------- printlogf "New Setup"; print LOG "\n--------------------\n" ; *set8 = DBIx::Recordset -> Setup ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]"}) or die "not ok ($DBI::errstr)" ; print "ok\n" ; printlogf "SQLInsert"; print LOG "\n--------------------\n" ; my %h = ('id' => 21, 'name2' => 'sqlinsert id 21', 'value2'=> 1021) ; $set8 -> SQLInsert ('id, name2, value2', "21, 'sqlinsert id 21', 1021") or die "not ok ($DBI::errstr)" ; AddTestRowAndId (1, \%h) ; $set8 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set8) or print "ok\n" ; DBIx::Recordset::Undef ('set8') ; # --------------------- printlogf "New Insert"; print LOG "\n--------------------\n" ; %h = ('id' => 22, 'name2' => 'sqlinsert id 22', 'value2'=> 1022) ; *set9 = DBIx::Recordset -> Insert ({%h, ('!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]")}) or die "not ok ($DBI::errstr)" ; AddTestRowAndId (1, \%h) ; $set9 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set9) or print "ok\n" ; # --------------------- printlogf "Insert without quoting"; print LOG "\n--------------------\n" ; %h = ('id' => 229, '\name2' => "'sqlinsert id 229'", 'value2'=> undef) ; $set9 -> Insert (\%h) or die "not ok ($DBI::errstr)" ; $h{name2} = 'sqlinsert id 229' ; AddTestRowAndId (1, \%h) ; $set9 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set9) or print "ok\n" ; # --------------------- printlogf "Update"; print LOG "\n--------------------\n" ; %h = ('id' => 22, 'name2' => 'sqlinsert id 22u', 'value2'=> 2022) ; $set9 -> Update (\%h, 'id=22') or die "not ok ($DBI::errstr)" ; AddTestRowAndId (1, \%h) ; $set9 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set9) or print "ok\n" ; # --------------------- printlogf "Update without quoting"; print LOG "\n--------------------\n" ; %h = ('id' => 229, '\name2' => "'sqlinsert id 229uq'", 'value2'=> 2022) ; $set9 -> Update (\%h, 'id=229') or die "not ok ($DBI::errstr)" ; $h{name2} = 'sqlinsert id 229uq' ; AddTestRowAndId (1, \%h) ; $set9 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set9) or print "ok\n" ; # --------------------- if ($Driver ne 'Sybase') { printlogf "Update to NULL"; print LOG "\n--------------------\n" ; %h = ('id' => 229, 'value2'=> undef) ; $set9 -> Update (\%h, {id=>229}) or die "not ok ($DBI::errstr)" ; AddTestRowAndId (1, \%h) ; $set9 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set9) or print "ok\n" ; } DBIx::Recordset::Undef ('set9') ; # --------------------- printlogf "New Update"; print LOG "\n--------------------\n" ; %h = ('id' => 21, 'name2' => 'sqlinsert id 21u', 'value2'=> 2021) ; { local *set10 = DBIx::Recordset -> Update ({%h, ('!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!PrimKey' => 'id')}) or die "not ok ($DBI::errstr)" ; AddTestRowAndId (1, \%h) ; $set10 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set10) or print "ok\n" ; } # We use closing block instead of Undef here #DBIx::Recordset::Undef ('set10') ; # --------------------- printlogf "New Delete"; print LOG "\n--------------------\n" ; %h = ('id' => 21, 'name2' => 'ssdadadqlid 21u', 'value2'=> 202331) ; *set11 = DBIx::Recordset -> Delete ({%h, ('!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!PrimKey' => 'id')}) or die "not ok ($DBI::errstr)" ; DelTestRowAndId (1, 21) ; $set11 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set11) or print "ok\n" ; DBIx::Recordset::Undef ('set11') ; # --------------------- printlogf "New Execute Search (default)"; print LOG "\n--------------------\n" ; *set12 = DBIx::Recordset -> Execute ({'id' => 20, '*id' => '<', '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; Check ([1, 2, 3, 4], $TestFields[1], \@set12) or print "ok\n" ; # --------------------- printlogf "Execute =search"; print LOG "\n--------------------\n" ; *set13 = DBIx::Recordset -> Execute ({'=search' => 'ok', 'name' => 'Fourth Name', '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; Check ([4], $TestFields[0], \@set13) or print "ok\n" ; DBIx::Recordset::Undef ('set13') ; # --------------------- printlogf "Execute =insert"; print LOG "\n--------------------\n" ; $set12 -> Execute ({'=insert' => 'ok', 'id' => 31, 'name2' => 'insert by exec', 'value2' => 3031, # Execute should ignore the following params, since it is already setup '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "quztr", '!PrimKey' => 'id99'}) or die "not ok ($DBI::errstr)" ; AddTestRowAndId (1, { 'id' => 31, 'name2' => 'insert by exec', 'value2' => 3031, }) ; $set12 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set12) or print "ok\n" ; # --------------------- printlogf "Execute =update"; print LOG "\n--------------------\n" ; $set12 -> Execute ({'=update' => 'ok', 'id' => 31, 'name2' => 'update by exec'}) or die "not ok ($DBI::errstr)" ; AddTestRowAndId (1, { 'id' => 31, 'name2' => 'update by exec', }) ; $set12 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set12) or print "ok\n" ; # --------------------- printlogf "Execute =insert"; print LOG "\n--------------------\n" ; $set12 -> Execute ({'=insert' => 'ok', 'id' => 32, 'name2' => 'insert/upd by exec', 'value2' => 3032}) or die "not ok ($DBI::errstr)" ; AddTestRowAndId (1, { 'id' => 32, 'name2' => 'insert/upd by exec', 'value2' => 3032, }) ; $set12 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set12) or print "ok\n" ; # --------------------- # #printlogf "Execute =update =insert 2"; #print LOG "\n--------------------\n" ; # #$set12 -> Execute ({'=insert' => 'ok', # '=update' => 'ok', # 'id' => 32, # 'name2' => 'ins/update by exec', # 'value2' => 3032}) or die "not ok ($DBI::errstr)" ; # #AddTestRowAndId (1, { # 'id' => 32, # 'name2' => 'ins/update by exec', # 'value2' => 3032, # }) ; # #$set12 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; #Check ($TestIds[1], $TestFields[1], \@set12) or print "ok\n" ; # # --------------------- printlogf "Execute =delete"; print LOG "\n--------------------\n" ; $set12 -> Execute ({'=delete' => 'ok', 'id' => 32, 'name2' => 'ins/update by exec', 'value2' => 3032}) or die "not ok ($DBI::errstr)" ; DelTestRowAndId (1, 32) ; $set12 -> SQLSelect () or die "not ok in SELECT ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set12) or print "ok\n" ; DBIx::Recordset::Undef ('set12') ; # --------------------- if ($Driver ne 'CSV') { printlogf "DeleteWithLinks"; print LOG "\n--------------------\n" ; *set1 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[7], }) or die "not ok ($DBI::errstr)" ; $set1 -> {'*Links'}{'-dbixrsdel'}{'!OnDelete'} = DBIx::Recordset::odDELETE ; Check ([1, 2, 3, 4], $TestFields[7], \@set1) or print "ok\n" ; printlogf ""; print LOG "\n--------------------\n" ; $set1 -> DeleteWithLinks ({ 'id' => 1 }) or die "not ok ($DBI::errstr)" ; $set1 -> Search ; Check ([], $TestFields[7], \@set1) or print "ok\n" ; DBIx::Recordset::Undef ('set1') ; } # --------------------- printlogf "Array Update/Insert"; print LOG "\n--------------------\n" ; *set20 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '$order' => 'id', '!PrimKey' => 'id', 'id' => 7, '*id' => '<' }) or die "not ok ($DBI::errstr)" ; Check ([1,2,3,4,5,6], $TestFields[0], \@set20) or print "ok\n" ; $set20[3]{name} = 'New Name on id 4' ; $set20[3]{value1} = 4444 ; AddTestRowAndId (0, { 'id' => 4, 'name' => 'New Name on id 4', 'value1' => 4444 }) ; $set20[7]{id} = 1234 ; $set20[7]{name} = 'New rec' ; AddTestRowAndId (0, { 'id' => 1234, 'name' => 'New rec', }) ; $set20 -> Search ({'id' => 4}) or die "not ok ($DBI::errstr)" ; printlogf ""; Check ([4], $TestFields[0], \@set20) or print "ok\n" ; $set20 -> Search ({'id' => 1234}) or die "not ok ($DBI::errstr)" ; printlogf ""; Check ([1234], $TestFields[0], \@set20) or print "ok\n" ; # --------------------- printlogf "Array Update/Insert -> Flush"; print LOG "\n--------------------\n" ; $set20[0]{id} = 1234 ; $set20[0]{name} = 'New rec 1234' ; *set20c = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '$order' => 'id', '!PrimKey' => 'id', 'id' => 1234}) or die "not ok ($DBI::errstr)" ; Check ([1234], $TestFields[0], \@set20c) or print "ok\n" ; printlogf "Dirty"; print LOG "\n--------------------\n" ; if ($set20->Dirty) { print LOG "DIRTY: ok\n"; print "ok\n" ; } else { printlog "ERROR in $lasttest: not set\n" ; $errors++; } # write it to the db print LOG "Flush\n" ; $set20 -> Flush () ; printlogf ""; if (!$set20->Dirty) { print LOG "DIRTY: ok\n"; print "ok\n" ; } else { printlog "ERROR in $lasttest: set\n" ; $errors++; } AddTestRowAndId (0, { 'id' => 1234, 'name' => 'New rec 1234', }) ; #$set20c -> Search ({'id' => 1234}) or die "not ok ($DBI::errstr)" ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set20c') ; *set20c = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '!PrimKey' => 'id', 'id' => 1234}) or die "not ok ($DBI::errstr)" ; printlogf "Array Update/Insert -> Flush 2"; Check ([1234], $TestFields[0], \@set20c) or print "ok\n" ; printlogf "Array Insert Hashref"; print LOG "\n--------------------\n" ; $set20[8] = {id => 12345, 'name' => 'New rec 12345'} ; # write it to the db print LOG "Flush\n" ; $set20 -> Flush () ; AddTestRowAndId (0, { 'id' => 12345, 'name' => 'New rec 12345', }) ; #$set20c -> Search ({'id' => 12345}) or die "not ok ($DBI::errstr)" ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) # we try here undef instead of DBIx::Recordset::Undef ('set20c') ; undef *set20c ; *set20c = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '!PrimKey' => 'id', 'id' => 12345}) or die "not ok ($DBI::errstr)" ; Check ([12345], $TestFields[0], \@set20c) or print "ok\n" ; printlogf "Array Add Record"; print LOG "\n--------------------\n" ; $set20 -> Add ({id => 123456, 'name' => 'New rec 123456'}) ; # write it to the db print LOG "Flush\n" ; $set20 -> Flush () ; AddTestRowAndId (0, { 'id' => 123456, 'name' => 'New rec 123456', }) ; #$set20c -> Search ({'id' => 123456}) or die "not ok ($DBI::errstr)" ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set20c') ; *set20c = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '!PrimKey' => 'id', 'id' => 123456}) or die "not ok ($DBI::errstr)" ; Check ([123456], $TestFields[0], \@set20c) or print "ok\n" ; printlogf "Array Add Empty Record (Ndx)"; print LOG "\n--------------------\n" ; my $ndx = $set20 -> Add () ; $set20[$ndx]{id} = 1234567 ; $set20[$ndx]{name} = 'New rec 1234567' ; # write it to the db print LOG "Flush\n" ; $set20 -> Flush () ; AddTestRowAndId (0, { 'id' => 1234567, 'name' => 'New rec 1234567', }) ; #$set20c -> Search ({'id' => 1234567}) or die "not ok ($DBI::errstr)" ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set20c') ; *set20c = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '!PrimKey' => 'id', 'id' => 1234567}) or die "not ok ($DBI::errstr)" ; Check ([1234567], $TestFields[0], \@set20c) or print "ok\n" ; printlogf "Array Add Empty Record (CurrRec)"; print LOG "\n--------------------\n" ; $set20 -> Add () ; $set20{id} = 876 ; $set20{name} = 'New rec 876' ; # write it to the db print LOG "Flush\n" ; $set20 -> Flush () ; AddTestRowAndId (0, { 'id' => 876, 'name' => 'New rec 876', }) ; #$set20c -> Search ({'id' => 876}) or die "not ok ($DBI::errstr)" ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set20c') ; *set20c = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '!PrimKey' => 'id', 'id' => 876}) or die "not ok ($DBI::errstr)" ; Check ([876], $TestFields[0], \@set20c) or print "ok\n" ; DBIx::Recordset::Undef ('set20') ; DBIx::Recordset::Undef ('set20c') ; { local *set13 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; # --------------------- printlogf "Select id (Hash)"; print LOG "\n--------------------\n" ; my %set13h ; tie %set13h, 'DBIx::Recordset::Hash', $set13 ; $set13h[0] = $set13h{2} ; Check ([2], $TestFields[1], \@set13h) or print "ok\n" ; # --------------------- printlogf "Select name (Hash)"; print LOG "\n--------------------\n" ; my %set13h2 ; tie %set13h2, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!PrimKey' => 'name2'} ; $set13h2[0] = $set13h2{'Third Name in Tab2'} ; Check ([3], $TestFields[1], \@set13h2) or print "ok\n" ; # --------------------- printlogf "Iterate over ::Hash"; print LOG "\n--------------------\n" ; # { my $i ; my $v ; my $k ; my $n ; my @set13h ; $i = 0 ; while (($k, $v) = each %set13h) { @set13h = () ; $set13h[0] = $v ; printlogf "" if ($i > 0) ; Check ([$k], $TestFields[1], \@set13h) or print "ok\n" ; $i++ ; } $n = ($#{$TestIds[1]})+1 ; if ($i != $n) { print "ERROR in $lasttest\n" ; print "Not enougth records (get $i, expected $n)\n" ; $errors++ ; } } #untie %set13h ; #@set13h = () ; #DBIx::Recordset::Undef ('set13') ; } # --------------------- { printlogf "Select name (PreFetch Hash)"; print LOG "\n--------------------\n" ; my %set13h3 ; my @set13h3 ; tie %set13h3, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[0], '!PreFetch' => {'*id' => '<', 'id' => 7}, '!PrimKey' => 'id'} ; $set13h3[0] = $set13h3{2} ; Check ([2], $TestFields[0], \@set13h3) or print "ok\n" ; # --------------------- printlogf "Iterate over ::Hash PreFetch"; print LOG "\n--------------------\n" ; # { my $i ; my $v ; my $k ; my $n ; my @set13h ; $i = 0 ; while (($k, $v) = each %set13h3) { @set13h = () ; $set13h[0] = $v ; printlogf "" if ($i > 0) ; Check ([$k], $TestFields[0], \@set13h) or print "ok\n" ; $i++ ; } $n = 6 ; if ($i != $n) { print "ERROR in $lasttest\n" ; print "Not enougth records (get $i, expected $n)\n" ; $errors++ ; } } #untie %set13h ; #@set13h = () ; #DBIx::Recordset::Undef ('set13') ; } # --------------------- { printlogf "PreFetch Hash with merge"; print LOG "\n--------------------\n" ; my %set13h3 ; tie %set13h3, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[3], '!PreFetch' => '*', '!MergeFunc' => sub { my ($a, $b) = @_ ; $a->{typ} .= ' , ' . $b->{typ} ; $a->{typ} =~ s/\s+/ /g ; }, '!PrimKey' => 'id'} ; my $ec = $errors ; CheckField ('id', $set13h3{1}{id}, 1) ; CheckField ('typ', $set13h3{1}{typ}, 'First item Type 1 , First item Type 2 , First item Type 3') ; CheckField ('id', $set13h3{2}{id}, 2) ; CheckField ('typ', $set13h3{2}{typ}, 'Second item Type 1 , Second item Type 2 , Second item Type 3 , Second item Type 4') ; CheckField ('id', $set13h3{3}{id}, 3) ; CheckField ('typ', $set13h3{3}{typ}, 'Third item Type 1') ; print "ok\n" if ($ec == $errors) ; } # --------------------- { printlogf "Hash with merge"; print LOG "\n--------------------\n" ; my %set13h3 ; tie %set13h3, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[3], '!MergeFunc' => sub { my ($a, $b) = @_ ; $a->{typ} .= ' , ' . $b->{typ} ; $a->{typ} =~ s/\s+/ /g ; }, '!PrimKey' => 'id'} ; my $ec = $errors ; my $x ; $x = $set13h3{1} ; CheckField ('id', $x -> {id}, 1) ; CheckField ('typ', $x -> {typ}, 'First item Type 1 , First item Type 2 , First item Type 3') ; $x = $set13h3{2} ; CheckField ('id', $x -> {id}, 2) ; CheckField ('typ', $x -> {typ}, 'Second item Type 1 , Second item Type 2 , Second item Type 3 , Second item Type 4') ; $x = $set13h3{3} ; CheckField ('id', $x -> {id}, 3) ; CheckField ('typ', $set13h3{3}{typ}, 'Third item Type 1') ; print "ok\n" if ($ec == $errors) ; } # --------------------- *set14 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!HashAsRowKey' => 1, '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; printlogf "Select id (HashAsRowKey)"; print LOG "\n--------------------\n" ; my @set14h = () ; my @set15h = () ; $set14h[0] = $set14{3} ; Check ([3], $TestFields[1], \@set14h) or print "ok\n" ; @set14h = () ; @set15h = () ; # --------------------- printlogf "Select name (Hash) with setup"; print LOG "\n--------------------\n" ; tie %set15h, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!PrimKey' => 'name2'} or die "not ok ($DBI::errstr)" ; $set15h[0] = $set15h{'Fourth Name in Tab2'} ; Check ([4], $TestFields[1], \@set15h) or print "ok\n" ; # --------------------- printlogf "Modify Hash"; print LOG "\n--------------------\n" ; $set15h{'Fourth Name in Tab2'}{value2} = 4444 ; tied (%set15h) -> Flush () ; AddTestRowAndId (1, { 'id' => 4, 'value2' => 4444 , }) ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set14') ; *set14 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!HashAsRowKey' => 1, '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; $set14 -> Search ({'id' => 4}) ; Check ([4], $TestFields[1], \@set14) or print "ok\n" ; # --------------------- printlogf "Add To Hash"; print LOG "\n--------------------\n" ; $set15h{'Fifth Name in Tab2'}{id} = 5 ; $set15h{'Fifth Name in Tab2'}{value2} = 5555 ; tied (%set15h) -> Flush () ; AddTestRowAndId (1, { 'id' => 5, 'name2'=> 'Fifth Name in Tab2', 'value2' => 5555 , }) ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set14') ; *set14 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!HashAsRowKey' => 1, '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; $set14 -> Search ({'id' => 5}) ; Check ([5], $TestFields[1], \@set14) or print "ok\n" ; # --------------------- printlogf "Add Hashref To Hash "; print LOG "\n--------------------\n" ; $set15h{'Sixth Name in Tab2'}= {id => 6, value2 => 6666} ; tied (%set15h) -> Flush () ; AddTestRowAndId (1, { 'id' => 6, 'name2'=> 'Sixth Name in Tab2', 'value2' => 6666 , }) ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set14') ; *set14 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!HashAsRowKey' => 1, '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; $set14 -> Search ({'id' => 6}) ; Check ([6], $TestFields[1], \@set14) or print "ok\n" ; # --------------------- printlogf "Modify PrimKey in Hash"; print LOG "\n--------------------\n" ; $set15h{'Fourth Name in Tab2'}{name2} = 'New Fourth Name' ; tied (%set15h) -> Flush () ; AddTestRowAndId (1, { 'id' => 4, 'name2' => 'New Fourth Name' , }) ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set14') ; *set14 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!HashAsRowKey' => 1, '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; $set14 -> Search ({'id' => 4}) ; Check ([4], $TestFields[1], \@set14) or print "ok\n" ; # --------------------- printlogf "Add undef as PrimKey to Hash"; print LOG "\n--------------------\n" ; my $ud ; $set15h{$ud}{id} = 531 ; $set15h{$ud}{value2} = 9531 ; tied (%set15h) -> Flush () ; AddTestRowAndId (1, { 'id' => 531, 'value2' => 9531 , }) ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set14') ; *set14 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!HashAsRowKey' => 1, '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; $set14 -> Search ({'id' => 531}) ; Check ([531], $TestFields[1], \@set14) or print "ok\n" ; # --------------------- printlogf ""; $set15h{$ud}{id} = 532 ; $set15h{$ud}{value2} = 9532 ; tied (%set15h) -> Flush () ; $set15h{$ud}{id} = 533 ; $set15h{$ud}{value2} = 9533 ; tied (%set15h) -> Flush () ; AddTestRowAndId (1, { 'id' => 532, 'value2' => 9532 , }) ; AddTestRowAndId (1, { 'id' => 533, 'value2' => 9533 , }) ; # The resetup is neccessary to work with all, also stupid drivers (MSAccess) DBIx::Recordset::Undef ('set14') ; *set14 = DBIx::Recordset -> Setup ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!HashAsRowKey' => 1, '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; $set14 -> Search ; Check ($TestIds[1], $TestFields[1], \@set14) or print "ok\n" ; # --------------------- printlogf "Test Syntax error"; print LOG "\n--------------------\n" ; $rc = $set14 -> Update ({id => 9999}, "qwer=!§" ) and die "not ok (returns $rc)" ; if (defined ($rc)) { printlog "ERROR in $lasttest: Update should return undef\n" ; $errors++ ; } elsif (!DBIx::Recordset -> LastError) { printlog "ERROR in $lasttest: LastError should return error message\n" ; $errors++ ; } elsif (!$set14 -> LastError) { printlog "ERROR in $lasttest: LastError should return error message\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set14') ; untie %set15h ; printlogf "Test error within setup"; print LOG "\n--------------------\n" ; *set14 = DBIx::Recordset -> Update ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[1], '!HashAsRowKey' => 1, '!PrimKey' => 'id', id => 9999}, 'qwert=!%&') ; if (!DBIx::Recordset -> LastError) { printlog "ERROR in $lasttest: LastError should return error message\n" ; $errors++ ; } elsif (!$set14 -> LastError) { printlog "ERROR in $lasttest: LastError should return error message\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set14') ; # --------------------- printlogf "MoreRecords on empty set"; print LOG "\n--------------------\n" ; *set4 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'id' => 9753 }) or die "not ok ($DBI::errstr)" ; if ($set4 -> MoreRecords) { printlog "ERROR in $lasttest: MoreRecords returns true\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set4') ; # --------------------- printlogf "First on empty set"; print LOG "\n--------------------\n" ; *set5 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'id' => 9753 }) or die "not ok ($DBI::errstr)" ; if ($set5 -> First) { printlog "ERROR in $lasttest: First returns true\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set5') ; # --------------------- printlogf "Next on empty set"; print LOG "\n--------------------\n" ; *set6 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'id' => 9753 }) or die "not ok ($DBI::errstr)" ; if ($set6 -> Next) { printlog "ERROR in $lasttest: Next returns true\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set6') ; # --------------------- printlogf "Use First to get first record"; print LOG "\n--------------------\n" ; *set7 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '$order'=>'id', '*id' => '>=', 'id' => 2 }) or die "not ok ($DBI::errstr)" ; { my $r = $set7 -> First ; my @r = ($r) ; Check ([2], $TestFields[1], \@r) or print "ok\n" ; } DBIx::Recordset::Undef ('set7') ; # --------------------- printlogf "Use First/Next to get all records"; print LOG "\n--------------------\n" ; *set8 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", }) or die "not ok ($DBI::errstr)" ; { my $r ; my @r ; push @r, $set8 -> First ; push @r, $r while ($r = $set8 -> Next) ; Check ($TestIds[1], $TestFields[1], \@r) or print "ok\n" ; } DBIx::Recordset::Undef ('set8') ; # --------------------- printlogf "Use Next to get all records"; print LOG "\n--------------------\n" ; *set9 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", }) or die "not ok ($DBI::errstr)" ; { my $r ; my @r ; push @r, $r while ($r = $set9 -> Next) ; Check ($TestIds[1], $TestFields[1], \@r) or print "ok\n" ; } # --------------------- printlogf "Use Reset/Next to get all records"; print LOG "\n--------------------\n" ; { $set9 -> Reset ; my $r ; my @r ; push @r, $r while ($r = $set9 -> Next) ; Check ($TestIds[1], $TestFields[1], \@r) or print "ok\n" ; } DBIx::Recordset::Undef ('set9') ; # --------------------- printlogf "Update via assigning array ref"; print LOG "\n--------------------\n" ; *set1 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '$order' => 'id' }) or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set1) or print "ok\n" ; #my @array = $set1{value2} ; #my $id ; # #for ($id = 0; $id <= $#array; $id++) # { # print LOG "CHK: array[$id] = $array[$id], should $TestCheck{$id}{value2}\n" ; # if ($array[$id] != $TestCheck{$id}{value2}) # { # $errors++ ; # printlog ("Error array[$id] = $array[$id], should $TestCheck{$id}{value2}\n") # } # } $set1{value2} = [1234, 2345, 3456, 4567] ; $set1 -> Flush ; AddTestRowAndId (1, { 'id' => 1, 'value2' => '1234', }) ; AddTestRowAndId (1, { 'id' => 2, 'value2' => '2345', }) ; AddTestRowAndId (1, { 'id' => 3, 'value2' => '3456', }) ; AddTestRowAndId (1, { 'id' => 4, 'value2' => '4567', }) ; DBIx::Recordset::Undef ('set1') ; *set1_ = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", }) or die "not ok ($DBI::errstr)" ; printlogf ""; Check ($TestIds[1], $TestFields[1], \@set1_) or print "ok\n" ; DBIx::Recordset::Undef ('set1_') ; # --------------------- printlogf "Update via assigning array ref 2"; print LOG "\n--------------------\n" ; *set1 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'id' => 9753 }) or die "not ok ($DBI::errstr)" ; Check ([], $TestFields[1], \@set1) or print "ok\n" ; $set1{id} = [9753, 9754, 9755, 9756] ; $set1{name2} = ['a', 'b', 'c', 'd'] ; $set1{value2} = [12340, 23450, 34560, 45670] ; $set1 -> Flush ; AddTestRowAndId (1, { 'id' => 9753, 'name2' => 'a', 'value2' => '12340', }) ; AddTestRowAndId (1, { 'id' => 9754, 'name2' => 'b', 'value2' => '23450', }) ; AddTestRowAndId (1, { 'id' => 9755, 'name2' => 'c', 'value2' => '34560', }) ; AddTestRowAndId (1, { 'id' => 9756, 'name2' => 'd', 'value2' => '45670', }) ; DBIx::Recordset::Undef ('set1') ; *set1_ = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'id' => "9753\t9754\t9755\t9756" }) or die "not ok ($DBI::errstr)" ; printlogf ""; Check ([9753, 9754, 9755, 9756], $TestFields[1], \@set1_) or print "ok\n" ; DBIx::Recordset::Undef ('set1_') ; # --------------------- printlogf "Select with sub table"; print LOG "\n--------------------\n" ; *set1 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!Links' => { 'subid' => { '!Table' => $Table[3], '!LinkedField' => 'id', '!PrimKey' => 'typ' } }, 'id' => 2, }) or die "not ok ($DBI::errstr)" ; Check ([2], $TestFields[0], \@set1) or print "ok\n" ; printlogf ""; Check (['Second item Type 1', 'Second item Type 2', 'Second item Type 3', 'Second item Type 4'], $TestFields[3], $set1{subid}, 'typ') or print "ok\n" ; printlogf "Modify sub table"; print LOG "\n--------------------\n" ; $set1[0]{subid}[1]{typ} = '2.item, new Type 2' ; AddTestRowAndId (3, { 'id' => 2, 'typ' => '2.item, new Type 2', }, 'typ') ; DBIx::Recordset::Undef ('set1') ; *set1_ = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!Links' => { 'subid' => { '!Table' => $Table[3], '!LinkedField' => 'id', } }, 'id' => 2, }) or die "not ok ($DBI::errstr)" ; Check ([2], $TestFields[0], \@set1_) or print "ok\n" ; printlogf ""; Check (['Second item Type 1', '2.item, new Type 2', 'Second item Type 3', 'Second item Type 4'], $TestFields[3], $set1_{subid}, 'typ') or print "ok\n" ; DBIx::Recordset::Undef ('set1_') ; # --------------------- printlogf "Add with sub table"; print LOG "\n--------------------\n" ; *set1 = DBIx::Recordset -> Setup ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!Links' => { 'subid' => { '!Table' => $Table[3], '!LinkedField' => 'id' } }, }) or die "not ok ($DBI::errstr)" ; $set1 -> Add ; $set1{id} = 9988 ; $set1{value} = 998877 ; #$set1{subid}{id} = 9988; $set1{subid}{typ} = 'Typ for 9988' ; #${$set1{subid}} -> Flush ; AddTestRowAndId (0, { 'id' => 9988, 'value' => 9988772, }) ; AddTestRowAndId (3, { 'id' => 9988, 'typ' => 'Typ for 9988', }, 'typ') ; DBIx::Recordset::Undef ('set1') ; *set1_ = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!Links' => { 'subid' => { '!Table' => $Table[3], '!LinkedField' => 'id' } }, 'id' => 9988, }) or die "not ok ($DBI::errstr)" ; Check ([9988], $TestFields[0], \@set1_) or print "ok\n" ; printlogf ""; Check (['Typ for 9988'], $TestFields[3], $set1_{subid}, 'typ') or print "ok\n" ; DBIx::Recordset::Undef ('set1_') ; # --------------------- printlogf "Select sub table NULL"; print LOG "\n--------------------\n" ; *set2 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!Links' => { 'subv1' => { '!Table' => $Table[2], '!LinkedField' => 'value1' } }, 'id' => 13, }) or die "not ok ($DBI::errstr)" ; Check ([13], $TestFields[0], \@set2) or print "ok\n" ; printlogf ""; Check ([], $TestFields[0], $set2{subv1}) or print "ok\n" ; DBIx::Recordset::Undef ('set2') ; # --------------------- if ($SQLJoin) { printlogf "Select with linked name mode 1"; print LOG "\n--------------------\n" ; *set3 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!LinkName' => 1, '!Links' => { 'subid' => { '!Table' => $Table[1], '!LinkedField' => 'id', '!PrimKey' => 'id', '!NameField' => 'value2' } }, 'id' => "2\t5\t10", }) or die "not ok ($DBI::errstr)" ; Check ([2, 5, 10], [@{$TestFields[0]}, 'value2'], \@set3) or print "ok\n" ; DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Select with linked name hash access"; print LOG "\n--------------------\n" ; *set3 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!Links' => { 'subid' => { '!Table' => $Table[1], '!LinkedField' => 'id', '!PrimKey' => 'id', } }, '!PrimKey' => 'id', 'id' => 3, }) or die "not ok ($DBI::errstr)" ; if ($set3{subid}{id} != 3 || $set3{subid}{value2} != 3456) { printlog "ERROR in $lasttest\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Select with linked names mode 1"; print LOG "\n--------------------\n" ; *set3 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!LinkName' => 1, '!Links' => { 'subid' => { '!Table' => $Table[1], '!LinkedField' => 'id', '!PrimKey' => 'id', '!NameField' => ['name2', 'value2'] } }, 'id' => "2\t4\t7", }) or die "not ok ($DBI::errstr)" ; Check ([2, 4, 7], [@{$TestFields[0]}, 'name2', 'value2'], \@set3) or print "ok\n" ; DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Select with linked name mode 2"; print LOG "\n--------------------\n" ; *set3 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!LinkName' => 2, '!Links' => { 'subid' => { '!Table' => $Table[1], '!LinkedField' => 'id', '!PrimKey' => 'id', '!NameField' => 'value2' } }, 'id' => "4", }) or die "not ok ($DBI::errstr)" ; Check ([4], [@{$TestFields[0]}], \@set3) or print "ok\n" ; printlogf ""; if ($set3{ID} eq $TestCheck{4}{'value2'}) { print "ok\n" ; print LOG "ID = $set3{ID}\n" ; } else { printlog "ERROR in $lasttest\n" ; printlog "Field ID\n" ; printlog "Is = $set3{ID}\n" ; printlog "Should = $TestCheck{4}{'value2'}\n" ; $errors++ ; } DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Select with linked names mode 2"; print LOG "\n--------------------\n" ; *set3 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!LinkName' => 2, '!Links' => { 'subid' => { '!Table' => $Table[1], '!LinkedField' => 'id', '!PrimKey' => 'id', '!NameField' => ['name2', 'value2'] } }, 'id' => "6", }) or die "not ok ($DBI::errstr)" ; Check ([6], [@{$TestFields[0]}], \@set3) or print "ok\n" ; printlogf ""; my $re = "$TestCheck{6}{'name2'}\\s+$TestCheck{6}{'value2'}" ; if ($set3{ID} =~ /$re/) { print "ok\n" ; print LOG "ID = $set3{ID}\n" ; } else { printlog "ERROR in $lasttest\n" ; printlog "Field ID\n" ; printlog "Is = $set3{ID}\n" ; printlog "Should = $TestCheck{6}{'name2'} $TestCheck{6}{'value2'}\n" ; $errors++ ; } # --------------------- printlogf "Select with linked name mode 3"; print LOG "\n--------------------\n" ; *set3 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!LinkName' => 3, '!Links' => { 'subid' => { '!Table' => $Table[1], '!LinkedField' => 'id', '!PrimKey' => 'id', '!NameField' => 'value2' } }, 'id' => "4", }) or die "not ok ($DBI::errstr)" ; Check ([4], ['name', 'addon', 'value1'], \@set3) or print "ok\n" ; printlogf ""; if ($set3{id} eq $TestCheck{4}{'value2'}) { print "ok\n" ; print LOG "id = $set3{ID}\n" ; } else { printlog "ERROR in $lasttest\n" ; printlog "Field id\n" ; printlog "Is = $set3{id}\n" ; printlog "Should = $TestCheck{4}{'value2'}\n" ; $errors++ ; } DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Select with linked names mode 3"; print LOG "\n--------------------\n" ; *set3 = DBIx::Recordset -> Search ({ '!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!LinkName' => 3, '!Links' => { 'subid' => { '!Table' => $Table[1], '!LinkedField' => 'id', '!PrimKey' => 'id', '!NameField' => ['name2', 'value2'] } }, 'id' => "6", }) or die "not ok ($DBI::errstr)" ; Check ([6], ['name', 'addon', 'value1'], \@set3) or print "ok\n" ; printlogf ""; $re = "$TestCheck{6}{'name2'}\\s+$TestCheck{6}{'value2'}" ; if ($set3{id} =~ /$re/) { print "ok\n" ; print LOG "id = $set3{id}\n" ; } else { printlog "ERROR in $lasttest\n" ; printlog "Field id\n" ; printlog "Is = $set3{id}\n" ; printlog "Should = $TestCheck{6}{'name2'} $TestCheck{6}{'value2'}\n" ; $errors++ ; } DBIx::Recordset::Undef ('set3') ; } else { printlogf "Select with linked names"; print "skipped due to missing SQL-Join\n" ; } # --------------------- printlogf "Delete from hash"; print LOG "\n--------------------\n" ; tie %set15h, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!PrimKey' => 'id'} or die "not ok ($DBI::errstr)" ; delete $set15h{5} ; untie %set15h ; DelTestRowAndId (1, 5) ; *set3 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]"}) or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set3) or print "ok\n" ; DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Clear hash disabled"; print LOG "\n--------------------\n" ; tie %set15h, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => $Table[1], '!PrimKey' => 'id'} or die "not ok ($DBI::errstr)" ; eval { %set15h = () ; } ; if ($@) { print "ok\n" ; print LOG "disable CLEAR causes message = $@\n" ; } else { printlog "ERROR in $lasttest\n" ; printlog "Disable wmCLEAR does not work\n" ; $errors++ ; } untie %set15h ; printlogf ""; *set3 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]"}) or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set3) or print "ok\n" ; DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Clear hash"; print LOG "\n--------------------\n" ; tie %set15h, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", '!WriteMode' => (DBIx::Recordset::wmDELETE + DBIx::Recordset::wmCLEAR), '!PrimKey' => 'id'} or die "not ok ($DBI::errstr)" ; %set15h = () ; untie %set15h ; my @ids = @{$TestIds[1]} ; foreach my $id (@ids) { DelTestRowAndId (1, $id) ; } *set3 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]"}) or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set3) or print "ok\n" ; DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Assign hash"; print LOG "\n--------------------\n" ; tie %set15h, 'DBIx::Recordset::Hash', {'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]", '!WriteMode' => (DBIx::Recordset::wmALL), '!PrimKey' => 'id'} or die "not ok ($DBI::errstr)" ; my %assign = (61 => {id => 61, name => 'n61', value1 => 961, addon => 'ao61'}, 62 => {name => 'n62', value1 => 962, addon => 'ao62'}) ; my %a2 = %assign ; %set15h = %a2 ; untie %set15h ; $assign {62} -> {id} = 62 ; @ids = @{$TestIds[0]} ; foreach my $id (@ids) { DelTestRowAndId (0, $id) ; } foreach my $id (keys %assign) { AddTestRowAndId (0, $assign{$id}) ; } *set3 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[0]"}) or die "not ok ($DBI::errstr)" ; Check ($TestIds[0], $TestFields[0], \@set3) or print "ok\n" ; DBIx::Recordset::Undef ('set3') ; # --------------------- printlogf "Input Filter"; print LOG "\n--------------------\n" ; *set3 = DBIx::Recordset -> Insert ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'id' => '4455', 'name2' => '05.10.99', '!Filter' => { 'name2' => [ sub { shift =~ /(\d\d)\.(\d\d)\.(\d\d)/ ; "19$3$2$1"}, undef ] } }) or die "not ok ($DBI::errstr)" ; DBIx::Recordset::Undef ('set3') ; AddTestRowAndId (1, { id => '4455', name2 => '19991005'}) ; *set4 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'name2' => '05.10.99', '!Filter' => { 'name2' => [ sub { shift =~ /(\d\d)\.(\d\d)\.(\d\d)/ ; "19$3$2$1"}, ] } }) or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set4) or print "ok\n" ; DBIx::Recordset::Undef ('set4') ; # --------------------- printlogf "Output Filter"; print LOG "\n--------------------\n" ; AddTestRowAndId (1, { id => '4455', name2 => '05.10.99'}) ; *set5 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'name2' => '19991005', '!Filter' => { 'name2' => [ undef, sub { shift =~ /\d\d(\d\d)(\d\d)(\d\d)/ ; "$3.$2.$1"} ] } }) or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set5) or print "ok\n" ; # --------------------- printlogf "look for 4455"; print LOG "\n--------------------\n" ; $set5 -> Search ({id => 4455 }) or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set5) or print "ok\n" ; DBIx::Recordset::Undef ('set5') ; # --------------------- if ($Driver !~ /(?i:csv|sqlite)/) { printlogf "I/O Filter on type"; print LOG "\n--------------------\n" ; *set6 = DBIx::Recordset -> Search ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!Table' => "$Table[1]", 'name2' => '05.10.99', '!Filter' => { &DBI::SQL_CHAR() => [ sub { shift =~ /(\d\d)\.(\d\d)\.(\d\d)/ ; "19$3$2$1"}, sub { shift =~ /\d\d(\d\d)(\d\d)(\d\d)/ ; "$3.$2.$1"} ], &DBI::SQL_VARCHAR() => [ sub { shift =~ /(\d\d)\.(\d\d)\.(\d\d)/ ; "19$3$2$1"}, sub { shift =~ /\d\d(\d\d)(\d\d)(\d\d)/ ; "$3.$2.$1"} ], 1043 => [ sub { shift =~ /(\d\d)\.(\d\d)\.(\d\d)/ ; "19$3$2$1"}, sub { shift =~ /\d\d(\d\d)(\d\d)(\d\d)/ ; "$3.$2.$1"} ] } }) or die "not ok ($DBI::errstr)" ; Check ($TestIds[1], $TestFields[1], \@set6) or print "ok\n" ; DBIx::Recordset::Undef ('set6') ; } # --------------------- if ($Driver ne 'CSV') { printlogf "DBIx::Database setup"; print LOG "\n--------------------\n" ; my $db = DBIx::Database -> new ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!KeepOpen' => 1}) ; my $tab ; my $k ; my $v ; my $e = $errors ; my $n ; my $tables = $db -> AllTables ; foreach (keys %$tables) { print LOG "Found table: $_\n" ; } foreach (@Table) { if (!$tables -> {lc($_)} && !$tables -> {uc($_)} ) { printlog "ERROR in $lasttest: table $_ not found\n" ; $errors++ ; } my $l = $db -> TableLink ($_) ; if ($_ eq $Table[1] && (($n = keys (%$l)) != 1 || !$l -> {"-$Table[3]"})) { printlog "ERROR in $lasttest: table $_ does not contains the right link (#$n)\n" ; $errors++ ; } elsif ($_ eq $Table[3] && (($n = keys (%$l)) != 1 || !$l -> {"*$Table[1]"})) { printlog "ERROR in $lasttest: table $_ does not contains the right link (#$n)\n" ; $errors++ ; } elsif ($_ eq $Table[7] && (($n = keys (%$l)) != 2 || !$l -> {"-$Table[7]"} || !$l -> {"*$Table[7]"})) { printlog "ERROR in $lasttest: table $_ does not contains the right link (#$n)\n" ; $errors++ ; } elsif ($_ ne $Table[1] && $_ ne $Table[3] && $_ ne $Table[7] && keys (%$l) != 0) { printlog "ERROR in $lasttest: table $_ contains wrong link\n" ; $errors++ ; } } print "ok\n" if ($e == $errors) ; $db -> TableAttr ('*', '!PrimKey', 'id') ; # --------------------- if ($Driver !~ /(csv|sqlite)/i) { printlogf "DBIx::Database and I/O Filter"; print LOG "\n--------------------\n" ; } $db -> TableAttr ($Table[1], '!Filter', { DBI::SQL_CHAR => [ sub { shift =~ /(\d\d)\.(\d\d)\.(\d\d)/ ; "19$3$2$1"}, sub { shift =~ /\d\d(\d\d)(\d\d)(\d\d)/ ; "$3.$2.$1"} ], DBI::SQL_VARCHAR => [ sub { shift =~ /(\d\d)\.(\d\d)\.(\d\d)/ ; "19$3$2$1"}, sub { shift =~ /\d\d(\d\d)(\d\d)(\d\d)/ ; "$3.$2.$1"} ], 1043 => [ sub { shift =~ /(\d\d)\.(\d\d)\.(\d\d)/ ; "19$3$2$1"}, sub { shift =~ /\d\d(\d\d)(\d\d)(\d\d)/ ; "$3.$2.$1"} ] }) ; *set7 = DBIx::Recordset -> Search ({'!DataSource' => $db, '!Table' => $Table[1], 'name2' => '05.10.99', }) or die "not ok ($DBI::errstr)" ; if ($Driver !~ /(?i:csv|sqlite)/) { Check ($TestIds[1], $TestFields[1], \@set7) or print "ok\n" ; } # --------------------- printlogf "Attributes"; print LOG "\n--------------------\n" ; if ($set7 -> PrimKey ne 'id') { printlog "ERROR in $lasttest: PrimKey not set\n" ; $errors++ ; } else { print "ok\n" ; } printlogf ""; if ($set7 -> TableName ne $Table[1]) { printlog "ERROR in $lasttest: wrong TableName\n" ; $errors++ ; } else { print "ok\n" ; } DBIx::Recordset::Undef ('set7') ; $db -> MetaData ($Table[4], undef, 1) ; $db -> MetaData ($Table[5], undef, 1) ; # --------------------- printlogf "DBIx::Database !TableFilter"; print LOG "\n--------------------\n" ; my $db2 = DBIx::Database -> new ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password, '!KeepOpen' => 1, '!TableFilter' => 'dbix_'}) ; $e = $errors ; $tables = $db2 -> AllTables ; if (($n = keys (%$tables)) != 2) { printlog "ERROR in $lasttest: wrong number of table (#$n)\n" ; $errors++ ; } foreach (($Table[4], $Table[5])) { if (!$tables -> {$_}) { printlog "ERROR in $lasttest: table $_ not found\n" ; $errors++ ; } my $l = $db -> TableLink ($_) ; if ($_ eq $Table[4] && (($n = keys (%$l)) != 4 || !$l -> {"-up__rs5"} || !$l -> {"*up__rs5"} || !$l -> {"-a__rs6"} || !$l -> {"-b__rs6"})) { printlog "ERROR in $lasttest: table $_ does not contains the right link (#$n)\n" ; foreach my $link (keys %$l) { print LOG "Found link $link\n" ; } $errors++ ; } elsif ($_ eq $Table[5] && (($n = keys (%$l)) != 2 || !$l -> {"*a__rs5"}|| !$l -> {"*b__rs5"})) { printlog "ERROR in $lasttest: table $_ does not contains the right link (#$n)\n" ; foreach my $link (keys %$l) { print LOG "Found link $link\n" ; } $errors++ ; } elsif ($_ ne $Table[4] && $_ ne $Table[5]) { printlog "ERROR in $lasttest: table $_ contains wrong link\n" ; $errors++ ; } } print "ok\n" if ($e == $errors) ; if ($Driver eq 'mysql') { $e = $errors ; # --------------------- printlogf "DBIx::Recordset::DBseq"; print LOG "\n--------------------\n" ; my $seq = DBIx::Recordset::DBSeq -> new ($db2 -> DBHdl, $Table[6]) ; if ($seq -> NextVal('foo') != 1) { printlog "ERROR in $lasttest: sequence value != 1\n" ; $errors++ ; } elsif ($seq -> NextVal('foo') != 2) { printlog "ERROR in $lasttest: sequence value != 2\n" ; $errors++ ; } elsif ($seq -> NextVal('foo') != 3) { printlog "ERROR in $lasttest: sequence value != 3\n" ; $errors++ ; } if ($seq -> NextVal('bar') != 1) { printlog "ERROR in $lasttest: sequence value for bar != 1\n" ; $errors++ ; } if ($seq -> NextVal('foo') != 4) { printlog "ERROR in $lasttest: sequence value != 4\n" ; $errors++ ; } print "ok\n" if ($e == $errors) ; } $db = undef ; $db2 = undef ; } { my $e = $errors ; printlogf "DBIx::Recordset::FileSeq"; print LOG "\n--------------------\n" ; unlink ; my $seq = DBIx::Recordset::FileSeq -> new (undef ,'test') ; if ($seq -> NextVal('foo') != 1) { printlog "ERROR in $lasttest: sequence value != 1\n" ; $errors++ ; } elsif ($seq -> NextVal('foo') != 2) { printlog "ERROR in $lasttest: sequence value != 2\n" ; $errors++ ; } elsif ($seq -> NextVal('foo') != 3) { printlog "ERROR in $lasttest: sequence value != 3\n" ; $errors++ ; } if ($seq -> NextVal('bar') != 1) { printlog "ERROR in $lasttest: sequence value for bar != 1\n" ; $errors++ ; } if ($seq -> NextVal('foo') != 4) { printlog "ERROR in $lasttest: sequence value != 4\n" ; $errors++ ; } print "ok\n" if ($e == $errors) ; } { my $e = $errors ; printlogf "DBIx::Recordset::FileSeq 2"; print LOG "\n--------------------\n" ; my $seq = DBIx::Recordset::FileSeq -> new (undef ,'test') ; if ($seq -> NextVal('foo') != 5) { printlog "ERROR in $lasttest: sequence value != 5\n" ; $errors++ ; } elsif ($seq -> NextVal('foo') != 6) { printlog "ERROR in $lasttest: sequence value != 6\n" ; $errors++ ; } if ($seq -> NextVal('bar') != 2) { printlog "ERROR in $lasttest: sequence value for bar != 2\n" ; $errors++ ; } if ($seq -> NextVal('foo') != 7) { printlog "ERROR in $lasttest: sequence value != 7\n" ; $errors++ ; } print "ok\n" if ($e == $errors) ; } ######################################################################################### # cleanup if (!$nocleanup) { my $dbh = DBIx::Recordset -> SetupObject ({'!DataSource' => $dbh, '!Username' => $User, '!Password' => $Password }); DropTestTables($dbh, @Table); $dbh->Disconnect; } ######################################################################################### if ($errors) { print "\n$errors Errors detected for driver $Driver\n" ; } else { print "\nTests passed successfully for driver $Driver\n" ; } return $errors ; } ######################################################################################### unlink "test.log" ; unlink ; chmod 0777, 'test' ; open LOG, ">>test.log" or die "Cannot open test.log" ; *DBIx::Recordset::LOG = \*LOG ; $DBIx::Recordset::Debug = 5 ; open (STDERR, ">&LOG") || die "Cannot redirect stderr" ; #open (STDERR, ">dbi.log") || die "Cannot redirect stderr" ; #DBI->trace(2) ; select (STDERR) ; $| = 1 ; select (LOG) ; $| = 1 ; select (STDOUT) ; $| = 1 ; if ($#ARGV != -1) { eval { do $configfile ; } ; $Driver = $ARGV[0] ; $DSN = $ARGV[1] || $Drivers{$Driver}{dsn} ; $User = $ARGV[2] || $Drivers{$Driver}{user} ; $Password = $ARGV[3] || $Drivers{$Driver}{pass} ; $nocleanup = $ARGV[4] || 0 ; $> = $Drivers{$Driver}{uid} if (defined ($Drivers{$Driver}{uid})) ; $rc = DoTest ($Driver, $DSN, $User, $Password) ; $> = $< if ($Drivers{$Driver}{uid}) ; $fatal = 0 ; exit $rc ; } do $configfile ; @drivers = sort keys %Drivers ; foreach $drv (@drivers) { $> = $Drivers{$drv}{uid} if (defined ($Drivers{$drv}{uid})) ; $errcnt {$drv} = DoTest ($drv, $Drivers{$drv}{dsn}, $Drivers{$drv}{user}, $Drivers{$drv}{pass}) ; $> = $< if ($Drivers{$drv}{uid}) ; } $err = 0 ; print "\nSummary:\n" ; foreach $drv (@drivers) { if ($errcnt {$drv}) { print "$errcnt{$drv} Errors detected for $drv\n" ; } else { print "Tests for $drv passed successfully\n" ; } $err += $errcnt {$drv} ; } if ($err) { print "\n$err Errors detected at all\n" ; } else { print "\nAll tests passed successfully\n" ; } $fatal = 0 ; __END__ DBIx-Recordset-0.26/Makefile.PL0100644000000000000000000001214507763543511014646 0ustar rootroot# # Building Makefile for DBIx::Recordset # # (C) 1997-1998 G.Richter (richter@dev.ecos.de) / ECOS # # use ExtUtils::MakeMaker; # Ignore the following DBD Drivers %ignore = ('ExampleP' => 1, 'NullP' => 1, 'Sponge' => 1, 'Proxy' => 1, 'File' => 1) ; %datasource = ( 'Pg' => 'dbi:Pg:dbname=test', 'SQLite' => 'dbi:SQLite:test.db', 'Solid' => 'dbi:Solid:' ) ; $configfile = 'test/Config.pl' ; mkdir 'test', 0755 ; if (-e $configfile) { do $configfile ; } ## ---------------------------------------------------------------------------- sub MY::test_via_script { my ($txt) = shift -> MM::test_via_script (@_) ; $txt =~ s/\$\(TEST_FILE\)/\$(TEST_FILE) \$(TESTARGS)/g ; return $txt ; } ## ---------------------------------------------------------------------------- sub GetString { my ($prompt, $default) = @_ ; printf ("%s [%s]", $prompt, $default) ; chop ($_ = ) ; if (!/^\s*$/) {return $_ ;} else { if ($_ eq "") {return $default ;} else { return "" ; } } } ## ---------------------------------------------------------------------------- sub GetYesNo { my ($prompt, $default) = @_ ; my ($value) ; do { $value = lc (GetString ($prompt . "(y/n)", ($default?"y":"n"))) ; } until (($value cmp "j") == 0 || ($value cmp "y") == 0 || ($value cmp "n" ) == 0) ; return ($value cmp "n") != 0 ; } ## ---------------------------------------------------------------------------- print "\n" ; eval 'use DBI' ; if ($@ ne '') { print "\nPlease install DBI before installing DBIx::Recordset\n" ; exit (1) ; } print "Found DBI version $DBI::VERSION\n" ; $skip = 0 ; if (-e $configfile) { $skip = GetYesNo ("Use previous configuration for tests", 1) ; } if (!$skip) { @drvs = DBI::available_drivers () ; $driversinstalled = 0 ; foreach $drv (@drvs) { next if (exists ($ignore{$drv})) ; if (exists $Drivers{$drv}) { $drivers{$drv} = $Drivers{$drv} ; } else { $drivers{$drv}{dsn} = $datasource{$drv} || "dbi:$drv:test" ; } $driversinstalled = 1 ; } if ($driversinstalled == 0) { print "\nAt least one DBD driver must be installed before installing DBIx::Recordset\n" ; exit (1) ; } print "Found the following DBD drivers:\n" ; @drivers = sort keys %drivers ; $i = 1 ; foreach $drv (@drivers) { print "$i.) $drv\n" ; $i++ ; } print "\n" ; print "For running tests it's necessary to have an existing datasource for each\n" ; print "DBD driver against which DBIx::Recordset should be tested.\n" ; print ' For most drivers, DROP DATABASE TEST; CREATE DATABASE TEST is adequate. For SQLite, it is recommended to do the following: cd $DIST; cat > test.db to create an empty database. $DIST is the root directory of the Recordset distribution. '; print "Please enter a valid datasource (or accept the default) for each DBD driver\n" ; print "or enter a '.' if you do not want to test DBIx::Recordset against this driver\n" ; print "\n" ; $i = 1 ; foreach $drv (@drivers) { $dsn = GetString ("$i.) $drv", $drivers{$drv}{dsn}) ; if ($dsn eq '.') { delete $drivers{$drv} ; } else { $drivers{$drv}{dsn} = $dsn ; $user = GetString ("\tUsername", defined ($drivers{$drv}{user})?$drivers{$drv}{user}:"undef") ; if ($user ne 'undef') { $drivers{$drv}{user} = $user ; $pass = GetString ("\tPassword", defined ($drivers{$drv}{pass})?$drivers{$drv}{pass}:"undef") ; $drivers{$drv}{pass} = $pass if ($pass ne 'undef') ; } } $i++ ; } print "\n" ; print "The tests will performed with the following parameters\n" ; @drivers = sort keys %drivers ; foreach $drv (@drivers) { print "$drv \t-> $drivers{$drv}{dsn}\t" ; print "user: $drivers{$drv}{user}\t" if (defined ($drivers{$drv}{user})) ; print "password: $drivers{$drv}{pass}" if (defined ($drivers{$drv}{pass})) ; print "\n" ; } print "\n" ; print "In every of the above datasources the tables\n" ; print "\n" ; print " dbixrs1, dbixrs2, dbixrs3, dbixrs4\n" ; print "\n" ; print "will be created and dropped afterwards. Please make sure the datasources exist *before* you run make test. " ; open FH, ">$configfile" or die "Cannot open $configfile for writing ($!)" ; print FH "%Drivers = (\n" ; foreach $drv (@drivers) { print FH "\t$drv => {\n" ; print FH "\t\tdsn => \"$drivers{$drv}{dsn}\",\n" ; print FH "\t\tuser => \"$drivers{$drv}{user}\",\n" if (defined ($drivers{$drv}{user})) ; print FH "\t\tpass => \"$drivers{$drv}{pass}\",\n" if (defined ($drivers{$drv}{pass})); print FH "\t},\n" ; } print FH ");\n" ; close FH ; } WriteMakefile ( 'NAME' => 'DBIx::Recordset', 'VERSION_FROM' => 'Recordset.pm', # finds $VERSION 'dist' => { COMPRESS => 'gzip', SUFFIX => 'gz'}, 'clean' => { FILES => 'test.log' }, 'realclean' => { FILES => './test test.log' }, ); DBIx-Recordset-0.26/META.yml0100644000000000000000000000045710126462365014142 0ustar rootroot# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: DBIx-Recordset version: 0.26 version_from: Recordset.pm installdirs: site requires: distribution_type: module generated_by: ExtUtils::MakeMaker version 6.17 DBIx-Recordset-0.26/Compat.pm0100644000000000000000000005135107773276730014466 0ustar rootroot ################################################################################### # # DBIx::Compat - Copyright (c) 1997-1998 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. # For use with Apache httpd and mod_perl, see also Apache copyright. # # 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: Compat.pm,v 1.27 2001/07/10 05:06:51 richter Exp $ # ################################################################################### package DBIx::Compat ; use DBI ; sub SelectFields { my $hdl = shift ; my $table = shift ; my $sth = $hdl -> prepare ("select * from $table where 1=0") ; if (!$sth -> execute ()) { warn "select * from $table where 1=0 failed $DBI::errstr" ; return undef ; } return $sth ; } sub SelectFieldsQuoted { my $hdl = shift ; my $table = shift ; my $sth = $hdl -> prepare ("select * from \"$table\" where 1=0") ; if (!$sth -> execute ()) { warn "select * from \"$table\" where 1=0 failed $DBI::errstr" ; return undef ; } return $sth ; } sub SelectAllFields { my $hdl = shift ; my $table = shift ; my $sth = $hdl -> prepare ("select * from $table") ; if (!$sth -> execute ()) { warn "select * from $table failed $DBI::errstr" ; return undef ; } return $sth ; } sub ListFields { my $hdl = shift ; my $table = shift ; my $sth = $hdl -> prepare ("LISTFIELDS $table") ; $sth -> execute () or return undef ; # Meaning of TYPE has changed from 1.19_18 -> 1.19_19 $Compat{mSQL}{QuoteTypes} = { 1=>1, 12=>1, -1=>1 } if (exists ($sth -> {msql_type})) ; return $sth ; } sub ListFieldsFunc { my $hdl = shift ; my $table = shift ; my $sth = $hdl -> func($table, 'listfields' ) or return undef ; return $sth ; } sub ListTables { my $hdl = shift ; return $hdl -> tables ; } sub ListTablesODBC { my $hdl = shift ; return grep (!/^MSys/, $hdl -> tables) ; } sub ListTablesFunc { my $hdl = shift ; my @tabs ; eval { @tabs = $hdl -> tables } ; # try the _ListTables function for DBD::mysql before 1.21.. @tabs = $hdl -> func('_ListTables' ) if ($#tabs < 0 || $@) ; return @tabs ; } sub ListTablesPg { my $hdl = shift ; my @tabs ; my $st = $hdl -> tables ; while ($dat = $st -> fetch) { push @tabs, $dat -> [0] ; } return @tabs ; } sub ListTablesIfmx { my $hdl = shift ; my @tabs = $hdl -> func('_tables' ); return @tabs ; } sub LimitOffsetStrPg { my ($start,$max) = @_; return ($max > 0?"LIMIT $max":'') . ($start > 0?" OFFSET $start":'') ; } sub LimitOffsetStrMySQL { my ($start,$max) = @_; $start ||= 0 ; return ($max > 0)?"LIMIT $start,$max":'' ; } sub MysqlGetSerial { my ($dbh, $table) = @_ ; return $dbh -> {'mysql_insertid'} ; } sub SeqGetSerial { my ($dbh, $table, $seq) = @_ ; $seq ||= ($table . '_seq') ; my $sth = $dbh -> prepare ("select $seq.nextval from dual") ; $sth -> execute () or die "Cannot get serial from $seq ($DBI::errstr)" ; my $row = $sth -> fetchrow_arrayref ; return $row->[0] ; } sub PgGetSerial { my ($dbh, $table, $seq) = @_ ; $seq ||= ($table . '_seq') ; my $sth = $dbh -> prepare ("select nextval ('$seq')") ; $sth -> execute () or die "Cannot get serial from $seq ($DBI::errstr)" ; my $row = $sth -> fetchrow_arrayref ; return $row->[0] ; } sub InformixGetSerial { my ($dbh, $table) = @_ ; my $sth = $dbh -> prepare ("select distinct dbinfo('sqlca.sqlerrd1') from $table") ; $sth -> execute () or die "Cannot get serial from $seq ($DBI::errstr)" ; my $row = $sth -> fetchrow_arrayref ; return $row->[0] ; } #################################################################################### %Compat = ( '*' => { 'Placeholders' => 10, # Default: Placeholder are supported 'ListFields' => \&SelectFields, # Default: Use Select to get field names 'ListTables' => \&ListTables, # Default: Use DBI $dbh -> tables # QuoteTypes isn't used anymore !! 'QuoteTypes' => { 1=>1, 12=>1, -1=>1, 9 => 1, 10 => 1, 11 => 1}, # Default: ODBC Types, quote char, varchar and longvarchar 'NumericTypes' => { 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, -5 => 1, -6 => 1}, # Default numeric ODBC Types 'SupportJoin' => 1, # Default: Driver supports joins (select with multiple tables) 'SupportSQLJoin' => 1, # Default: Driver supports INNER/LEFT/RIGHT JOIN Syntax in SQL select 'SQLJoinOnly2Tabs' => 0, # Default: Driver supports LEFT/RIGHT JOIN with more then two tables 'HaveTypes' => 1, # Default: Driver supports $sth -> {TYPE} 'NullOperator' => 'IS', # Default: Operator to compare with NULL is IS 'HasInOperator' => 1, # Default: DBMS support x IN (y) 'NeedNullInCreate' => '', # Default: NULL allowed without explicit declare in CREATE 'EmptyIsNull' => 0, # Default: Empty strings ('') and NULL are different 'LimitOffset' => undef, # Default: Don't use LIMIT/OFFSET in SELECTs 'GetSerialPreInsert' => undef, # Default: Driver does not support serials 'GetSerialPostInsert' => undef, # Default: Driver does not support serials 'CreateTypes' => {}, # conversion for CreateTables 'CreateSeq' => 0, # Create sequence for counter 'CreatePublic' => 0, # Create public synonym for table 'CanDropColumn' => 1, # DBMS can drop a column 'QuoteIdentifier' => undef, # DBMS can handle idntifiers with spaces by quoteing them. Default: no }, 'SQLite' => { 'Placeholders' => 10, # Default: Placeholder are supported 'ListFields' => \&SelectFields, # Default: Use Select to get field names 'ListTables' => \&ListTables, # Default: Use DBI $dbh -> tables # QuoteTypes isn't used anymore !! 'QuoteTypes' => { 1=>1, 12=>1, -1=>1, 9 => 1, 10 => 1, 11 => 1}, # Default: ODBC Types, quote char, varchar and longvarchar 'NumericTypes' => { 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, -5 => 1, -6 => 1}, # Default numeric ODBC Types 'SupportJoin' => 1, # Default: Driver supports joins (select with multiple tables) 'SupportSQLJoin' => 4, # Default: Driver supports INNER/LEFT/RIGHT JOIN Syntax in SQL select 'SQLJoinOnly2Tabs' => 0, # Default: Driver supports LEFT/RIGHT JOIN with more then two tables 'HaveTypes' => 0, # Default: Driver supports $sth -> {TYPE} 'NullOperator' => 'IS', # Default: Operator to compare with NULL is IS 'HasInOperator' => 1, # Default: DBMS support x IN (y) 'NeedNullInCreate' => '', # Default: NULL allowed without explicit declare in CREATE 'EmptyIsNull' => 0, # Default: Empty strings ('') and NULL are different 'LimitOffset' => \&LimitOffsetStrPg, # Default: Don't use LIMIT/OFFSET in SELECTs 'GetSerialPreInsert' => undef, # Default: Driver does not support serials 'GetSerialPostInsert' => undef, # Default: Driver does not support serials 'CreateTypes' => # conversion for CreateTables { 'counter' => 'INTEGER PRIMARY KEY', }, 'CreateSeq' => 0, # Create sequence for counter 'CreatePublic' => 0, # Create public synonym for table 'CanDropColumn' => 0, # DBMS can drop a column 'QuoteIdentifier' => undef, # DBMS can handle idntifiers with spaces by quoteing them. Default: no }, 'ConfFile' => { 'Placeholders' => 2, # Placeholders supported, but the perl # type must be the same as the db type 'ListFields' => \&SelectFields, 'SupportJoin' => 0, 'HaveTypes' => 0 # Driver does not support $sth -> {TYPE} }, 'CSV' => { 'Placeholders' => 2, # Placeholders supported, but the perl # type must be the same as the db type 'ListFields' => \&SelectFields, 'SupportJoin' => 0, 'SupportSQLJoin' => 0, # Driver does not supports INNER/LEFT/RIGHt JOIN Syntax in SQL select 'HaveTypes' => 0, # Driver does not support $sth -> {TYPE} 'ListTables' => undef, # no tables 'EmptyIsNull' => 1, # DBD::CSV does not really knows about NULL 'HasInOperator' => 0, # DBD::CSV does not support x IN (y) }, 'XBase' => { # 'Placeholders' => 2, # Placeholders supported, but the perl # type must be the same as the db type 'ListFields' => \&SelectAllFields, 'SupportJoin' => 0, 'HaveTypes' => 0 # Driver does not support $sth -> {TYPE} }, 'Pg' => { 'Placeholders' => 2, # Placeholders supported, but the perl # type must be the same as the db type 'SupportSQLJoin' => 1, # Driver does not supports INNER/LEFT/RIGHt JOIN Syntax in SQL select 'NumericTypes' => { 20 => 1, 21 => 1, 22 => 1, 23 => 1, 700 => 1, 701 => 1, 1005 => 0, 1006 => 1, 1007 => 1, }, 'QuoteTypes' => { 16 => 1, 17=>1, 18=>1, 19=>1, 20=>1, 25=>1, 409=>1, 410=>1, 411=>1, 605=>1, 702 =>1, # abstime 703 =>1, # reltime 1002=>1, 1003=>1, 1004=>1, 1009=>1, 1026=>1, 1039=>1, 1040=>1, 1041=>1, 1042=>1, 1043=>1, 1082 =>1, # date 1083 =>1, # time 1184 =>1, # datetime 1186 =>1, # interval 1296 =>1 }, 'LimitOffset' => \&LimitOffsetStrPg, # Only PostgreSQL 6.5+ #### Use the following line for older DBD::Pg versions (< 0.89) which does # not support the table_info function # 'ListTables' => \&ListTablesPg, # DBD::Pg 'GetSerialPreInsert' => \&PgGetSerial, 'CreateTypes' => # conversion for CreateTables { 'counter' => 'serial', } }, 'mSQL' => { 'Placeholders' => 2, # Placeholders supported, but the perl # type must be the same as the db type 'SupportSQLJoin' => 0, # Driver does not supports INNER/LEFT/RIGHt JOIN Syntax in SQL select 'ListFields' => \&ListFields, # mSQL has it own ListFields function 'ListTables' => \&ListTablesFunc,# DBD::mysql $dbh -> func 'NullOperator' => '=', # Operator to compare with NULL is = 'QuoteTypes' => { 1=>1, 12=>1, -1=>1 } # ### use the following line for older mSQL drivers # 'QuoteTypes' => { 2=>1, 6=>1 } }, 'mysql' => { 'ListFields' => \&ListFields, # mysql has it own ListFields function 'QuoteTypes' => { 1=>1, 12=>1, -1=>1 , 9=>1}, 'Placeholders' => 10, # Placeholders supported, but the perl # type must be the same as the db type 'SQLJoinOnly2Tabs' => 0, # mysql supports LEFT/RIGHT JOIN with more than two tables 'ListTables' => \&ListTablesFunc, # DBD::mysql $dbh -> func 'LimitOffset' => \&LimitOffsetStrMySQL, 'GetSerialPostInsert' => \&MysqlGetSerial, 'CreateTypes' => # conversion for CreateTables { 'counter' => 'integer not null auto_increment', } }, 'Solid' => { 'Placeholders' => 3, # Placeholders supported, but cannot use a # string where a number expected 'QuoteTypes' => { 1=>1, 12=>1, -1=>1, 9=> 1 } }, 'ODBC' => { 'Placeholders' => 10, # Placeholders supported, but seems not # to works all the time ? 'QuoteTypes' => { 1=>1, 12=>1, -1=>1}, 'NeedNullInCreate' => 'NULL', 'ListTables' => \&ListTablesODBC, # Use DBI $dbh -> tables, exclude /^MSys/ 'SQLJoinOnly2Tabs' => 0, # Driver supports LEFT/RIGHT JOIN only with two tables 'CreateTypes' => # conversion for CreateTables { 'tinytext' => 'text', 'text' => 'longtext', }, }, 'Oracle' => { 'Placeholders' => 3, # Placeholders supported, but cannot use a # string where a number expected 'QuoteTypes' => { -4=>1, -3=>1, -1=>1, 1=>1, 9=>1, 11=>1, 12=>1, }, 'SupportSQLJoin' => 3, # Oracle need a = b (+) instead of INNER/LEFT/RIGHt JOIN Syntax in SQL select 'EmptyIsNull' => 1, # Oracle converts empty strings ('') to NULL # older DBD::Orcales only need the following one entry, but some test may fail # 'HaveTypes' => 0 # Driver does not supports $sth -> {TYPE} 'GetSerialPreInsert' => \&SeqGetSerial, 'CreateTypes' => # conversion for CreateTables { 'counter' => 'integer', 'tinytext' => 'varchar2(256)', 'text' => 'varchar2(2000)', 'datetime' => 'date', 'bool' => 'number(1)', 'bit' => 'number(1)', }, 'CreateSeq' => 1, # Create sequence for counter 'CreatePublic' => 1, # Create public synonym for table 'CanDropColumn' => 0, # DBMS can drop a column 'QuoteIdentifier' => '""', # DBMS can handle idntifiers with spaces by quoteing them. ## 'ListFields' => \&SelectFieldsQuoted, # Use Select to get field names }, 'Sybase' => { 'Placeholders' => 10, 'ListFields' => \&SelectFields, 'QuoteTypes' => { -6=>0, -4=>0, -2=>0, -1=>1, 1=>1, 2=>0, 3=>1, 4=>0, 6=>0, 7=>0, 9=>0 }, 'SupportSQLJoin' => 2, # Driver need *= instead of INNER/LEFT/RIGHt JOIN Syntax in SQL select 'HaveTypes' => 1, 'NullOperator' => 'IS', 'NeedNullInCreate' => 'NULL' }, 'Informix' => { 'Placeholders' => 2, 'SupportSQLJoin' => 4, 'SQLJoinOnly2Tabs' => 0, 'ListTables' => \&ListTablesIfmx, 'GetSerialPostInsert' => \&InformixGetSerial, 'QuoteIdentifier' => $ENV{DELIMIDENT}?'""':undef, # DBMS can handle idntifiers with spaces by quoteing them. }, 'Sprite' => { 'Placeholders' => 2, # Placeholders supported, but perl type must be the same as type in db # string where a number expected 'QuoteTypes' => { -4=>1, -3=>1, -1=>1, 1=>1, 9=>1, 11=>1, 12=>1, }, 'SupportJoin' => 0, # NO JOINS (YET) IN SPRITE! 'SupportSQLJoin' => 0, # Oracle need a = b (+) instead of INNER/LEFT/RIGHt JOIN Syntax in SQL select 'EmptyIsNull' => 1, # Sprite converts empty strings ('') to NULL 'HaveTypes' => 1, # Driver does supports $sth -> {TYPE} 'GetSerialPreInsert' => \&SeqGetSerial, 'HasInOperator' => 0, # DBMS not support x IN (y) }, ) ; ########################################################################################################### sub GetItem { my ($driver, $name) = @_ ; return $Compat{$driver}{$name} if (exists ($Compat{$driver}{$name})) ; return $Compat{'*'}{$name} ; } 1 ; =head1 NAME DBIx::Compat - Perl extension for Compatibility Infos about DBD Drivers =head1 SYNOPSIS use DBIx::Compat; my $HaveTypes = DBIx::Compat::GetItem ($drv, 'HaveTypes') ; =head1 DESCRIPTION DBIx::Compat contains a hash which gives information about DBD drivers, to allow to write driver independent programs. Currently there are the following attributes defined: =head2 B A function which will return information about all fields of an table. Needs an database handle and a tablename as argument. Must at least return the fieldnames and the fieldtypes. Example: $ListFields = $DBIx::Compat::Compat{$Driver}{ListFields} ; $sth = &{$ListFields}($DBHandle, $Table) or die "Cannot list fields" ; @{ $sth -> {NAME} } ; # array of fieldnames @{ $sth -> {TYPE} } ; # array of filedtypes $sth -> finish ; =head2 B A function which will return an array of all tables of the datasource. Defaults to C<$dbh> -> C. =head2 B Hash which contains one entry for all datatypes that are numeric. =head2 B Set to true if the DBMS supports joins (select with multiple tables) =head2 B Set to 1 if the DBMS supports INNER/LEFT/RIGHT JOIN Syntax in SQL select. Set to 2 if DBMS needs a *= b syntax for inner join (MS-SQL, Sybase). Set to 3 if DBMS needs a = b (+) syntax for inner join (Oracle syntax). =head2 B Set to true if DBMS can only support two tables in inner joins. =head2 B Set to true if DBMS supports datatypes (most DBMS will do) =head2 B Set to C<'NULL'> if DBMS requires the NULL keyword when creating tables where fields should contains nulls. =head2 B Set to true if an empty string ('') and NULL is the same for the DBMS. =head2 B An function which will be used to create a SQL text for limiting the number of fetched rows and selecting the starting row in selects. =head1 B =head2 B Gives information if and how placeholders are supported: =over 4 =item B<0> = Not supported =item B<1> = Supported, but not fully, unknown how much =item B<2> = Supported, but perl type must be the same as type in db =item B<3> = Supported, but can not give a string when a numeric type is in the db =item B<10> = Supported under all circumstances =back =head2 B Gives information which datatypes must be quoted when passed literal (not via a placeholder). Contains a hash with all type number which need to be quoted. $DBIx::Compat::Compat{$Driver}{QuoteTypes}{$Type} will be true when the type in $Type for the driver $Driver must be quoted. =head1 Supported Drivers Currently there are entry for =item B =item B =item B =item B =item B =item B =item B =item B =item B if you detect an error in the definition or add an definition for a new DBD driver, please mail it to the author. =head1 AUTHOR G.Richter =head1 SEE ALSO perl(1), DBI(3), DBIx::Recordset(3) =cut DBIx-Recordset-0.26/Recordset.pm0100755000000000000000000054676010126462005015167 0ustar rootroot ################################################################################### # # DBIx::Recordset - 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 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: Recordset.pm,v 1.106 2002/10/15 14:11:19 richter Exp $ # ################################################################################### package DBIx::Recordset ; use strict 'vars' ; use Carp ; use Data::Dumper; use DBIx::Database ; use DBIx::Compat ; use Text::ParseWords ; use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK $self @self %self $newself $Debug $fld @fld %Compat $id $numOpen %Data %Metadata %unaryoperators $LastErr $LastErrstr $PreserveCase $FetchsizeWarn ); use DBI ; require Exporter; @ISA = qw(Exporter DBIx::Database::Base); $VERSION = '0.26'; $PreserveCase = 0 ; $FetchsizeWarn = 2 ; $id = 1 ; $numOpen = 0 ; $Debug = 0 ; # Disable debugging output # Write Modes use constant wmNONE => 0 ; use constant wmINSERT => 1 ; use constant wmUPDATE => 2 ; use constant wmDELETE => 4 ; use constant wmCLEAR => 8 ; use constant wmALL => 15 ; # required Filters use constant rqINSERT => 1 ; use constant rqUPDATE => 2 ; # OnDelete actions use constant odDELETE => 1 ; use constant odCLEAR => 2 ; %unaryoperators = ( 'is null' => 1, 'is not null' => 1 ) ; # Get filehandle of logfile if (defined ($INC{'Embperl.pm'})) { tie *LOG, 'Embperl::Log' ; } elsif (defined ($INC{'HTML/Embperl.pm'})) { tie *LOG, 'HTML::Embperl::Log' ; } else { *LOG = \*STDOUT ; } ## ---------------------------------------------------------------------------- ## ## SetupDBConnection ## ## $data_source = Driver/DB/Host ## or recordset from which the data_source and dbhdl should be taken (optional) ## $table = table (multiple tables must be comma separated) ## $username = Username (optional) ## $password = Password (optional) ## \%attr = Attributes (optional) ## sub SetupDBConnection($$$;$$\%) { my ($self, $data_source, $table, $username, $password, $attr, $autolink) = @_ ; if ($table =~ /^\"/) { $self->{'*Table'} = $table ; } else { $self->{'*Table'} = $PreserveCase?$table:lc ($table) ; } $self->{'*MainTable'} = $PreserveCase?$table:lc ($table) ; $self->{'*Id'} = $id++ ; if (!($data_source =~ /^dbi\:/i)) { my $metakey = "-DATABASE//$data_source" ; $data_source = $DBIx::Recordset::Metadata{$metakey} if (exists $DBIx::Recordset::Metadata{$metakey}) ; } if (ref ($data_source) eq 'DBIx::Recordset') { # copy from another recordset $self->{'*Driver'} = $data_source->{'*Driver'} ; $self->{'*DataSource'} = $data_source->{'*DataSource'} ; $self->{'*Username'} = $data_source->{'*Username'} ; $self->{'*DBHdl'} = $data_source->{'*DBHdl'} ; $self->{'*DBIAttr'} = $data_source->{'*DBIAttr'} ; $self->{'*MainHdl'} = 0 ; $self->{'*TableFilter'}= $data_source->{'*TableFilter'} ; $self->{'*Query'} = $data_source->{'*Query'} ; } elsif (ref ($data_source) eq 'DBIx::Database') { # copy from database object $self->{'*DataSource'} = $data_source->{'*DataSource'} ; $self->{'*Username'} = $data_source->{'*Username'} ; $self->{'*DBIAttr'} = $data_source->{'*DBIAttr'} ; $self->{'*TableFilter'}= $data_source->{'*TableFilter'} ; $self->{'*DBHdl'} = $data_source->{'*DBHdl'} ; $self->{'*Driver'} = $data_source->{'*Driver'} ; $self->{'*DoOnConnect'} = $data_source->{'*DoOnConnect'} ; } elsif (ref ($data_source) and eval { $data_source->isa('DBI::db') } ) { # copy from database handle $self->{'*Driver'} = $data_source->{'Driver'}->{'Name'} ; $self->{'*DataSource'} = $data_source->{'Name'} ; # DBI does not save user name $self->{'*Username'} = undef ; $self->{'*DBHdl'} = $data_source ; # XXX no idea how to fetch attr hash other than handle itself $self->{'*DBIAttr'} = {} ; $self->{'*MainHdl'} = 0 ; } else { $self->{'*DataSource'} = $data_source ; $self->{'*Username'} = $username ; $self->{'*DBIAttr'} = $attr ; $self->{'*DBHdl'} = undef ; } my $hdl ; if (!defined ($self->{'*DBHdl'})) { $hdl = $self->{'*DBHdl'} = DBI->connect($self->{'*DataSource'}, $self->{'*Username'}, $password, $self->{'*DBIAttr'}) or return undef ; $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 LOG "DB: Found DBD::Proxy, take compability entrys for driver $self->{'*Driver'}\n" if ($self->{'*Debug'} > 1) ; } $numOpen++ ; print LOG "DB: Successfull connect to $self->{'*DataSource'} (id=$self->{'*Id'}, numOpen = $numOpen)\n" if ($self->{'*Debug'} > 1) ; my $cmd ; if ($hdl && ($cmd = $self -> {'*DoOnConnect'})) { $self -> DoOnConnect ($cmd) ; } } else { $LastErr = $self->{'*LastErr'} = undef ; $LastErrstr = $self->{'*LastErrstr'} = undef ; $hdl = $self->{'*DBHdl'} ; print LOG "DB: Use already open dbh for $self->{'*DataSource'} (id=$self->{'*Id'}, numOpen = $numOpen)\n" if ($self->{'*Debug'} > 1) ; } my $meta = $self -> QueryMetaData ($self->{'*Table'}) ; my $metakey = "$self->{'*DataSource'}//" . $self->{'*Table'} ; $self->{'*NullOperator'} = DBIx::Compat::GetItem ($self->{'*Driver'}, 'NullOperator') ; $self->{'*HasInOperator'} = DBIx::Compat::GetItem ($self->{'*Driver'}, 'HasInOperator') ; $meta or $self -> savecroak ("No meta data available for $self->{'*Table'}") ; $self->{'*Table4Field'} = $meta->{'*Table4Field'} ; $self->{'*Type4Field'} = $meta->{'*Type4Field'} ; #$self->{'*MainFields'} = $meta->{'*MainFields'} ; $self->{'*FullNames'}= $meta->{'*FullNames'} ; $self->{'*Names'} = $meta->{'*Names'} ; $self->{'*Types'} = $meta->{'*Types'} ; $self->{'*Quote'} = $meta->{'*Quote'} ; $self->{'*Numeric'} = $meta->{'*Numeric'} ; $self->{'*NumericTypes'} = $meta->{'*NumericTypes'} ; $self->{'*Links'} = $meta->{'*Links'} ; $self->{'*PrimKey'} = $meta->{'!PrimKey'} ; return $hdl ; } ## ---------------------------------------------------------------------------- ## ## TIEARRAY ## ## tie an array to the object, object must be aready blessed ## ## tie @self, 'DBIx::Recordset', $self ; ## sub TIEARRAY { my ($class, $arg) = @_ ; my $rs ; if (ref ($arg) eq 'HASH') { $rs = DBIx::Recordset -> SetupObject ($arg) or return undef ; } elsif (ref ($arg) eq 'DBIx::Recordset') { $rs = $arg ; } else { croak ("Need DBIx::Recordset or setup parameter") ; } return $rs ; } sub STORESIZE { my ($self, $size) = @_ ; $self -> ReleaseRecords if ($size == 0) ; } ## ---------------------------------------------------------------------------- ## ## New ## ## creates an new recordset object and ties an array and an hash to it ## ## returns a typeglob which contains: ## scalar: ref to new object ## array: array tied to object ## hash: hash tied to object ## ## $data_source = Driver/DB/Host ## $table = table (multiple tables must be comma separated) ## $username = Username (optional) ## $password = Password (optional) ## \%attr = Attributes (optional) ## sub New { my ($class, $data_source, $table, $username, $password, $attr) = @_ ; my $self = {'*Debug' => $Debug} ; bless ($self, $class) ; my $rc = $self->SetupDBConnection ($data_source, $table, $username, $password, $attr) ; $self->{'*Placeholders'}= $DBIx::Compat::Compat{$self->{'*Driver'}}{Placeholders} ; $self->{'*Placeholders'}= $DBIx::Compat::Compat{'*'}{Placeholders} if (!defined ($self->{'*Placeholders'})) ; $self->{'*Placeholders'}= 0 if ($self->{'*Placeholders'} < 10) ; # only full support for placeholders works if ($self->{'*Debug'} > 0) { print LOG "DB: ERROR open DB $data_source ($DBI::errstr)\n" if (!defined ($rc)) ; my $n = '' ; $n = ' NOT' if (!$self->{'*Placeholders'}) ; print LOG "DB: New Recordset driver=$self->{'*Driver'} placeholders$n supported\n" if ($self->{'*Debug'} > 2) } return defined($rc)?$self:undef ; } ## ---------------------------------------------------------------------------- ## ## SetupMemberVar ## ## setup a member config variable checking ## 1.) given parameter ## 2.) TableAttr ## 3.) default ## sub SetupMemberVar { my ($self, $name, $param, $default) = @_ ; my $pn = "!$name" ; my $sn = "\*$name" ; my $attr ; if (exists $param -> {$pn}) { $self -> {$sn} = $param -> {$pn} ; } elsif (defined ($attr = $self -> TableAttr ($pn))) { $self -> {$sn} = $attr ; } else { $self -> {$sn} ||= $default ; } print LOG "DB: Setup: $pn = " . (defined ($self->{$sn})?$self->{$sn}:'') . "\n" if ($self -> {'*Debug'} > 2) ; } ## ---------------------------------------------------------------------------- ## ## Setup ## ## creates an new recordset object and ties an array and an hash to it ## ## Same as New, but parameters passed as hash: ## ## !DataSource = Driver/DB/Host ## or a Recordset object from which to take the DataSource, DBIAttrs and username ## !Username = username ## !Password = password ## !DBIAttr = reference to a hash which is passed to the DBI connect method ## ## !Table = Tablename, muliply tables are comma separated ## !Fields = fields which should be return by a query ## !Order = order for any query ## !TabRelation = condition which describes the relation ## between the given tables ## !TabJoin = JOIN to use in table part of select statement ## !PrimKey = name of primary key ## !StoreAll = store all fetched data ## !LinkName = query !NameField field(s) instead of !MainField for links ## 0 = off ## 1 = select additional fields ## 2 = build name in uppercase of !MainField ## 3 = replace !MainField with content of !NameField ## ## !Default = hash with default record data ## !IgnoreEmpty = 1 ignore undef values, 2 ignore empty strings ## ## !WriteMode = 1 => allow insert (wmINSERT) ## 2 => allow update (wmUPDATE) ## 4 => allow delete (wmDELETE) ## 8 => allow delete all (wmCLEAR) ## default = 7 ## !TableFilter = prefix which tables should be used ## sub SetupObject { my ($class, $parm) = @_ ; my $self = New ($class, $$parm{'!DataSource'}, $$parm{'!Table'}, $$parm{'!Username'}, $$parm{'!Password'}, $$parm{'!DBIAttr'}) or return undef ; HTML::Embperl::RegisterCleanup (sub { $self -> Disconnect }) if (defined (&HTML::Embperl::RegisterCleanup)) ; $self -> SetupMemberVar ('Debug', $parm, $Debug) ; $self -> SetupMemberVar ('Fields', $parm) ; $self -> SetupMemberVar ('TabRelation', $parm) ; $self -> SetupMemberVar ('TabJoin', $parm) ; $self -> SetupMemberVar ('PrimKey', $parm) ; $self -> SetupMemberVar ('Serial', $parm) ; $self -> SetupMemberVar ('Sequence', $parm) ; $self -> SetupMemberVar ('SeqClass', $parm) ; $self -> SetupMemberVar ('StoreAll', $parm) ; $self -> SetupMemberVar ('Default', $parm) ; $self -> SetupMemberVar ('IgnoreEmpty', $parm, 0) ; $self -> SetupMemberVar ('WriteMode', $parm, 7) ; $self -> SetupMemberVar ('TieRow', $parm, 1) ; $self -> SetupMemberVar ('LongNames', $parm, 0) ; $self -> SetupMemberVar ('KeepFirst', $parm, 0) ; $self -> SetupMemberVar ('LinkName', $parm, 0) ; $self -> SetupMemberVar ('NameField', $parm) ; $self -> SetupMemberVar ('Order', $parm) ; $self -> SetupMemberVar ('TableFilter', $parm) ; $self -> SetupMemberVar ('DoOnConnect', $parm) ; $self -> SetupMemberVar ('Query', $parm) ; if ($self -> {'*Serial'}) { $self->{'*PrimKey'} = $self -> {'*Serial'} if (!$parm->{'!PrimKey'}) ; $self->{'*Sequence'} ||= "$self->{'*Table'}_seq" ; if ($self->{'*SeqClass'}) { my @seqparm = split (/\s*,\s*/, $self->{'*SeqClass'}) ; my $class = shift @seqparm ; if (!defined (&{"$class\:\:new"})) { my $fn = $class ; $fn =~ s/::/\//g ; $fn .= '.pm' ; require $fn ; } $self->{'*SeqObj'} = $class -> new ($self -> {'*DBHdl'}, @seqparm) ; } else { $self->{'*GetSerialPreInsert'} = DBIx::Compat::GetItem ($self -> {'*Driver'}, 'GetSerialPreInsert') ; $self->{'*GetSerialPostInsert'} = DBIx::Compat::GetItem ($self -> {'*Driver'}, 'GetSerialPostInsert') ; } } $Data{$self->{'*Id'}} = [] ; $self->{'*FetchStart'} = 0 ; $self->{'*LastSerial'} = undef ; $self->{'*FetchMax'} = undef ; $self->{'*EOD'} = undef ; $self->{'*CurrRow'} = 0 ; $self->{'*Stats'} = {} ; $self->{'*CurrRecStack'} = [] ; $self->{'*LinkSet'} = {} ; $LastErr = $self->{'*LastErr'} = undef ; $LastErrstr = $self->{'*LastErrstr'} = undef ; my $ofunc = $self->{'*OutputFunctions'} = {} ; my $ifunc = $self->{'*InputFunctions'} = {} ; my $irfunc_insert = $self->{'*InputFunctionsRequiredOnInsert'} = [] ; my $irfunc_update = $self->{'*InputFunctionsRequiredOnUpdate'} = [] ; my $names = $self->{'*Names'} ; my $types = $self->{'*Types'} ; my $key ; my $value ; my $conversion ; my $dbg = ($self -> {'*Debug'} > 2) ; foreach $conversion (($self -> TableAttr ('!Filter'), $$parm{'!Filter'})) { if ($conversion) { foreach $key (sort keys %$conversion) { $value = $conversion -> {$key} ; if ($key =~ /^-?\d*$/) { # numeric -> SQL_TYPE my $i = 0 ; my $name ; foreach (@$types) { if ($_ == $key) { $name = $names -> [$i] ; if ($value -> [0] || $ifunc -> {$name}) { local $^W = 0 ; $ifunc -> {$name} = $value -> [0] ; print LOG "DB: Apply input Filter to $name (type=$_)\n" if ($dbg) ; push @$irfunc_insert, $name if ($value -> [2] & rqINSERT) ; print LOG "DB: Apply required INSERT Filter to $name (type=$_)\n" if ($dbg && $value -> [2] & rqINSERT) ; push @$irfunc_update, $name if ($value -> [2] & rqUPDATE) ; print LOG "DB: Apply required UPDATE Filter to $name (type=$_)\n" if ($dbg && $value -> [2] & rqUPDATE) ; } $ofunc -> {$name} = $value -> [1] if ($value -> [1] || $ofunc -> {$name}) ; print LOG "DB: Apply output Filter to $name (type=$_)\n" if ($dbg && ($value -> [1] || $ofunc -> {$name})) ; } $i++ ; } } else { if ($value -> [0] || $ifunc -> {$key}) { local $^W = 0 ; $ifunc -> {$key} = $value -> [0] ; print LOG "DB: Apply input Filter to $key\n" if ($dbg) ; push @$irfunc_insert, $key if ($value -> [2] & rqINSERT) ; print LOG "DB: Apply required INSERT Filter to $key\n" if ($dbg && $value -> [2] & rqINSERT) ; push @$irfunc_update, $key if ($value -> [2] & rqUPDATE) ; print LOG "DB: Apply required UPDATE Filter to $key\n" if ($dbg && $value -> [2] & rqUPDATE) ; } $ofunc -> {$key} = $value -> [1] if ($value -> [1] || $ofunc -> {$key}) ; print LOG "DB: Apply output Filter to $key\n" if ($dbg && ($value -> [1] || $ofunc -> {$key})) ; } } } } delete $self->{'*OutputFunctions'} if (keys (%$ofunc) == 0) ; delete $self->{'*InputFunctionsRequiredOnInsert'} if ($#$irfunc_insert == -1) ; delete $self->{'*InputFunctionsRequiredOnUpdate'} if ($#$irfunc_update == -1) ; my $links = $$parm{'!Links'} ; if (defined ($links)) { my $k ; my $v ; while (($k, $v) = each (%$links)) { $v -> {'!LinkedField'} = $v -> {'!MainField'} if (defined ($v) && !defined ($v -> {'!LinkedField'})) ; $v -> {'!MainField'} = $v -> {'!LinkedField'} if (defined ($v) && !defined ($v -> {'!MainField'})) ; } $self->{'*Links'} = $links ; } if ($self->{'*LinkName'}) { ($self->{'*Fields'}, $self->{'*Table'}, $self->{'*TabJoin'}, $self->{'*TabRelation'}, $self->{'*ReplaceFields'}) = $self -> BuildFields ($self->{'*Fields'}, $self->{'*Table'}, $self->{'*TabRelation'}) ; } return $self ; } sub Setup { my ($class, $parm) = @_ ; local *self ; $self = SetupObject ($class, $parm) or return undef ; tie @self, $class, $self ; if ($parm -> {'!HashAsRowKey'}) { tie %self, "$class\:\:Hash", $self ; } else { tie %self, "$class\:\:CurrRow", $self ; } return *self ; } ## ---------------------------------------------------------------------------- ## ## ReleaseRecords ... ## ## Release all records, write data if necessary ## sub ReleaseRecords { $_[0] -> {'*LastKey'} = undef ; $_[0] -> Flush (1) ; #delete $Data{$_[0] -> {'*Id'}} ; $Data{$_[0] -> {'*Id'}} = [] ; } ## ---------------------------------------------------------------------------- ## ## undef and untie the object ## sub Undef { my ($objname) = @_ ; if (!($objname =~ /\:\:/)) { my ($c) = caller () ; $objname = "$c\:\:$objname" ; } print LOG "DB: Undef $objname\n" if (defined (${$objname}) && (${$objname}->{'*Debug'} > 1 || $Debug > 1)) ; if (defined (${$objname}) && ref (${$objname}) && UNIVERSAL::isa (${$objname}, 'DBIx::Recordset')) { # Cleanup rows and write them if necessary ${$objname} -> ReleaseRecords () ; ${$objname} -> Disconnect () ; } if (defined (%{$objname})) { my $obj = tied (%{$objname}) ; $obj -> {'*Recordset'} = undef if ($obj) ; $obj = undef ; } #${$objname} = undef ; untie %{$objname} ; undef ${$objname} if (defined (${$objname}) && ref (${$objname})) ; untie @{$objname} ; } ## ---------------------------------------------------------------------------- ## ## disconnect from database ## sub Disconnect ($) { my ($self) = @_ ; if (defined ($self->{'*StHdl'})) { $self->{'*StHdl'} -> finish () ; print LOG "DB: Call DBI finish (id=$self->{'*Id'}, Last = $self->{'*LastSQLStatement'})\n" if ($self->{'*Debug'} > 3) ; undef $self->{'*StHdl'} ; } $self -> ReleaseRecords () ; if (defined ($self->{'*DBHdl'}) && $self->{'*MainHdl'}) { $numOpen-- ; print LOG "DB: Call DBI disconnect (id=$self->{'*Id'}, numOpen = $numOpen)\n" if ($self->{'*Debug'} > 3) ; $self->{'*DBHdl'} -> disconnect () ; undef $self->{'*DBHdl'} ; } print LOG "DB: Disconnect (id=$self->{'*Id'}, numOpen = $numOpen)\n" if ($self->{'*Debug'} > 1) ; } ## ---------------------------------------------------------------------------- ## ## do some cleanup ## sub DESTROY ($) { my ($self) = @_ ; my $orgerr = $@ ; local $@ ; eval { $self -> Disconnect () ; delete $Data{$self -> {'*Id'}} ; { local $^W = 0 ; print LOG "DB: DESTROY (id=$self->{'*Id'}, numOpen = $numOpen)\n" if ($self->{'*Debug'} > 2) ; } } ; $self -> savecroak ($@) if (!$orgerr && $@) ; warn $@ if ($orgerr && $@) ; } ## ---------------------------------------------------------------------------- ## ## begin transaction ## sub Begin { my ($self) = @_ ; # 'begin' method is unhandled by DBI ## ?? $self->{'*DBHdl'} -> func('begin') unless $self->{'*DBHdl'}->{'AutoCommit'}; } ## ---------------------------------------------------------------------------- ## ## commit transaction ## sub Commit { my ($self) = @_ ; $self -> Flush ; $self->{'*DBHdl'} -> commit unless $self->{'*DBHdl'}->{'AutoCommit'} ; } ## ---------------------------------------------------------------------------- ## ## rollback transaction ## sub Rollback { my ($self) = @_ ; $self -> ReleaseRecords ; $self->{'*DBHdl'} -> rollback unless $self->{'*DBHdl'}->{'AutoCommit'} ; } ## ---------------------------------------------------------------------------- ## ## store something in the array ## sub STORE { my ($self, $fetch, $value) = @_ ; $fetch += $self->{'*FetchStart'} ; #$max = $self->{'*FetchMax'} ; print LOG "DB: STORE \[$fetch\] = " . (defined ($value)?$value:'') . "\n" if ($self->{'*Debug'} > 3) ; if ($self->{'*Debug'} > 2 && ref ($value) eq 'HASH') { my $k ; my $v ; while (($k, $v) = each (%$value)) { print LOG "<$k>=<$v> " ; } print LOG "\n" ; } my $r ; my $rec ; $value ||= {} ; if (keys %$value) { my %rowdata ; $r = tie %rowdata, 'DBIx::Recordset::Row', $self ; %rowdata = %$value ; $rec = $Data{$self->{'*Id'}}[$fetch] = \%rowdata ; } else { local $^W = 0 ; $r = tie %$value, 'DBIx::Recordset::Row', $self, $value ; $rec = $Data{$self->{'*Id'}}[$fetch] = $value ; my $dirty = $r->{'*dirty'} ; # preserve dirty state %$value = %{$self -> {'*Default'}} if (exists ($self -> {'*Default'})) ; $r->{'*dirty'} = $dirty } $r -> {'*new'} = 1 ; #$self->{'*LastRow'} = $fetch ; #$self->{'*LastKey'} = $r -> FETCH ($self -> {'*PrimKey'}) ; return $rec ; } ## ---------------------------------------------------------------------------- ## ## Add ## ## Add a new record ## sub Add { my ($self, $data) = @_ ; my $num = $#{$Data{$self->{'*Id'}}} + 1 ; $self -> STORE ($num, $data) if ($data) ; $self -> {'*CurrRow'} = $num + 1 ; $self -> {'*LastRow'} = $num ; return $num ; } ## ---------------------------------------------------------------------------- ## ## StHdl ## ## return DBI statement handle of last select ## sub StHdl ($) { return $_[0] -> {'*StHdl'} ; } ## ---------------------------------------------------------------------------- ## ## TableName ## ## return name of table ## sub TableName ($) { return $_[0] -> {'*Table'} ; } ## ---------------------------------------------------------------------------- ## ## TableNameWithoutFilter ## ## return name of table. If a !TabFilter was specified, and the table start with ## that filter text, it is removed from the front of the name ## sub TableNameWithoutFilter ($) { my $tab = $_[0] -> {'*Table'} ; return $1 if ($tab =~ /^$_[0]->{'*TableFilter'}(.*?)$/) ; return $tab ; } ## ---------------------------------------------------------------------------- ## ## PrimKey ## ## return name of primary key ## sub PrimKey ($) { return $_[0] -> {'*PrimKey'} ; } ## ---------------------------------------------------------------------------- ## ## TableFilter ## ## return table filter ## sub TableFilter ($) { return $_[0] -> {'*TableFilter'} ; } ## ---------------------------------------------------------------------------- ## ## AllNames ## ## return reference to array of all names in all tables ## sub AllNames { return $_[0] -> {'*Names'} ; } ## ---------------------------------------------------------------------------- ## ## AllTypes ## ## return reference to array of all types in all tables ## sub AllTypes { return $_[0] -> {'*Types'} ; } ## ---------------------------------------------------------------------------- ## ## Names ## ## return reference to array of names of the last query ## sub Names { my $self = shift ; if ($self -> {'*LinkName'} < 2) { return $self->{'*SelectFields'} ; } else { my $names = $self->{'*SelectFields'}; my $repl = $self -> {'*ReplaceFields'} ; my @newnames ; my $i ; for ($i = 0; $i <= $#$repl; $i++) { #print LOG "### Names $i = $names->[$i]\n" ; push @newnames, $names -> [$i] ; } return \@newnames ; } } ## ---------------------------------------------------------------------------- ## ## Types ## ## return reference to array of types of the last query ## sub Types { my $sth = $_[0] -> {'*StHdl'} ; return undef if (!$sth) ; return $sth -> FETCH('TYPE') ; } ## ---------------------------------------------------------------------------- ## ## Link ## ## if linkname if undef returns reference to an hash of all links ## else returns reference to that link ## sub Link { my ($self, $linkname) = @_ ; my $links = $self -> {'*Links'} ; return undef if (!defined ($links)) ; return $links if (!defined ($linkname)) ; return $links -> {$linkname} ; } ## ---------------------------------------------------------------------------- ## ## Link4Field ## ## returns the Linkname for that field, if any ## sub Link4Field { my ($self, $field) = @_ ; my $links = $self -> {'*Links'} ; return undef if (!defined ($field)) ; my $tab4f = $self -> {'*Table4Field'} ; if (!exists ($self -> {'*MainFields'})) { my $k ; my $v ; my $mf = {} ; my $f ; while (($k, $v) = each (%$links)) { $f = $v -> {'!MainField'} ; $mf -> {$f} = $k ; $mf -> {"$tab4f->{$f}.$f"} = $k ; print LOG "DB: Field $v->{'!MainField'} has link $k\n" ; } $self -> {'*MainFields'} = $mf ; } return $self -> {'*MainFields'} -> {$field} ; } ## ---------------------------------------------------------------------------- ## ## Links ## ## return reference to an hash of links ## sub Links { return $_[0] -> {'*Links'} ; } ## ---------------------------------------------------------------------------- ## ## TableAttr ## ## get and/or set an unser defined attribute of that table ## ## $key = key ## $value = new value (optional) ## $table = Name of table(s) (optional) ## sub TableAttr { my ($self, $key, $value, $table) = @_ ; $table ||= $self -> {'*MainTable'} ; my $meta ; my $metakey = "$self->{'*DataSource'}//" . ($PreserveCase?$table:lc ($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} ; } ## ---------------------------------------------------------------------------- ## ## Stats ## ## return statistics ## sub Stats { return $_[0] -> {'*Stats'} ; } ## ---------------------------------------------------------------------------- ## ## StartRecordNo ## ## return the record no which will be returned for index 0 ## sub StartRecordNo { return $_[0] -> {'*StartRecordNo'} ; } ## ---------------------------------------------------------------------------- ## ## LastSQLStatement ## ## return the last executed SQL Statement ## sub LastSQLStatement { return $_[0] -> {'*LastSQLStatement'} ; } ## ---------------------------------------------------------------------------- ## ## LastSerial ## ## return the last value of the field defined with !Serial ## sub LastSerial { return $_[0] -> {'*LastSerial'} ; } ## ---------------------------------------------------------------------------- ## ## LastError ## ## returns the last error message and code (code only in array context) ## sub LastError { my $self = shift ; if (ref $self) { if (wantarray) { return ($self -> {'*LastErrstr'}, $self -> {'*LastErr'}) ; } else { return $self -> {'*LastErrstr'} ; } } else { if (wantarray) { return ($LastErrstr, $LastErr) ; } else { return $LastErrstr ; } } } ## ---------------------------------------------------------------------------- ## ## SQL Insert ... ## ## $fields = comma separated list of fields to insert ## $vals = comma separated list of values to insert ## \@bind_values = values which should be insert for placeholders ## \@bind_types = data types of bind_values ## sub SQLInsert ($$$$) { my ($self, $fields, $vals, $bind_values, $bind_types) = @_ ; $self -> savecroak ("Insert disabled for table $self->{'*Table'}") if (!($self->{'*WriteMode'} & wmINSERT)) ; $self->{'*Stats'}{insert}++ ; if (defined ($bind_values)) { return $self->do ("INSERT INTO $self->{'*Table'} ($fields) VALUES ($vals)", undef, $bind_values, $bind_types) ; } else { return $self->do ("INSERT INTO $self->{'*Table'} ($fields) VALUES ($vals)") ; } } ## ---------------------------------------------------------------------------- ## ## SQL Update ... ## ## $data = komma separated list of fields=value to update ## $where = SQL Where condition ## \@bind_values = values which should be insert for placeholders ## \@bind_types = data types of bind_values ## ## sub SQLUpdate ($$$$) { my ($self, $data, $where, $bind_values, $bind_types) = @_ ; $self -> savecroak ("Update disabled for table $self->{'*Table'}") if (!($self->{'*WriteMode'} & wmUPDATE)) ; $self->{'*Stats'}{update}++ ; if (defined ($bind_values)) { return $self->do ("UPDATE $self->{'*Table'} SET $data WHERE $where", undef, $bind_values, $bind_types) ; } else { return $self->do ("UPDATE $self->{'*Table'} SET $data WHERE $where") ; } } ## ---------------------------------------------------------------------------- ## ## SQL Delete ... ## ## $where = SQL Where condition ## \@bind_values = values which should be insert for placeholders ## \@bind_types = data types of bind_values ## ## sub SQLDelete ($$$) { my ($self, $where, $bind_values, $bind_types) = @_ ; $self -> savecroak ("Delete disabled for table $self->{'*Table'}") if (!($self->{'*WriteMode'} & wmDELETE)) ; $self -> savecroak ("Clear (Delete all) disabled for table $self->{'*Table'}") if (!$where && !($self->{'*WriteMode'} & wmCLEAR)) ; $self->{'*Stats'}{'delete'}++ ; if (defined ($bind_values)) { return $self->do ("DELETE FROM $self->{'*Table'} " . ($where?"WHERE $where":''), undef, $bind_values, $bind_types) ; } else { return $self->do ("DELETE FROM $self->{'*Table'} " . ($where?"WHERE $where":'')) ; } } ## ---------------------------------------------------------------------------- ## ## SQL Select ## ## Does an SQL Select of the form ## ## SELECT $fields FROM WHERE $expr ORDERBY $order ## ## $expr = SQL Where condition (optional, defaults to no condition) ## $fields = fields to select (optional, default to *) ## $order = fields for sql order by or undef for no sorting (optional, defaults to no order) ## $group = fields for sql group by or undef (optional, defaults to no grouping) ## $append = append that string to the select statemtn for other options (optional) ## \@bind_values = values which should be inserted for placeholders ## \@bind_types = data types of bind_values ## sub SQLSelect ($;$$$$$$$) { my ($self, $expr, $fields, $order, $group, $append, $bind_values, $bind_types, $makesql, ) = @_ ; my $sth ; # statement handle my $where ; # where or nothing my $orderby ; # order by or nothing my $groupby ; # group by or nothing my $rc ; # my $table ; if (defined ($self->{'*StHdl'})) { $self->{'*StHdl'} -> finish () ; print LOG "DB: Call DBI finish (id=$self->{'*Id'}, Last = $self->{'*LastSQLStatement'})\n" if ($self->{'*Debug'} > 3) ; } undef $self->{'*StHdl'} ; $self->ReleaseRecords ; undef $self->{'*LastKey'} ; $self->{'*FetchStart'} = 0 ; $self->{'*StartRecordNo'} = 0 ; $self->{'*FetchMax'} = undef ; $self->{'*EOD'} = undef ; $self->{'*SelectFields'} = undef ; $self->{'*LastRecord'} = undef ; $order ||= '' ; $expr ||= '' ; $group ||= '' ; $append ||= '' ; $orderby = $order?'ORDER BY':'' ; $groupby = $group?'GROUP BY':'' ; $where = $expr?'WHERE':'' ; $fields ||= '*'; $table = $self->{'*TabJoin'} || $self->{'*Table'} ; my $statement; if ($self->{'*Query'}) { $statement = $self->{'*Query'} . " " . $append; } else { $statement = "SELECT $fields FROM $table $where $expr $groupby $group $orderby $order $append" ; } if ($self->{'*Debug'} > 1) { my $bv = $bind_values || [] ; my $bt = $bind_types || [] ; print LOG "DB: '$statement' bind_values=<@$bv> bind_types=<@$bt>\n" ; } $self -> {'*LastSQLStatement'} = $statement ; return $statement if $makesql; $self->{'*Stats'}{'select'}++ ; $sth = $self->{'*DBHdl'} -> prepare ($statement) ; if (defined ($sth)) { my @x ; my $ni = 0 ; my $Numeric = $self->{'*NumericTypes'} ; local $^W = 0 ; # avoid warnings for (my $i = 0 ; $i < @$bind_values; $i++) { #print LOG "bind $i bv=<$bind_values->[$i]> bvcnv=" . ($Numeric -> {$bind_types -> [$i]}?$bind_values -> [$i]+0:$bind_values -> [$i]) . " bt=$bind_types->[$i] n=$Numeric->{$bind_types->[$i]}\n" ; $bind_values -> [$i] += 0 if (defined ($bind_values -> [$i]) && defined ($bind_types -> [$i]) && $Numeric -> {$bind_types -> [$i]}) ; #my $bti = $bind_types -> [$i]+0 ; #$sth -> bind_param ($i+1, $bind_values -> [$i], {TYPE => $bti}) ; #$sth -> bind_param ($i+1, $bind_values -> [$i], $bind_types -> [$i] == DBI::SQL_CHAR()?DBI::SQL_CHAR():undef) ; my $bt = $bind_types -> [$i] ; $sth -> bind_param ($i+1, $bind_values -> [$i], (defined ($bt) && $bt <= DBI::SQL_CHAR())?{TYPE => $bt}:undef ) ; } $rc = $sth -> execute ; $self->{'*SelectedRows'} = $sth->rows; } $LastErr = $self->{'*LastErr'} = $DBI::err ; $LastErrstr = $self->{'*LastErrstr'} = $DBI::errstr ; my $names ; if ($rc) { $names = $sth -> FETCH (($PreserveCase?'NAME':'NAME_lc')) ; $self->{'*NumFields'} = $#{$names} + 1 ; } else { print LOG "DB: ERROR $DBI::errstr\n" if ($self->{'*Debug'}) ; print LOG "DB: in '$statement' bind_values=<@$bind_values> bind_types=<@$bind_types>\n" if ($self->{'*Debug'} == 1) ; $self->{'*NumFields'} = 0 ; undef $sth ; } $self->{'*CurrRow'} = 0 ; $self->{'*LastRow'} = 0 ; $self->{'*StHdl'} = $sth ; my @ofunca ; my $ofunc = $self -> {'*OutputFunctions'} ; if ($ofunc && $names) { my $i = 0 ; foreach (@$names) { $ofunca [$i++] = $ofunc -> {$_} ; } } $self -> {'*OutputFuncArray'} = \@ofunca ; if ($self->{'*LongNames'}) { if ($fields eq '*') { $self->{'*SelectFields'} = $self->{'*FullNames'} ; } else { my $tab4f = $self -> {'*Table4Field'} ; #my @allfields = map { (/\./)?$_:"$tab4f->{$_}.$_" } split (/\s*,\s*/, $fields) ; my @allfields = map { (/\./)?$_:"$tab4f->{$_}.$_" } quotewords ('\s*,\s*', 0, $fields) ; shift @allfields if (lc($allfields[0]) eq 'distinct') ; $self->{'*SelectFields'} = \@allfields ; } } else { $self->{'*SelectFields'} = $names ; } return $rc ; } ## ---------------------------------------------------------------------------- ## ## FECTHSIZE - returns the number of rows form the last SQLSelect ## ## WARNING: Not all DBD drivers returns the correct number of rows ## so we issue a warning/error message when this function is used ## sub FETCHSIZE { my ($self) = @_; die "FETCHSIZE may not supported by your DBD driver, set \$FetchsizeWarn to zero if you are sure it works. Read about \$FetchsizeWarn in the docs!" if ($FetchsizeWarn == 2) ; warn "FETCHSIZE may not supported by your DBD driver, set \$FetchsizeWarn to zero if you are sure it works. Read about \$FetchsizeWarn in the docs!" if ($FetchsizeWarn == 1) ; my $sel = $self->{'*SelectedRows'} ; return $sel if (!defined ($self->{'*FetchMax'})) ; my $max = $self->{'*FetchMax'} - $self->{'*FetchStart'} + 1 ; return $max<$sel?$max:$sel ; } ## ---------------------------------------------------------------------------- ## ## Fetch the data from a previous SQL Select ## ## $fetch = Row to fetch ## ## fetchs the nth row and return a ref to an hash containing the entire row data ## sub FETCH { my ($self, $fetch) = @_ ; print LOG "DB: FETCH \[$fetch\]\n" if ($self->{'*Debug'} > 3) ; $fetch += $self->{'*FetchStart'} ; return $self->{'*LastRecord'} if (defined ($self->{'*LastRecordFetch'}) && $fetch == $self->{'*LastRecordFetch'} && $self->{'*LastRecord'}) ; my $max ; my $key ; my $dat ; # row data $max = $self->{'*FetchMax'} ; my $row = $self->{'*CurrRow'} ; # row next to fetch from db my $sth = $self->{'*StHdl'} ; # statement handle my $data = $Data{$self->{'*Id'}} ; # data storage (Data is stored in a seperate hash to avoid circular references) if ($row <= $fetch && !$self->{'*EOD'} && defined ($sth)) { # successfull select has happend before ? return undef if (!defined ($sth)) ; return undef if (defined ($max) && $row > $max) ; my $fld = $self->{'*SelectFields'} ; my $arr ; my $i ; if ($self -> {'*StoreAll'}) { while ($row < $fetch) { if (!($arr = $sth -> fetchrow_arrayref ())) { $self->{'*EOD'} = 1 ; $sth -> finish ; print LOG "DB: Call DBI finish (id=$self->{'*Id'}, LastRow = $row, Last = $self->{'*LastSQLStatement'})\n" if ($self->{'*Debug'} > 3) ; undef $self->{'*StHdl'} ; last ; } $i = 0 ; $data->[$row] = [ @$arr ] ; $row++ ; last if (defined ($max) && $row > $max) ; } } else { while ($row < $fetch) { if (!$sth -> fetchrow_arrayref ()) { $self->{'*EOD'} = 1 ; $sth -> finish ; print LOG "DB: Call DBI finish (id=$self->{'*Id'}, Last = $self->{'*LastSQLStatement'})\n" if ($self->{'*Debug'} > 3) ; undef $self->{'*StHdl'} ; last ; } $row++ ; last if (defined ($max) && $row > $max) ; } } $self->{'*LastRow'} = $row ; if ($row == $fetch && !$self->{'*EOD'}) { $arr = $sth -> fetchrow_arrayref () ; if ($arr) { $row++ ; $dat = {} ; if ($self -> {'*TieRow'}) { my $obj = tie %$dat, 'DBIx::Recordset::Row', $self, $fld, $arr ; $self->{'*LastKey'} = $obj -> FETCH ($self -> {'*PrimKey'}) ; } else { @$dat{@$fld} = @$arr ; my $nf = $self -> {'*NameField'} || $self -> TableAttr ('!NameField') ; if ($nf) { if (!ref $nf) { $dat -> {'!Name'} = $dat -> {uc($nf)} || $dat -> {$nf} ; } else { $dat -> {'!Name'} = join (' ', map { $dat -> {uc ($_)} || $dat -> {$_} } @$nf) ; } } $self->{'*LastKey'} = $dat -> {$self -> {'*PrimKey'}} if ($self -> {'*PrimKey'}) ; } $data -> [$fetch] = $dat ; } else { $dat = $data -> [$fetch] = undef ; #print LOG "new dat undef\n" ; $self->{'*EOD'} = 1 ; $sth -> finish ; print LOG "DB: Call DBI finish (id=$self->{'*Id'}, Last = $self->{'*LastSQLStatement'})\n" if ($self->{'*Debug'} > 3) ; undef $self->{'*StHdl'} ; } } $self->{'*CurrRow'} = $row ; } else { my $obj ; $dat = $data -> [$fetch] if (!defined ($max) || $fetch <= $max); if (ref $dat eq 'ARRAY') { # just an Array so tie it now my $arr = $dat ; $dat = {} ; $obj = tie %$dat, 'DBIx::Recordset::Row', $self, $self->{'*SelectFields'} , $arr ; $data -> [$fetch] = $dat ; $self->{'*LastRow'} = $fetch ; $self->{'*LastKey'} = $obj -> FETCH ($self -> {'*PrimKey'}) ; } else { #my $v ; #my $k ; #print LOG "old dat\n" ; # = $dat ref = " . ref ($dat) . " tied = " . ref (tied(%$dat)) . " fetch = $fetch\n" ; #while (($k, $v) = each (%$dat)) # { # print "$k = $v\n" ; # } my $obj = tied(%$dat) if ($dat) ; $self->{'*LastRow'} = $fetch ; $self->{'*LastKey'} = $obj?($obj -> FETCH ($self -> {'*PrimKey'})):undef ; } } if ($row == $fetch + 1 && !$self->{'*EOD'}) { # check if there are more records, if not close the statement handle my $arr ; $arr = $sth -> fetchrow_arrayref () if ($sth) ; my $orgrow = $row ; if ($arr) { $data->[$row] = [ @$arr ] ; $row++ ; $self->{'*CurrRow'} = $row ; } if ((defined ($max) && $orgrow > $max) || !$arr) { $self->{'*EOD'} = 1 ; $sth -> finish if ($sth) ; print LOG "DB: Call DBI finish (id=$self->{'*Id'}, LastRow = $row, Last = $self->{'*LastSQLStatement'})\n" if ($self->{'*Debug'} > 3) ; undef $self->{'*StHdl'} ; } } $self->{'*LastRecord'} = $dat ; $self->{'*LastRecordFetch'} = $fetch ; print LOG 'DB: FETCH return ' . (defined ($dat)?$dat:'') . "\n" if ($self->{'*Debug'} > 3) ; return $dat ; } ## ---------------------------------------------------------------------------- ## ## Reset ... ## ## position the record pointer before the first row, just as same as after Search ## sub Reset ($) { my $self = shift ; $self->{'*LastRecord'} = undef ; $self ->{'*LastRow'} = 0 ; } ## ---------------------------------------------------------------------------- ## ## First ... ## ## position the record pointer to the first row and return it ## sub First ($;$) { my ($self, $new) = @_ ; my $rec = $self -> FETCH (0) ; return $rec if (defined ($rec) || !$new) ; # create new record return $self -> {'*LastRecord'} = $self -> STORE (0) ; } ## ---------------------------------------------------------------------------- ## ## Last ... ## ## position the record pointer to the last row ## DOES NOT WORK!! ## ## sub Last ($) { $_[0] -> FETCH (0x7fffffff) ; # maxmimun postiv integer return undef if ($_[0] -> {'*LastRow'} == 0) ; return $_[0] -> Prev ; } ## ---------------------------------------------------------------------------- ## ## Next ... ## ## position the record pointer to the next row and return it ## sub Next ($;$) { my ($self, $new) = @_ ; my $lr = $self -> {'*LastRow'} ; $lr -= $self -> {'*FetchStart'} ; $lr = 0 if ($lr < 0) ; $lr++ if (defined ($self -> {'*LastRecord'})) ; ##$lr++ if ($_[0] ->{'*CurrRow'} > 0 || $_[0] ->{'*EOD'}) ; my $rec = $self -> FETCH ($lr) ; return $rec if (defined ($rec) || !$new) ; # create new record return $self -> {'*LastRecord'} = $self -> STORE ($lr) ; } ## ---------------------------------------------------------------------------- ## ## Prev ... ## ## position the record pointer to the previous row and return it ## sub Prev ($) { $_[0] -> {'*LastRow'} = 0 if (($_[0] -> {'*LastRow'})-- == 0) ; return $_[0] -> FETCH ($_[0] ->{'*LastRow'} - $_[0] -> {'*FetchStart'}) ; } ## ---------------------------------------------------------------------------- ## ## Fetch the data from current row ## sub Curr ($;$) { my ($self, $new) = @_ ; my $lr ; return $lr if ($lr = $self->{'*LastRecord'}) ; my $n = $self ->{'*LastRow'} - $self -> {'*FetchStart'} ; my $rec = $self -> FETCH ($n) ; return $rec if (defined ($rec) || !$new) ; # create new record return $self -> STORE ($n) ; } ## ---------------------------------------------------------------------------- ## ## BuildFields ... ## sub BuildFields { my ($self, $fields, $table, $tabrel) = @_ ; my @fields ; my $tab4f = $self -> {'*Table4Field'} ; my $fnames = $self -> {'*FullNames'} ; my $debug = $self -> {'*Debug'} ; my $drv = $self->{'*Driver'} ; my %tables ; my %fields ; my %tabrel ; my @replace ; my $linkname ; my $link ; my $nf ; my $fn ; my @allfields ; my @orderedfields ; my $i ; my $n ; my $m ; my %namefields ; my $leftjoin = DBIx::Compat::GetItem ($drv, 'SupportSQLJoin') ; my $numtabs = 99 ; local $^W = 0 ; $numtabs = 2 if (DBIx::Compat::GetItem ($drv, 'SQLJoinOnly2Tabs')) ; #%tables = map { $_ => 1 } split (/\s*,\s*/, $table) ; %tables = map { $_ => 1 } quotewords ('\s*,\s*', 0, $table) ; $numtabs -= keys %tables ; #print LOG "###--> numtabs = $numtabs\n" ; if (defined ($fields) && !($fields =~ /^\s*\*\s*$/)) { #@allfields = map { (/\./)?$_:"$tab4f->{$_}.$_" } split (/\s*,\s*/, $fields) ; # @allfields = map { (/\./)?$_:"$tab4f->{$_}.$_" } quotewords ('\s*,\s*', 0, $fields) ; @allfields = map { (/\./ || !$tab4f->{$_})?$_:"$tab4f->{$_}.$_" } quotewords ('\s*,\s*', 0, $fields) ; #print LOG "###allfields = @allfields\n" ; } else { @allfields = @$fnames ; } $nf = $self -> {'*NameField'} || $self -> TableAttr ('!NameField') ; if ($nf) { if (ref ($nf) eq 'ARRAY') { %namefields = map { ($fn = "$tab4f->{$_}\.$_") => 1 } @$nf ; } else { %namefields = ( "$tab4f->{$nf}.$nf" => 1 ) ; } @orderedfields = keys %namefields ; foreach $fn (@allfields) { push @orderedfields, $fn if (!$namefields{$fn}) ; } } else { @orderedfields = @allfields ; } $i = 0 ; %fields = map { $_ => $i++ } @orderedfields ; $n = $#orderedfields ; $m = $n + 1; for ($i = 0; $i <=$n; $i++) { #print LOG "###loop numtabs = $numtabs\n" ; $fn = $orderedfields[$i] ; $replace[$i] = [$i] ; next if ($numtabs <= 0) ; next if (!($linkname = $self -> Link4Field ($fn))) ; next if (!($link = $self -> Link ($linkname))) ; # does not work with another Datasource or with an link to the table itself next if ($link -> {'!DataSource'} || $link -> {'!Table'} eq $self -> {'!Table'}) ; $nf = $link->{'!NameField'} || $self -> TableAttr ('!NameField', undef, $link->{'!Table'}) ; if (!$link -> {'!LinkedBy'} && $nf) { $replace[$i] = [] ; if (ref $nf) { foreach (@$nf) { if (!exists $fields{"$link->{'!Table'}.$_"}) { push @orderedfields, "$link->{'!Table'}.$_" ; push @allfields, "$link->{'!Table'}.$_" ; $fields{"$link->{'!Table'}.$_"} = $m ; push @{$replace[$i]}, $m ; print LOG "[$$] DB: Add to $self->{'*Table'} linked name field $link->{'!Table'}.$_ (i=$i, n=$n, m=$m)\n" if ($debug > 2) ; $m++ ; } } } else { if (!exists $fields{"$link->{'!Table'}.$nf"}) { push @orderedfields, "$link->{'!Table'}.$nf" ; push @allfields, "$link->{'!Table'}.$nf" ; $fields{"$link->{'!Table'}.$nf"} = $m ; push @{$replace[$i]}, $m ; print LOG "[$$] DB: Add to $self->{'*Table'} linked name field $link->{'!Table'}.$nf (i=$i, n=$n, m=$m)\n" if ($debug > 2) ; $m++ ; } } $numtabs-- if (!exists $tables{$link->{'!Table'}}) ; $tables{$link->{'!Table'}} = "$fn = $link->{'!Table'}.$link->{'!LinkedField'}" ; } elsif ($debug > 2 && !$link -> {'!LinkedBy'}) { print LOG "[$$] DB: No name, so do not add to $self->{'*Table'} linked name field $link->{'!Table'}.$fn\n" ;} } #my $rfields = join (',', @allfields) ; my $rfields = join (',', @orderedfields) ; my $rtables = join (',', keys %tables) ; delete $tables{$table} ; my $rtabrel ; if ($leftjoin == 1) { my @tabs = keys %tables ; $rtabrel = ('(' x scalar(@tabs)) . $table . ' ' . join (' ', map { "LEFT JOIN $_ on $tables{$_})" } @tabs) ; } elsif ($leftjoin == 2) { my $v ; $tabrel = ($tabrel?"$tabrel and ":'') . join (' and ', map { $v = $tables{$_} ; $v =~ s/=/*=/ ; $v } keys %tables) ; } elsif ($leftjoin == 3) { my $v ; $tabrel = ($tabrel?"$tabrel and ":'') . join (' and ', map { "$tables{$_} (+)" } keys %tables) ; } elsif ($leftjoin == 4) { my @tabs = keys %tables ; $rtabrel = $table . ' ' . join ' ', map { "LEFT JOIN $_ on $tables{$_}" } @tabs ; } else { my $v ; $rtabrel = $table . ',' . join (',', map { "OUTER $_ " } keys %tables) ; $tabrel = ($tabrel?"$tabrel and ":'') . join (' and ', values %tables) ; } return ($rfields, $rtables, $rtabrel, $tabrel, \@replace) ; } ## ---------------------------------------------------------------------------- ## ## BuildWhere ... ## ## \%where/$where = hash of which the SQL Where condition is build ## or SQL Where condition as text ## \@bind_values = returns the bind_value array for placeholder supported ## \@bind_types = returns the bind_type array for placeholder supported ## ## ## Builds the WHERE condition for SELECT, UPDATE, DELETE ## upon the data which is given in the hash \%where or string $where ## ## Key Value ## Value for field (automatily quote if necessary) ## ' Value for field (always quote) ## # Value for field (never quote, convert to number) ## \ Value for field (leave value as it is) ## +|.. Value for fields (value must be in one/all fields ## depending on $compconj ## $compconj 'or' or 'and' (default is 'or') ## ## $valuesplit regex for spliting a field value in mulitply value ## per default one of the values must match the field ## could be changed via $valueconj ## $valueconj 'or' or 'and' (default is 'or') ## ## $conj 'or' or 'and' (default is 'and') conjunction between ## fields ## ## $operator Default operator ## * Operator for the named field ## ## $primkey primary key ## ## $where where as string ## sub BuildWhere ($$$$) { my ($self, $where, $xbind_values, $bind_types, $sub) = @_ ; my $expr = '' ; my $primkey ; my $Quote = $self->{'*Quote'} ; my $Debug = $self->{'*Debug'} ; my $ignore = $self->{'*IgnoreEmpty'} ; my $nullop = $self->{'*NullOperator'} ; my $hasIn = $self->{'*HasInOperator'} ; my $linkname = $self->{'*LinkName'} ; my $tab4f = $self->{'*Table4Field'} ; my $type4f = $self->{'*Type4Field'} ; my $ifunc = $self->{'*InputFunctions'} ; my $bind_values = ref ($xbind_values) eq 'ARRAY'?$xbind_values:$$xbind_values ; if (!ref($where)) { # We have the where as string $expr = $where ; if ($Debug > 2) { print LOG "DB: Literal where -> $expr\n" ; } } elsif (exists $where -> {'$where'}) { # We have the where as string $expr = $where -> {'$where'} ; if (exists $where -> {'$values'}) { if (ref ($xbind_values) eq 'ARRAY') { push @$xbind_values, @{$where -> {'$values'}} ; } else { $$xbind_values = $where -> {'$values'} ; } } if ($Debug > 2) { print LOG "DB: Literal where -> $expr\n" ; } } elsif (defined ($primkey = $self->{'*PrimKey'}) && defined ($where -> {$primkey}) && (!defined ($where -> {"\*$primkey"}) || $where -> {"\*$primkey"} eq '=') && !ref ($where -> {$primkey})) { # simplify where when ask for = ? my $oper = $$where{"\*$primkey"} || '=' ; my $pkey = $primkey ; $pkey = "$tab4f->{$primkey}.$primkey" if ($linkname && !($primkey =~ /\./)) ; # any input conversion ? my $val = $where -> {$primkey} ; my $if = $ifunc -> {$primkey} ; $val = &{$if} ($val) if ($if) ; $expr = "$pkey$oper ? "; push @$bind_values, $val ; push @$bind_types, $type4f -> {$primkey} ; if ($Debug > 2) { print LOG "DB: Primary Key $primkey found -> $expr\n" ; } } else { my $key ; my $lkey ; my $val ; my @mvals ; my $field ; my @fields ; my $econj ; my $vconj ; my $fconj ; my $vexp ; my $fieldexp ; my $type ; my $oper = $$where{'$operator'} || '=' ; my $op ; my $mvalsplit = $$where{'$valuesplit'} || "\t" ; my $lexpr = '' ; my $multcnt ; my $uright ; $econj = '' ; while (($key, $val) = each (%$where)) { my @multtypes ; my @multval ; my $if ; $type = substr ($key, 0, 1) || ' ' ; $val = undef if ($ignore > 1 && defined ($val) && $val eq '') ; if ($Debug > 2) { print LOG "DB: SelectWhere <$key>=<" . (defined ($val)?$val:'') ."> type = $type\n" ; } $vexp = '' ; if (substr ($key, 0, 5) eq '$expr') { $vexp = $self -> BuildWhere ($val, $bind_values, $bind_types, 1) if ($val) ; } else { if (($type =~ /^(\w|\\|\+|\'|\#|\s)$/) && !($ignore && !defined ($val))) { if ($type eq '+') { # composite field if ($Debug > 3) { print LOG "DB: Composite Field $key\n" ; } $fconj = '' ; $fieldexp = '' ; @fields = split (/\&|\|/, substr ($key, 1)) ; $multcnt = 0 ; foreach $field (@fields) { if ($Debug > 3) { print LOG "DB: Composite Field processing $field\n" ; } if (!defined ($$Quote{$PreserveCase?$field:lc ($field)})) { if ($Debug > 2) { print LOG "DB: Ignore non existing Composite Field $field\n" ; } next ; } # ignore no existent field $op = $$where{"*$field"} || $oper ; $field = "$tab4f->{$field}.$field" if ($linkname && !($field =~ /\./)) ; if (($uright = $unaryoperators{lc($op)})) { if ($uright == 1) { $fieldexp = "$fieldexp $fconj $field $op" } else { $fieldexp = "$fieldexp $fconj $op $field" } } elsif ($type eq '\\') { $fieldexp = "$fieldexp $fconj $field $op $val" ; } elsif (defined ($val)) { $fieldexp = "$fieldexp $fconj $field $op ?" ; push @multtypes, $type4f -> {$field} ; $multcnt++ ; } elsif ($op eq '<>') { $fieldexp = "$fieldexp $fconj $field $nullop not NULL" ; } else { $fieldexp = "$fieldexp $fconj $field $nullop NULL" ; } $fconj ||= $$where{'$compconj'} || ' or ' ; if ($Debug > 3) { print LOG "DB: Composite Field get $fieldexp\n" ; } } if ($fieldexp eq '') { next ; } # ignore no existent field } else { # single field $multcnt = 0 ; # any input conversion ? $if = $ifunc -> {$PreserveCase?$key:lc ($key)} ; ## see bvelow ## $val = &{$if} ($val) if ($if && !ref($val)) ; if ($type eq '\\' || $type eq '#' || $type eq "'") { # remove leading backslash, # or ' $key = substr ($key, 1) ; } $lkey = $PreserveCase?$key:lc ($key) ; if ($type eq "'") { $$Quote{$lkey} = 1 ; } elsif ($type eq '#') { $$Quote{$lkey} = 0 ; } { local $^W = 0 ; # avoid warnings #$val += 0 if ($$Quote{$lkey}) ; # convert value to a number if necessary } if (!defined ($$Quote{$lkey}) && $type ne '\\') { if ($Debug > 3) { print LOG "DB: Ignore Single Field $key\n" ; } next ; # ignore no existent field } if ($Debug > 3) { print LOG "DB: Single Field $key\n" ; } $op = $$where{"*$key"} || $oper ; $key = "$tab4f->{$lkey}.$key" if ($linkname && $type ne '\\' && !($key =~ /\./)) ; if (($uright = $unaryoperators{lc($op)})) { if ($uright == 1) { $fieldexp = "$key $op" } else { $fieldexp = "$op $key" } } elsif ($type eq '\\') { $fieldexp = "$key $op $val" ; } elsif (defined ($val)) { $fieldexp = "$key $op ?" ; push @multtypes, $type4f -> {$lkey} ; $multcnt++ ; } elsif ($op eq '<>') { $fieldexp = "$key $nullop not NULL" ; } else { $fieldexp = "$key $nullop NULL" ; } if ($Debug > 3) { print LOG "DB: Single Field gives $fieldexp\n" ; } } my @multop ; @multop = @$op if (ref ($op) eq 'ARRAY') ; if (!defined ($val)) { @mvals = (undef) } elsif ($val eq '') { @mvals = ('') } else { if (ref ($val) eq 'ARRAY') { if ($if) { @mvals = map { &{$if} ($_) } @$val } else { @mvals = @$val ; } } else { if ($if) { @mvals = map { &{$if} ($_) } split (/$mvalsplit/, $val) ; } else { @mvals = split (/$mvalsplit/, $val) ; } } } $vconj = '' ; my $i ; if ($hasIn && @mvals > 1 && !@multop && $op eq '=' && !$$where{'$valueconj'} && $type ne '+') { my $j = 0 ; $vexp = "$key IN (" ; foreach $val (@mvals) { $i = $multcnt ; push @$bind_values, $val while ($i-- > 0) ; push @$bind_types, @multtypes ; $vexp .= $j++?',?':'?' ; } $vexp .= ')' ; } else { foreach $val (@mvals) { $i = $multcnt ; push @$bind_values, $val while ($i-- > 0) ; push @$bind_types, @multtypes ; if (@multop) { $vexp = "$vexp $vconj ($key " . (shift @multop) . ' ?)' ; } else { $vexp = "$vexp $vconj ($fieldexp)" ; } $vconj ||= $$where{'$valueconj'} || ' or ' ; } } } } if ($vexp) { if ($Debug > 3) { local $^W = 0 ; print LOG "DB: Key $key gives $vexp bind_values = <@$bind_values> bind_types=<@$bind_types>\n" ; } $expr = "$expr $econj ($vexp)" ; $econj ||= $$where{'$conj'} || ' and ' ; } if ($Debug > 3 && $lexpr ne $expr) { $lexpr = $expr ; print LOG "DB: expr is $expr\n" ; } } } # Now we add the Table relations, if any my $tabrel = $self->{'*TabRelation'} ; if ($tabrel && !$sub) { if ($expr) { $expr = "($tabrel) and ($expr)" ; } else { $expr = $tabrel ; } } return $expr ; } ## ---------------------------------------------------------------------------- ## ## Dirty - see if there is at least one dirty row ## ## sub Dirty { my $self = shift; my $data = $Data{ $self->{'*Id'} }; return undef unless ( ref($data) eq 'ARRAY'); foreach my $rowdata (@$data) { print LOG "DIRTY: rowref " . (defined ($rowdata)?$rowdata:'') . "\n" if $self->{'*Debug'} > 4; next unless ((ref($rowdata) eq 'HASH') and eval { tied(%$rowdata)->isa('DBIx::Recordset::Row') } ); return 1 if tied(%$rowdata)->Dirty ; }; return 0; # clean } ## ---------------------------------------------------------------------------- ## ## Fush ... ## ## Write all dirty rows to the database ## sub Flush { my $self = shift ; return if ($self -> {'*InFlush'}) ; # avoid endless recursion my $release = shift ; my $dat ; my $obj ; my $dbg = $self->{'*Debug'} ; my $id = $self->{'*Id'} ; my $data = $Data{$id} ; my $rc = 1 ; print LOG "DB: FLUSH Recordset id = $id $self \n" if ($dbg > 2) ; $self -> {'*InFlush'} = 1 ; $self -> {'*UndefKey'} = undef ; # invalidate record for undef hashkey $self->{'*LastRecord'} = undef ; $self->{'*LastRecordFetch'} = undef ; if (defined ($self->{'*StHdl'})) { $self->{'*StHdl'} -> finish () ; print LOG "DB: Call DBI finish (id=$self->{'*Id'}, Last = $self->{'*LastSQLStatement'})\n" if ($self->{'*Debug'} > 3) ; undef $self->{'*StHdl'} ; } eval { my $err ; foreach $dat (@$data) { $obj = (ref ($dat) eq 'HASH')?tied (%$dat):undef ; if (defined ($obj)) { # isolate row update errors eval { local $SIG{__DIE__}; $obj -> Flush (); } or $rc = undef ; $err ||= $@ ; $obj -> {'*Recordset'} = undef if ($release) ; } } die $err if ($err) ; } ; $self -> {'*InFlush'} = 0 ; $self -> savecroak ($@) if ($@) ; return $rc ; } ## ---------------------------------------------------------------------------- ## ## Insert ... ## ## \%data = hash of fields for new record ## sub Insert ($\%) { my ($self, $data) = @_ ; local *newself ; if (!ref ($self)) { *newself = Setup ($self, $data) ; ($self = $newself) or return undef ; } my @bind_values ; my @bind_types ; my @qvals ; my @keys ; my $key ; my $val ; my $q ; my $type4f = $self->{'*Type4Field'} ; my $Quote = $self->{'*Quote'} ; my $ifunc = $self->{'*InputFunctions'} ; my $irfunc = $self->{'*InputFunctionsRequiredOnInsert'} ; my $insertserial ; if ($self -> {'*GetSerialPreInsert'}) { my $val = $data -> {$self -> {'*Serial'}} ; $val = $$val if (ref ($val) eq 'SCALAR') ; if (!defined ($val)) { $data -> {$self -> {'*Serial'}} = &{$self -> {'*GetSerialPreInsert'}} ($self -> {'*DBHdl'}, $self -> {'*Table'}, $self -> {'*Sequence'}) ; $insertserial = $self -> {'*Serial'} ; } $self -> {'*LastSerial'} = $data -> {$self -> {'*Serial'}} ; } elsif ($self -> {'*SeqObj'}) { my $val = $data -> {$self -> {'*Serial'}} ; $val = $$val if (ref ($val) eq 'SCALAR') ; if (!defined ($val)) { $data -> {$self -> {'*Serial'}} = $self -> {'*SeqObj'} -> NextVal ($self -> {'*Sequence'}) ; $insertserial = $self -> {'*Serial'} ; } $self -> {'*LastSerial'} = $data -> {$self -> {'*Serial'}} ; } while (($key, $val) = each (%$data)) { $val = $$val if (ref ($val) eq 'SCALAR') ; # any input conversion ? my $if = $ifunc -> {$key} ; $val = &{$if} ($val, 'insert', $data) if ($if) ; next if (!defined ($val)) ; # skip NULL values if ($key =~ /^\\(.*?)$/) { push @qvals, $val ; push @keys, $1 ; } elsif (defined ($$Quote{$PreserveCase?$key:lc ($key)})) { push @bind_values ,$val ; push @qvals, '?' ; push @keys, $key ; push @bind_types, $type4f -> {$PreserveCase?$key:lc ($key)} ; } } if (@qvals == 1 && $insertserial && exists ($data -> {$insertserial})) { # if the serial is the only value remove if and make no insert @qvals = () ; } if ($#qvals > -1) { foreach $key (@$irfunc) { next if (exists ($data -> {$key})) ; # input function alread applied my $if = $ifunc -> {$key} ; $val = &{$if} (undef, 'insert', $data) if ($if) ; next if (!defined ($val)) ; # skip NULL values if ($key =~ /^\\(.*?)$/) { push @qvals, $val ; push @keys, $1 ; } elsif (defined ($$Quote{$PreserveCase?$key:lc ($key)})) { push @bind_values ,$val ; push @qvals, '?' ; push @keys, $key ; push @bind_types, $type4f -> {$PreserveCase?$key:lc ($key)} ; } } } my $rc ; if ($#qvals > -1) { my $valstr = join (',', @qvals) ; my $keystr = join (',', @keys) ; $rc = $self->SQLInsert ($keystr, $valstr, \@bind_values, \@bind_types) ; $self -> {'*LastSerial'} = &{$self -> {'*GetSerialPostInsert'}} ($self -> {'*DBHdl'}, $self -> {'*Table'}, $self -> {'*Sequence'}) if ($self -> {'*GetSerialPostInsert'}) ; } else { $self -> {'*LastSerial'} = undef ; } return $newself?*newself:$rc ; } ## ---------------------------------------------------------------------------- ## ## Update ... ## ## \%data = hash of fields for new record ## $where/\%where = SQL Where condition ## ## sub Update ($\%$) { my ($self, $data, $where) = @_ ; local *newself ; if (!ref ($self)) { *newself = Setup ($self, $data) ; ($self = $newself) or return undef ; } my $expr ; my @bind_values ; my @bind_types ; my $key ; my $val ; my @vals ; my $q ; my $type4f = $self->{'*Type4Field'} ; my $primkey ; my $Quote = $self->{'*Quote'} ; my $ifunc = $self->{'*InputFunctions'} ; my $irfunc = $self->{'*InputFunctionsRequiredOnUpdate'} ; my $dbg = $self -> {'*Debug'} > 2 ; if ($irfunc) { map { $data -> {$_} = undef if (!exists ($data -> {$_})) } @$irfunc ; } if (defined ($primkey = $self->{'*PrimKey'})) { $val = $data -> {$primkey} ; $val = $$val if (ref ($val) eq 'SCALAR') ; #print LOG "1 primkey = $primkey d=$data->{$primkey} w=" . ($where?$where->{$primkey}:'') . " v=$val\n" ; if (defined ($val) && !$where) { $where = {$primkey => $val} ; } elsif (ref ($where) eq 'HASH' && $val eq $where -> {$primkey}) { delete $data -> {$primkey} ; } else { $primkey = '' ; } } else { $primkey = '' ; } #print LOG "2 primkey = $primkey d=$data->{$primkey} w=" . ($where?$where->{$primkey}:'') . " v=$val\n" ; my $datacnt = 0 ; while (($key, $val) = each (%$data)) { next if ($key eq $primkey) ; $val = $$val if (ref ($val) eq 'SCALAR') ; # any input conversion ? my $if = $ifunc -> {$key} ; print LOG "DB: UPDATE: $key = " . (defined ($val)?$val:'') . " " . ($if?"input filter = $if":'') . "\n" if ($dbg) ; $val = &{$if} ($val, 'update', $data, $where) if ($if) ; if ($key =~ /^\\(.*?)$/) { push @vals, "$1=$val" ; $datacnt++ ; } elsif (defined ($$Quote{$PreserveCase?$key:lc ($key)})) { push @vals, "$key=?" ; push @bind_values, $val ; push @bind_types, $type4f -> {$PreserveCase?$key:lc ($key)} ; $datacnt++ ; } } my $rc = '' ; if ($datacnt) { my $valstr = join (',', @vals) ; if (defined ($where)) { $expr = $self->BuildWhere ($where, \@bind_values, \@bind_types) ; } else { $expr = $self->BuildWhere ($data, \@bind_values, \@bind_types) ; } $rc = $self->SQLUpdate ($valstr, $expr, \@bind_values, \@bind_types) ; } return $newself?*newself:$rc ; } ## ---------------------------------------------------------------------------- ## ## UpdateInsert ... ## ## First try an update, if this fail insert an new record ## ## \%data = hash of fields for record ## sub UpdateInsert ($\%) { my ($self, $fdat) = @_ ; my $rc ; local *newself ; if (!ref ($self)) { *newself = Setup ($self, $fdat) ; ($self = $newself) or return undef ; } $rc = $self -> Update ($fdat) ; print LOG "DB: UpdateInsert update returns: $rc affected rows: $DBI::rows\n" if ($self->{'*Debug'} > 2) ; if (!$rc || $DBI::rows <= 0) { $rc = $self -> Insert ($fdat) ; } return $newself?*newself:$rc ; } ## ---------------------------------------------------------------------------- ## ## Delete ... ## ## $where/\%where = SQL Where condition ## ## sub Delete ($$) { my ($self, $where) = @_ ; local *newself ; if (!ref ($self)) { *newself = Setup ($self, $where) ; ($self = $newself) or return undef ; } my @bind_values ; my @bind_types ; my $expr = $self->BuildWhere ($where,\@bind_values,\@bind_types) ; $self->{'*LastKey'} = undef ; my $rc = $self->SQLDelete ($expr, \@bind_values, \@bind_types) ; return $newself?*newself:$rc ; } ## ---------------------------------------------------------------------------- ## ## DeleteWithLinks ... ## ## $where/\%where = SQL Where condition ## ## sub DeleteWithLinks ($$;$) { my ($self, $where, $seen) = @_ ; $seen = {} if (ref ($seen) ne 'HASH') ; local *newself ; if (!ref ($self)) { *newself = Setup ($self, $where) ; ($self = $newself) or return undef ; } $self -> savecroak ("Delete disabled for table $self->{'*Table'}") if (!($self->{'*WriteMode'} & wmDELETE)) ; my @bind_values ; my @bind_types ; my $expr = $self->BuildWhere ($where,\@bind_values,\@bind_types) ; my $clear_disabled_diag = "(!$expr && !($self->{'*WriteMode'} & wmCLEAR))"; $self -> savecroak ("Clear (Delete all) disabled for table $self->{'*Table'}: $clear_disabled_diag") if (!$expr && !($self->{'*WriteMode'} & wmCLEAR)) ; my $links = $self -> {'*Links'} ; my $k ; my $link ; my $od ; my $selected = 0 ; foreach $k (keys %$links) { $link = $links -> {$k} ; if ($od = $link -> {'!OnDelete'}) { if (!$selected) { my $rc = $self->SQLSelect ($expr, '*', undef, undef, undef, \@bind_values, \@bind_types) ; $selected = 1 ; } $self -> Reset ; my $lf = $link -> {'!LinkedField'} ; my $rec ; while ($rec = $self -> Next) { my $setup = {%$link} ; my $mv ; if (exists ($rec -> {$link -> {'!MainField'}})) { $mv = $rec -> {$link -> {'!MainField'}} ; } else { $mv = $rec -> {"$link->{'!MainTable'}.$link->{'!MainField'}"} ; } $setup -> {'!DataSource'} = $self if (!defined ($link -> {'!DataSource'})) ; print LOG "DB: DeleteLinks link = $k Setup New Recordset for table $link->{'!Table'}, $lf = " . (defined ($mv)?$mv:'') . "\n" if ($self->{'*Debug'} > 1) ; my $updset = DBIx::Recordset -> Setup ($setup) ; if ($od & odDELETE) { my $seenkey = "$link->{'!Table'}::$lf::$mv" ; if (!$seen -> {$seenkey}) { $seen -> {$seenkey} = 1 ; # avoid endless recursion $$updset -> DeleteWithLinks ({$lf => $mv}, $seen) ; } else { print LOG "DB: DeleteLinks detected recursion, do not follow link (key=$seenkey)\n" if ($self->{'*Debug'} > 1) ; } } elsif ($od & odCLEAR) { $$updset -> Update ({$lf => undef}, {$lf => $mv}) ; } } } } $self->{'*LastKey'} = undef ; my $rc = $self->SQLDelete ($expr, \@bind_values, \@bind_types) ; return $newself?*newself:$rc ; } ## ---------------------------------------------------------------------------- ## ## Select ## ## Does an SQL Select of the form ## ## SELECT $fields FROM
WHERE $expr ORDERBY $order ## ## $where/%where = SQL Where condition (optional, defaults to no condition) ## $fields = fields to select (optional, default to *) ## $order = fields for sql order by or undef for no sorting (optional, defaults to no order) ## $group = fields for sql group by or undef (optional, defaults to no grouping) ## $append = append that string to the select statemtn for other options (optional) ## sub Select (;$$$$$) { my ($self, $where, $fields, $order, $group, $append, $makesql) = @_ ; local *newself ; if (!ref ($self)) { *newself = Setup ($self, $where) ; ($self = $newself) or return undef ; } my $bind_values = [] ; my @bind_types ; my $expr = $self->BuildWhere ($where, \$bind_values, \@bind_types) ; my $rc = $self->SQLSelect ($expr, $self->{'*Fields'} || $fields, $self->{'*Order'} || $order, $group, $append, $bind_values, \@bind_types, $makesql, ) ; return $newself?*newself:$rc ; } ## ---------------------------------------------------------------------------- ## ## Search data ## ## \%fdat = hash of form data ## ## Special keys in hash: ## $start: first row to fetch ## $max: maximum number of rows to fetch ## $next: next n records ## $prev: previous n records ## $order: fieldname(s) for ordering (could also contain USING) ## $group: fields for sql group by or undef (optional, defaults to no grouping) ## $append:append that string to the select statemtn for other options (optional) ## $fields:fieldnams(s) to retrieve ## sub Search ($\%) { my ($self, $fdat) = @_ ; local *newself ; if (!ref ($self)) { *newself = Setup ($self, $fdat) ; ($self = $newself) or return undef; } my $Quote = $self->{'*Quote'} ; my $start = $$fdat{'$start'} || 0 ; my $max = $$fdat{'$max'} ; $start = 0 if (defined ($$fdat{'$first'}) || (defined ($start) && $start < 0)) ; $max = 1 if (defined ($max) && $max < 1) ; if (defined ($$fdat{'$prev'})) { $start -= $max ; if ($start < 0) { $start = 0 ; } } elsif (defined ($$fdat{'$next'})) { $start += $max ; } elsif (defined ($$fdat{'$goto'})) { $start = $$fdat{'$gotorow'} - 1 ; if ($start < 0) { $start = 0 ; } } my $startrecno = $start ; my $append = '' ; if (defined ($max) && !$$fdat{'$last'}) { my $LimitOffset = DBIx::Compat::GetItem ($self->{'*Driver'}, 'LimitOffset') ; if ($LimitOffset) { $append = &{$LimitOffset}($start,$$fdat{'$last'}?0:$max+1); $start = 0 if ($append) ; } } my $rc ; { local $^W = 0 ; $rc = $self->Select($fdat, $$fdat{'$fields'}, $$fdat{'$order'}, $$fdat{'$group'}, "$$fdat{'$append'} $append", $fdat->{'$makesql'} ) ; } if ($rc && $$fdat{'$last'}) { # read all until last row my $storeall = $self->{'*StoreAll'} ; $self->{'*StoreAll'} = 1 ; $self -> FETCH (0x7ffffff) ; $startrecno = $start = $self->{'*LastRow'} - ($max || 1) ; $self->{'*StoreAll'} = $storeall ; } $self->{'*StartRecordNo'} = $startrecno ; $self->{'*FetchStart'} = $start ; $self->{'*FetchMax'} = $start + $max - 1 if (defined ($max)) ; return $newself?*newself:$rc ; } ## ---------------------------------------------------------------------------- ## ## Execute ## ## ## \%fdat = hash of form data ## ## =search = search data ## =update = update record(s) ## =insert = insert record ## =delete = delete record(s) ## =empty = setup empty object ## sub Execute ($\%) { my ($self, $fdat) = @_ ; local *newself ; if (!ref ($self)) { *newself = Setup ($self, $fdat) ; ($self = $newself) or return undef ; } if ($self->{'*Debug'} > 2) { print LOG 'DB: Execute ' . ($$fdat{'=search'}?'=search ':'') . ($$fdat{'=update'}?'=update ':'') . ($$fdat{'=insert'}?'=insert ':'') . ($$fdat{'=empty'}?'=empty':'') . ($$fdat{'=delete'}?'=delete':'') . "\n" ; } my $rc = '-' ; if (defined ($$fdat{'=search'})) { $rc = $self -> Search ($fdat) } else { my $serial ; #$rc = $self -> UpdateInsert ($fdat) if (defined ($$fdat{'=update'}) && defined ($$fdat{'=insert'}) && !defined($rc)) ; $rc = $self -> Update ($fdat) if (defined ($$fdat{'=update'}) && $rc eq '-') ; if (defined ($$fdat{'=insert'}) && $rc eq '-') { $rc = $self -> Insert ($fdat) ; if (defined ($rc) && $self -> {'*LastSerial'}) { $serial = $self -> {'*LastSerial'} ; $rc = $self -> Search ({$self->{'*Serial'} => $serial}) ; return $newself?*newself:$rc ; } } $rc = $self -> DeleteWithLinks ($fdat) if (defined ($$fdat{'=delete'}) && $rc eq '-') ; $rc = $self -> Search ($fdat) if (!defined ($$fdat{'=empty'}) && defined ($rc)) ; $rc = 1 if (defined ($$fdat{'=empty'}) && $rc eq '-') ; } return $newself?*newself:$rc ; } ## ---------------------------------------------------------------------------- ## ## PushCurrRec ## sub PushCurrRec { my ($self) = @_ ; # Save Current Record my $sp = $self->{'*CurrRecStack'} ; push @$sp, $self->{'*LastRow'} ; push @$sp, $self->{'*LastKey'} ; push @$sp, $self->{'*FetchMax'} ; } ## ---------------------------------------------------------------------------- ## ## PopCurrRec ## sub PopCurrRec { my ($self) = @_ ; #Restore pointers my $sp = $self->{'*CurrRecStack'} ; $self->{'*FetchMax'} = pop @$sp ; $self->{'*LastKey'} = pop @$sp ; $self->{'*LastRow'} = pop @$sp ; } ## ---------------------------------------------------------------------------- ## ## MoreRecords ## sub MoreRecords { my ($self, $ignoremax) = @_ ; $self -> PushCurrRec ; $self->{'*FetchMax'} = undef if ($ignoremax) ; my $more = $self -> Next () ; $self -> PopCurrRec ; return $more ; # && (ref $more) && keys (%$more) > 0 ; } ## ---------------------------------------------------------------------------- ## ## PrevNextForm ## ## ## $textprev = Text for previous button ## $textnext = Text for next button ## \%fdat = fields/values for select where ## ## sub PrevNextForm { my ($self, $textprev, $textnext, $fdat) = @_ ; my $param = $textprev ; my $textfirst ; my $textlast ; my $textgoto ; if (ref $textprev eq 'HASH') { $fdat = $textnext ; $textprev = $param -> {'-prev'} ; $textnext = $param -> {'-next'} ; $textfirst = $param -> {'-first'} ; $textlast = $param -> {'-last'} ; $textgoto = $param -> {'-goto'} ; } my $more = $self -> MoreRecords (1) ; my $start = $self -> {'*StartRecordNo'} ; my $max = $self -> {'*FetchMax'} - $self -> {'*FetchStart'} + 1 ; my $esc = '' ; $esc = '\\' if ((defined ($HTML::Embperl::escmode) && ($HTML::Embperl::escmode & 1)) || (defined ($Embperl::escmode) && ($Embperl::escmode & 1))) ; my $buttons = "$esc$esc\n$esc\n" ; my $k ; my $v ; if ($fdat) { while (($k, $v) = each (%$fdat)) { if (substr ($k, 0, 1) eq '\\') { $k = '\\' . $k ; } if ($k ne '$start' && $k ne '$max' && $k ne '$prev' && $k ne '$next' && $k ne '$goto' && $k ne '$gotorow' && $k ne '$first' && $k ne '$last') { $buttons .= "$esc\n" ; } } } if ($start > 0 && $textfirst) { $buttons .= "$esc " ; } if ($start > 0 && $textprev) { $buttons .= "$esc " ; } if ($textgoto) { $buttons .= "$esc" ; $buttons .= "$esc " ; } if ($more > 0 && $textnext) { $buttons .= "$esc " ; } if ($more > 0 && $textlast) { $buttons .= "$esc" ; } $buttons .= "$esc" ; return $buttons ; } ########################################################################################## 1; package DBIx::Recordset::CurrRow ; use Carp ; ## ---------------------------------------------------------------------------- ## ## TIEHASH ## ## tie an hash to the object, object must be aready blessed ## ## tie %self, 'DBIx::Recordset::CurrRow', $self ; ## sub TIEHASH { my ($class, $arg) = @_ ; my $rs ; if (ref ($arg) eq 'HASH') { $rs = DBIx::Recordset -> SetupObject ($arg) or return undef ; } elsif (ref ($arg) eq 'DBIx::Recordset') { $rs = $arg ; } else { croak ("Need DBIx::Recordset or setup parameter") ; } my $self = {'*Recordset' => $rs} ; bless ($self, $class) ; return $self ; } ## ---------------------------------------------------------------------------- ## ## Fetch the data from a previous SQL Select ## ## $fetch = Column to fetch ## ## sub FETCH () { # if (wantarray) # { # my @result ; # my $rs = $_[0] -> {'*Recordset'} ; # $rs -> PushCurrRec ; # my $rec = $rs -> First () ; # while ($rec) # { ## push @result, tied (%$rec) -> FETCH ($_[1]) ; # push @result, $rec -> {$_[1]} ; # $rec = $rs -> Next () ; # } # $rs -> PopCurrRec ; # return @result ; # } # else { my $rec = $_[0] -> {'*Recordset'} -> Curr ; if (defined ($rec)) { my $obj ; return $obj -> FETCH ($_[1]) if ($obj = tied (%$rec)) ; return $rec -> {$_[1]} ; } return undef ; } } ## ---------------------------------------------------------------------------- sub STORE () { if (ref $_[2] eq 'ARRAY') { # array my ($self, $key, $dat) = @_ ; my $rs = $self -> {'*Recordset'} ; $rs -> PushCurrRec ; my $rec = $rs -> First (1) ; my $i = 0 ; while ($rec) { tied (%$rec) -> STORE ($key, $$dat[$i++]) ; last if ($i > $#$dat) ; $rec = $rs -> Next (1) ; } $rs -> PopCurrRec ; } else { tied (%{$_[0] -> {'*Recordset'} -> Curr (1)}) -> STORE ($_[1], $_[2]) ; } } ## ---------------------------------------------------------------------------- sub FIRSTKEY { my $rec = $_[0] -> {'*Recordset'} -> Curr ; my $obj = tied (%{$rec}) ; return tied (%{$rec}) -> FIRSTKEY if ($obj) ; my $k = keys %$rec ; return each %$rec ; } ## ---------------------------------------------------------------------------- sub NEXTKEY { my $rec = $_[0] -> {'*Recordset'} -> Curr ; my $obj = tied (%{$rec}) ; return tied (%{$rec}) -> NEXTKEY if ($obj) ; return each %$rec ; } ## ---------------------------------------------------------------------------- sub EXISTS { return exists ($_[0] -> {'*Recordset'} -> Curr -> {$_[1]}) ; } ## ---------------------------------------------------------------------------- sub DELETE { carp ("Cannot DELETE a field from a database record") ; } ## ---------------------------------------------------------------------------- sub CLEAR ($) { #carp ("Cannot DELETE all fields from a database record") ; } ## ---------------------------------------------------------------------------- sub DESTROY { my $self = shift ; my $orgerr = $@ ; local $@ ; eval { $self -> {'*Recordset'} -> ReleaseRecords () if (defined ($self -> {'*Recordset'})) ; { local $^W = 0 ; print DBIx::Recordset::LOG "DB: ::CurrRow::DESTROY\n" if ($self -> {'*Recordset'} -> {'*Debug'} > 3) ; } } ; $self -> savecroak ($@) if (!$orgerr && $@) ; warn $@ if ($orgerr && $@) ; } ########################################################################################## package DBIx::Recordset::Hash ; use Carp ; ## ---------------------------------------------------------------------------- ## ## PreFetch ## ## Prefetch data ## ## sub PreFetch { my ($self, $rs) = @_ ; my $where = $self -> {'*PreFetch'} ; my %keyhash ; my $rec ; my $merge = $self -> {'*MergeFunc'} ; my $pk ; $rs -> Search ($where eq '*'?undef:$where) or return undef ; my $primkey = $rs -> {'*PrimKey'} or $rs -> savecroak ('Need !PrimKey') ; while ($rec = $rs -> Next) { $pk = $rec -> {$primkey} ; if ($merge && exists ($keyhash{$pk})) { if (tied (%{$keyhash{$pk}})) { my %data = %{$keyhash{$pk}} ; $keyhash{$pk} = \%data ; } &$merge ($keyhash{$pk}, $rec) ; } else { $keyhash{$pk} = $rec ; } } $self -> {'*KeyHash'} = \%keyhash ; $self -> {'*ExpiresTime'} = time + $self -> {'*Expires'} if ($self -> {'*Expires'} > 0) ; } ## ---------------------------------------------------------------------------- ## ## PreFetchIfExpires ## ## Prefetch data ## ## sub PreFetchIfExpires { my ($self, $rs) = @_ ; my $prefetch; if (ref ($self -> {'*Expires'}) eq 'CODE') { $prefetch = $self -> {'*Expires'}->($self); } elsif (defined ($self -> {'*ExpiresTime'})) { $prefetch = $self -> {'*ExpiresTime'} < time } $self -> PreFetch ($rs) if $prefetch; } ## ---------------------------------------------------------------------------- ## ## TIEHASH ## ## tie an hash to the object, object must be aready blessed ## ## tie %self, 'DBIx::Recordset::Hash', $self ; ## sub TIEHASH { my ($class, $arg) = @_ ; my $rs ; my $keyhash ; my $self ; if (ref ($arg) eq 'HASH') { $self = { '*Expires' => $arg -> {'!Expires'}, '*PreFetch' => $arg -> {'!PreFetch'}, '*MergeFunc' => $arg -> {'!MergeFunc'}, } ; $rs = DBIx::Recordset -> SetupObject ($arg) or return undef ; } elsif (ref ($arg) eq 'DBIx::Recordset') { $rs = $arg ; $self = {} ; } else { croak ("Need DBIx::Recordset or setup parameter") ; } $self -> {'*Recordset'} = $rs ; bless ($self, $class) ; $self -> PreFetch ($rs) if ($self -> {'*PreFetch'}) ; return $self ; } ## ---------------------------------------------------------------------------- ## ## Fetch the data from a previous SQL Select ## ## $fetch = PrimKey for Row to fetch ## ## sub FETCH { my ($self, $fetch) = @_ ; my $rs = $self->{'*Recordset'} ; return $rs-> {'*UndefKey'} if (!defined ($fetch)) ; # undef could be used as key for autoincrement values my $h ; if ($self -> {'*PreFetch'}) { $self -> PreFetchIfExpires ($rs) ; $h = $self -> {'*KeyHash'} -> {$fetch} ; } else { print DBIx::Recordset::LOG "DB: Hash::FETCH \{" . (defined ($fetch)?$fetch:'') ."\}\n" if ($rs->{'*Debug'} > 3) ; if (!defined ($rs->{'*LastKey'}) || $fetch ne $rs->{'*LastKey'}) { $rs->SQLSelect ("$rs->{'*PrimKey'} = ?", undef, undef, undef, undef, [$fetch], [$rs->{'*Type4Field'}{$rs->{'*PrimKey'}}]) or return undef ; $h = $rs -> FETCH (0) ; my $merge = $self -> {'*MergeFunc'} ; $self -> {'*LastMergeRec'} = undef ; if ($merge && $rs -> MoreRecords) { my %data = %$h ; my $rec ; my $i = 1 ; while ($rec = $rs -> FETCH($i++)) { &$merge (\%data, $rec) ; } $self -> {'*LastMergeRec'} = $h = \%data ; } } else { if ($self -> {'*LastMergeRec'}) { $h = $self -> {'*LastMergeRec'} } else { $h = $rs -> Curr ; } } } print DBIx::Recordset::LOG "DB: Hash::FETCH return " . (defined ($h)?$h:'') . "\n" if ($rs->{'*Debug'} > 3) ; return $h ; } ## ---------------------------------------------------------------------------- ## ## store something in the hash ## ## $key = PrimKey for Row to fetch ## $value = Hashref with row data ## sub STORE { my ($self, $key, $value) = @_ ; my $rs = $self -> {'*Recordset'} ; print DBIx::Recordset::LOG "DB: ::Hash::STORE \{" . (defined ($key)?$key:'') . "\} = " . (defined ($value)?$value:'') . "\n" if ($rs->{'*Debug'} > 3) ; $rs -> savecroak ("Hash::STORE need hashref as value") if (!ref ($value) eq 'HASH') ; #$rs -> savecroak ("Hash::STORE doesn't work with !PreFetch") if ($self -> {'*PreFetch'}) ; return if ($self -> {'*PreFetch'}) ; my %dat = %$value ; # save values, if any $dat{$rs -> {'*PrimKey'}} = $key ; # setup primary key value %$value = () ; # clear out data in tied hash my $r = tie %$value, 'DBIx::Recordset::Row', $rs, \%dat, undef, 1 ; #$r -> STORE ($rs -> {'*PrimKey'}, $key) ; #$r -> {'*new'} = 1 ; # setup recordset $rs-> ReleaseRecords ; $DBIx::Recordset::Data{$rs-> {'*Id'}}[0] = $value ; $rs-> {'*UndefKey'} = defined($key)?undef:$value ; $rs-> {'*LastKey'} = $key ; $rs-> {'*CurrRow'} = 1 ; $rs-> {'*LastRow'} = 0 ; } ## ---------------------------------------------------------------------------- sub FIRSTKEY { my $self = shift ; my $rs = $self->{'*Recordset'} ; my $primkey = $rs->{'*PrimKey'} ; if ($self -> {'*PreFetch'}) { $self -> PreFetchIfExpires ($rs) ; my $keyhash = $self -> {'*KeyHash'} ; my $foo = keys %$keyhash ; # reset iterator return each %$keyhash ; } $rs->SQLSelect () or return undef ; my $dat = $rs -> First (0) or return undef ; my $key = $dat -> {$rs->{'*PrimKey'}} ; if ($rs->{'*Debug'} > 3) { print DBIx::Recordset::LOG "DB: Hash::FIRSTKEY \{" . (defined ($key)?$key:'') . "\}\n" ; } return $key ; } ## ---------------------------------------------------------------------------- sub NEXTKEY { my $self = shift ; my $rs = $self->{'*Recordset'} ; if ($self -> {'*PreFetch'}) { ##$self -> PreFetchIfExpires ($rs) ; my $keyhash = $self -> {'*KeyHash'} ; return each %$keyhash ; } my $dat = $rs -> Next () or return undef ; my $key = $dat -> {$rs->{'*PrimKey'}} ; if ($rs->{'*Debug'} > 3) { print DBIx::Recordset::LOG "DB: Hash::NEXTKEY \{" . (defined ($key)?$key:'') . "\}\n" ; } return $key ; } ## ---------------------------------------------------------------------------- sub EXISTS { my ($self, $key) = @_ ; if ($self -> {'*PreFetch'}) { my $rs = $self->{'*Recordset'} ; $self -> PreFetchIfExpires ($rs) ; my $keyhash = $self -> {'*KeyHash'} ; return exists ($keyhash -> {$key}) ; } return defined ($self -> FETCH ($key)) ; } ## ---------------------------------------------------------------------------- sub DELETE { my ($self, $key) = @_ ; my $rs = $self -> {'*Recordset'} ; $rs->{'*LastKey'} = undef ; $rs->SQLDelete ("$rs->{'*PrimKey'} = ?", [$key], [$rs->{'*Type4Field'}{$rs->{'*PrimKey'}}]) or return undef ; return 1 ; } ## ---------------------------------------------------------------------------- sub CLEAR { my ($self, $key) = @_ ; my $rs = $self -> {'*Recordset'} ; $rs->SQLDelete ('') or return undef ; } ## ---------------------------------------------------------------------------- ## ## Dirty - see if there are unsaved changes ## sub Dirty { return $_[0]->{'*Recordset'}->Dirty() } ## ---------------------------------------------------------------------------- sub Flush { $_[0]->{'*Recordset'} -> Flush () ; } ## ---------------------------------------------------------------------------- sub DESTROY { my $self = shift ; my $orgerr = $@ ; local $@ ; eval { $self -> {'*Recordset'} -> ReleaseRecords () if (defined ($self -> {'*Recordset'})) ; { local $^W = 0 ; print DBIx::Recordset::LOG "DB: ::Hash::DESTROY\n" if ($self -> {'*Recordset'} -> {'*Debug'} > 3) ; } } ; $self -> savecroak ($@) if (!$orgerr && $@) ; warn $@ if ($orgerr && $@) ; } ########################################################################################## package DBIx::Recordset::Access ; use overload 'bool' => sub { 1 }, '%{}' => \&gethash, '@{}' => \&getarray ; #, '${}' => \&getscalar ; sub new { my $class = shift; my $arg = shift ; bless $arg, $class; } sub gethash { my $self = shift ; return \%$$self ; } sub getarray { my $self = shift ; return \@$$self ; } sub getscalar { my $self = shift ; return \$$$self ; } ########################################################################################## package DBIx::Recordset::Row ; use Carp ; sub TIEHASH { my ($class, $rs, $names, $dat, $new) = @_ ; my $self = {'*Recordset' => $rs} ; my $data = $self -> {'*data'} = {} ; my $upd = $self -> {'*upd'} = {} ; bless ($self, $class) ; if (ref ($names) eq 'HASH') { my $v ; my $k ; if ($new) { my $dirty = 0 ; $self->{'*new'} = 1 ; # mark it as new record my $lk ; while (($k, $v) = each (%$names)) { $lk = $DBIx::Recordset::PreserveCase?$k:lc ($k) ; # store the value and remeber it for later update $upd ->{$lk} = \($data->{$lk} = $v) ; $dirty = 1 ; } $self->{'*dirty'} = $dirty ; # mark it as dirty only if data exists } else { while (($k, $v) = each (%$names)) { $data -> {$DBIx::Recordset::PreserveCase?$k:lc ($k)} = $v ; } } } else { my $i = 0 ; my $of ; my $ofunc = $rs -> {'*OutputFuncArray'} || [] ; my $linkname = $rs -> {'*LinkName'} ; if ($rs -> {'*KeepFirst'}) { $i = -1 ; %$data = () ; if ($dat) { foreach my $k (@$dat) { $i++ ; my $hkey = ($DBIx::Recordset::PreserveCase?$$names[$i]:lc($$names[$i])) ; #warn "hkey = $hkey data = $k\n" ; $data -> {$hkey} = ($ofunc->[$i]?(&{$ofunc->[$i]}($k)):$k) if (!exists $data -> {$hkey}) ; } } } elsif ($linkname < 2) { $i = -1 ; %$data = map { $i++ ; ($DBIx::Recordset::PreserveCase?$$names[$i]:lc($$names[$i])) => ($ofunc->[$i]?(&{$ofunc->[$i]}($_)):$_) } @$dat if ($dat) ; } elsif ($linkname < 3) { my $r ; my $repl = $rs -> {'*ReplaceFields'} ; my $n ; foreach $r (@$repl) { $n = $DBIx::Recordset::PreserveCase?$names -> [$i]:lc ($names -> [$i]) ; $of = $ofunc -> [$i] ; $data -> {$n} = ($of?(&{$of}($dat->[$i])):$dat->[$i]) ; $data -> {uc($n)} = join (' ', map ({ ($ofunc->[$_]?(&{$ofunc->[$_]}($dat->[$_])):$dat->[$_])} @$r)) if ($#$r > 0 || $r -> [0] != $i) ; $i++ ; } } else { my $r ; my $repl = $rs -> {'*ReplaceFields'} ; foreach $r (@$repl) { $data -> {($DBIx::Recordset::PreserveCase?$$names[$i]:lc($$names[$i]))} = join (' ', map ({ ($ofunc->[$_]?(&{$ofunc->[$_]}($dat->[$_])):$dat->[$_])} @$r)) ; #print LOG "###repl $r -> $data->{$$names[$i]}\n" ; $i++ ; } } $self -> {'*Recordset'} = $rs ; } if (!$new) { my $pk = $rs -> {'*PrimKey'} ; if ($pk && exists ($data -> {$pk})) { $self -> {'*PrimKeyOrgValue'} = $data -> {$pk} ; } else { # save whole record for usage as key in later update %{$self -> {'*org'}} = %$data ; $self -> {'*PrimKeyOrgValue'} = $self -> {'*org'} ; } } return $self ; } ## ---------------------------------------------------------------------------- sub STORE { my ($self, $key, $value) = @_ ; my $rs = $self -> {'*Recordset'} ; my $dat = $self -> {'*data'} ; local $^W = 0 ; print DBIx::Recordset::LOG "DB: Row::STORE $key = $value\n" if ($rs->{'*Debug'} > 3) ; # any changes? if ($dat -> {$key} ne $value || defined ($dat -> {$key}) != defined($value)) { # store the value and remeber it for later update $self -> {'*upd'}{$key} = \($dat -> {$_[1]} = $value) ; $self -> {'*dirty'} = 1 ; # mark row dirty } } ## ---------------------------------------------------------------------------- sub FETCH { my ($self, $key) = @_ ; return undef if (!$key) ; my $rs = $self -> {'*Recordset'} ; my $data = $self -> {'*data'}{$key} ; my $link ; if (!defined($data)) { if ($key eq '!Name') { my $nf = $rs -> {'*NameField'} || $rs -> TableAttr ('!NameField') ; if (!ref $nf) { return $self -> {'*data'}{$key} = $self -> {'*data'}{uc($nf)} || $self -> {'*data'}{$nf} ; } return $self -> {'*data'}{$key} = join (' ', map { $self -> {'*data'}{uc ($_)} || $self -> {'*data'}{$_} } @$nf) ; } elsif (defined ($link = $rs -> {'*Links'}{$key})) { my $lf = $link -> {'!LinkedField'} ; my $dat = $self -> {'*data'} ; my $mv ; if (exists ($dat -> {$link -> {'!MainField'}})) { $mv = $dat -> {$link -> {'!MainField'}} ; } else { $mv = $dat -> {"$link->{'!MainTable'}.$link->{'!MainField'}"} ; } if ($link -> {'!UseHash'}) { my $linkset = $rs -> {'*LinkSet'}{$key} ; if (!$linkset) { my $setup = {%$link} ; $setup -> {'!PrimKey'} = $lf ; $setup -> {'!DataSource'} = $rs if (!defined ($link -> {'!DataSource'})) ; my %linkset ; print DBIx::Recordset::LOG "DB: Row::FETCH $key = Setup New Recordset for table $link->{'!Table'}, $lf = " . (defined ($mv)?$mv:'') . "\n" if ($rs->{'*Debug'} > 3) ; $rs -> {'*LinkSet'}{$key} = $linkset = tie %linkset, 'DBIx::Recordset::Hash', $setup ; } $data = $linkset -> FETCH ($mv) ; } else { my $linkkey = "$key-$lf-$mv" ; my $linkset = $rs -> {'*LinkSet'}{$linkkey} ; if (!$linkset) { my $setup = {%$link} ; $setup -> {$lf} = $mv ; $setup -> {'!Default'} = { $lf => $mv } ; $setup -> {'!DataSource'} = $rs if (!defined ($link -> {'!DataSource'})) ; print DBIx::Recordset::LOG "DB: Row::FETCH $key = Setup New Recordset for table $link->{'!Table'}, $lf = " . (defined ($mv)?$mv:'') . "\n" if ($rs->{'*Debug'} > 3) ; $linkset = DBIx::Recordset -> Search ($setup) ; $data = $self -> {'*data'}{$key} = DBIx::Recordset::Access -> new(\$linkset) ; if ($link -> {'!Cache'}) { $rs -> {'*LinkSet'}{$linkkey} = $linkset ; } } else { $$linkset -> Reset ; $data = DBIx::Recordset::Access -> new(\$linkset) ; } } my $of = $rs -> {'*OutputFunctions'}{$key} ; $data = &{$of}($data) if ($of) ; } } if ($rs && $rs->{'*Debug'} > 3) { local $^W=0;print DBIx::Recordset::LOG "DB: Row::FETCH " . (defined ($key)?$key:'') . " = <" . (defined ($data)?$data:'') . ">\n" } ; return $data ; } ## ---------------------------------------------------------------------------- sub FIRSTKEY { my ($self) = @_ ; my $a = scalar keys %{$self -> {'*data'}}; return each %{$self -> {'*data'}} ; } ## ---------------------------------------------------------------------------- sub NEXTKEY { return each %{$_[0] -> {'*data'}} ; } ## ---------------------------------------------------------------------------- sub EXISTS { exists ($_[0]->{'*data'}{$_[1]}) ; } ## ---------------------------------------------------------------------------- sub DELETE { carp ("Cannot DELETE a field from a database record") ; } ## ---------------------------------------------------------------------------- sub CLEAR ($) { #carp ("Cannot DELETE all fields from a database record") ; } ## ---------------------------------------------------------------------------- ## ## report the cleanless of the row ## sub Dirty { return $_[0]->{'*dirty'} } ## ---------------------------------------------------------------------------- ## ## Flush data to database if row is dirty ## sub Flush { my $self = shift ; my $rs = $self -> {'*Recordset'} ; return 1 if (!$rs) ; if ($self -> {'*dirty'}) { my $rc ; print DBIx::Recordset::LOG "DB: Row::Flush id=$rs->{'*Id'} $self\n" if ($rs->{'*Debug'} > 3) ; my $dat = $self -> {'*upd'} ; if ($self -> {'*new'}) { $rc = $rs -> Insert ($dat) ; } else { my $pko ; my $pk = $rs -> {'*PrimKey'} ; $dat->{$pk} = \($self -> {'*data'}{$pk}) if ($pk && !exists ($dat->{$pk})) ; #carp ("Need primary key to update record") if (!exists($self -> {"=$pk"})) ; if (!exists($self -> {'*PrimKeyOrgValue'})) { $rc = $rs -> Update ($dat) ; } elsif (ref ($pko = $self -> {'*PrimKeyOrgValue'}) eq 'HASH') { $rc = $rs -> Update ($dat, $pko) ; } else { $rc = $rs -> Update ($dat, {$pk => $pko} ) ; } if ($rc != 1 && $rc ne '') { # must excatly be one row! print DBIx::Recordset::LOG "DB: ERROR: Row Update has updated $rc rows instead of one ($rs->{'*LastSQLStatement'})\n" if ($rs->{'*Debug'}) ; #$rs -> savecroak ("DB: ERROR: Row Update has updated $rc rows instead of one ($rs->{'*LastSQLStatement'})") ; } } delete $self -> {'*new'} ; delete $self -> {'*dirty'} ; $self -> {'*upd'} = {} ; } my $k ; my $v ; my $lrs ; my $rname ; # "each" is not reentrant !!!!!!!!!!!!!! #while (($k, $v) = each (%{$rs -> {'*Links'}})) foreach $k (keys %{$rs -> {'*Links'}}) { # Flush linked tables if ($lrs = $self->{'*data'}{$k}) { $rname = '' ; $rname = eval {ref ($$lrs)} || '' ; ${$lrs} -> Flush () if ($rname eq 'DBIx::Recordset') ; #if (defined ($lrs) && ref ($lrs) && defined ($$lrs) && ) ; } } return 1 ; } ## ---------------------------------------------------------------------------- sub DESTROY { my $self = shift ; my $orgerr = $@ ; local $@ ; eval { { local $^W = 0 ; print DBIx::Recordset::LOG "DB: Row::DESTROY\n" if ($DBIx::Recordset::Debug > 2 || $self -> {'*Recordset'} -> {'*Debug'} > 3) ; } $self -> Flush () ; } ; if (!$orgerr && $@) { Carp::croak $@ ; } elsif ($orgerr && $@) { warn $@ ; } } ################################################################################ 1; __END__ =pod =head1 NAME DBIx::Recordset - Perl extension for DBI recordsets =head1 SYNOPSIS use DBIx::Recordset; # Setup a new object and select some recods... *set = DBIx::Recordset -> Search ({'!DataSource' => 'dbi:Oracle:....', '!Table' => 'users', '$where' => 'name = ? and age > ?', '$values' => ['richter', 25] }) ; # Get the values of field foo ... print "First Records value of foo is $set[0]{foo}\n" ; print "Second Records value of foo is $set[1]{foo}\n" ; # Get the value of the field age of the current record ... print "Age is $set{age}\n" ; # Do another select with the already created object... $set -> Search ({name => 'bar'}) ; # Show the result... print "All users with name bar:\n" ; while ($rec = $set -> Next) { print $rec -> {age} ; } # Setup another object and insert a new record *set2 = DBIx::Recordset -> Insert ({'!DataSource' => 'dbi:Oracle:....', '!Table' => 'users', 'name' => 'foo', 'age' => 25 }) ; # Update this record (change age from 25 to 99)... $set -> Update ({age => 99}, {name => 'foo'}) ; =head1 DESCRIPTION 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. B uses 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). Most public functions take a hash reference as parameter, which makes it simple to supply various different arguments to the same function. The parameter hash can also be taken from a hash containing posted formfields like those available with CGI.pm, mod_perl, HTML::Embperl and others. Before using a recordset it is necessary to setup an object. Of course the setup step can be made with the same function call as the first database access, but it can also be handled separately. Most functions which set up an object return a B. A typglob in Perl is an object which holds pointers to all datatypes with the same name. Therefore a typglob must always have a name and B be declared with B. You can only use it as B variable or declare it with B. The trick for using a typglob is that setup functions can return a B, an B and a B at the same time. The object is used to access the object's methods, the array is used to access the records currently selected in the recordset and the hash is used to access the current record. If you don't like the idea of using typglobs you can also set up the object, array and hash separately, or just set the ones you need. =head1 ARGUMENTS Since most methods take a hash reference as argument, here is a description of the valid arguments first. =head2 Setup Parameters All parameters starting with an '!' are only recognized at setup time. If you specify them in later function calls they will be ignored. You can also preset these parameters with the TableAttr method of DBIx::Database. This allows you to presetup most parameters for the whole database and they will be use every time you create a new DBIx::Recordset object, without specifing it every time. =item B Specifies the database to which to connect. This information can be given in the following ways: =over 4 =item Driver/DB/Host. Same as the first parameter to the DBI connect function. =item DBIx::Recordset object Takes the same database handle as the given DBIx::Recordset object. =item DBIx::Database object Takes Driver/DB/Host from the given database object. See L for details about DBIx::Database object. When using more then one Recordset object, this is the most efficient method. =item DBIx::Datasbase object name Takes Driver/DB/Host from the database object which is saved under the given name ($saveas parameter to DBIx::Database -> new) =item an DBI database handle Uses given database handle. =back =item B Tablename. Multiple tables are comma-separated. =item B Username. Same as the second parameter to the DBI connect function. =item B Password. Same as the third parameter to the DBI connect function. =item B Reference to a hash which holds the attributes for the DBI connect function. See perldoc DBI for a detailed description. =item B Fields which should be returned by a query. If you have specified multiple tables the fieldnames should be unique. If the names are not unique you must specify them along with the tablename (e.g. tab1.field). NOTE 1: Fieldnames specified with !Fields can't be overridden. If you plan to use other fields with this object later, use $Fields instead. NOTE 2: The keys for the returned hash normally don't have a table part. Only the fieldname part forms the key. (See !LongNames for an exception.) NOTE 3: Because the query result is returned in a hash, there can only be one out of multiple fields with the same name fetched at once. If you specify multiple fields with the same name, only one is returned from a query. Which one this actually is depends on the DBD driver. (See !LongNames for an exception.) NOTE 4: Some databases (e.g. mSQL) require you to always qualify a fieldname with a tablename if more than one table is accessed in one query. =item B The TableFilter parameter specifies which tables should be honoured when DBIx::Recordset searches for links between tables (see below). When given as parameter to DBIx::Database it filters for which tables DBIx::Database retrieves metadata. Only thoses tables are used which starts with prefix given by C. Also the DBIx::Recordset link detection tries to use this value as a prefix of table names, so you can leave out this prefix when you write a fieldname that should be detected as a link to another table. =item B When set to 1, the keys of the hash returned for each record not only consist of the fieldnames, but are built in the form table.field. =item B Fields which should be used for ordering any query. If you have specified multiple tables the fieldnames should be unique. If the names are not unique you must specify them among with the tablename (e.g. tab1.field). NOTE 1: Fieldnames specified with !Order can't be overridden. If you plan to use other fields with this object later, use $order instead. =item B Condition which describes the relation between the given tables (e.g. tab1.id = tab2.id) (See also L.) Example '!Table' => 'tab1, tab2', '!TabRelation' => 'tab1.id=tab2.id', 'name' => 'foo' This will generate the following SQL statement: SELECT * FROM tab1, tab2 WHERE name = 'foo' and tab1.id=tab2.id ; =item B !TabJoin allows you to specify an B which is used in a B or a B can be accessed in two ways: 1.) Through an array. Each item of the array corresponds to one of the selected records. Each array-item is a reference to a hash containing an entry for every field. Example: $set[1]{id} access the field 'id' of the second record found $set[3]{name} access the field 'name' of the fourth record found The record is fetched from the DBD driver when you access it the first time and is stored by DBIx::Recordset for later access. If you don't access the records one after each other, the skipped records are not stored and therefore can't be accessed anymore, unless you specify the B parameter. 2.) DBIx::Recordset holds a B which can be accessed directly via a hash. The current record is the one you last accessed via the array. After a Select or Search, it is reset to the first record. You can change the current record via the methods B, B, B, B. Example: $set{id} access the field 'id' of the current record $set{name} access the field 'name' of the current record Instead of doing a B, the only way to really determine the number of selected rows would be to fetch them all from the DBMS. Since this could cause a lot of work, it may be very inefficent. Therefore I by default calls die() when Perl calls FETCHSIZE. If you know your DBD drivers returns the correct value in C<$sth> -> C after the execution of an C
; $set -> Select ({'id'=>2}) ; is the same as $set1 -> Select ('id=2') ; SELECT * from
WHERE id = 2 ; $set -> Search({ '$fields' => 'id, balance AS paid - total ' }) ; SELECT id, balance AS paid - total FROM
$set -> Select ({name => "Second Name\tFirst Name"}) ; SELECT * from
WHERE name = 'Second Name' or name = 'First Name' ; $set1 -> Select ({value => "9991 9992\t9993", '$valuesplit' => ' |\t'}) ; SELECT * from
WHERE value = 9991 or value = 9992 or value = 9993 ; $set -> Select ({'+name&value' => "9992"}) ; SELECT * from
WHERE name = '9992' or value = 9992 ; $set -> Select ({'+name&value' => "Second Name\t9991"}) ; SELECT * from
WHERE (name = 'Second Name' or name = '9991) or (value = 0 or value = 9991) ; $set -> Search ({id => 1,name => 'First Name',addon => 'Is'}) ; SELECT * from
WHERE id = 1 and name = 'First Name' and addon = 'Is' ; $set1 -> Search ({'$start'=>0,'$max'=>2, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; SELECT * from
ORDER BY id ; B Because of the B and B only records 0,1 will be returned $set1 -> Search ({'$start'=>0,'$max'=>2, '$next'=>1, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; SELECT * from
ORDER BY id ; B Because of the B, B and B only records 2,3 will be returned $set1 -> Search ({'$start'=>2,'$max'=>1, '$prev'=>1, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; SELECT * from
ORDER BY id ; B Because of the B, B and B only records 0,1,2 will be returned $set1 -> Search ({'$start'=>5,'$max'=>5, '$next'=>1, '$order'=>'id'}) or die "not ok ($DBI::errstr)" ; SELECT * from
ORDER BY id ; B Because of the B, B and B only records 5-9 will be returned *set6 = DBIx::Recordset -> Search ({ '!DataSource' => "dbi:$Driver:$DB", '!Table' => "t1, t2", '!TabRelation' => "t1.value=t2.value", '!Fields' => 'id, name, text', 'id' => "2\t4" }) or die "not ok ($DBI::errstr)" ; SELECT id, name, text FROM t1, t2 WHERE (id=2 or id=4) and t1.value=t2.value ; $set6 -> Search ({'name' => "Fourth Name" }) or die "not ok ($DBI::errstr)" ; SELECT id, name, text FROM t1, t2 WHERE (name = 'Fourth Name') and t1.value=t2.value ; $set6 -> Search ({'id' => 3, '$operator' => '<' }) or die "not ok ($DBI::errstr)" ; SELECT id, name, text FROM t1, t2 WHERE (id < 3) and t1.value=t2.value ; $set6 -> Search ({'id' => 4, 'name' => 'Second Name', '*id' => '<', '*name' => '<>' }) or die "not ok ($DBI::errstr)" ; SELECT id, name, text FROM t1, t2 WHERE (id<4 and name <> 'Second Name') and t1.value=t2.value ; $set6 -> Search ({'id' => 2, 'name' => 'Fourth Name', '*id' => '<', '*name' => '=', '$conj' => 'or' }) or die "not ok ($DBI::errstr)" ; SELECT id, name, text FROM t1, t2 WHERE (id<2 or name='Fourth Name') and t1.value=t2.value ; $set6 -> Search ({'+id|addon' => "7\tit", 'name' => 'Fourth Name', '*id' => '<', '*addon' => '=', '*name' => '<>', '$conj' => 'and' }) or die "not ok ($DBI::errstr)" ; SELECT id, name, text FROM t1, t2 WHERE (t1.value=t2.value) and ( ((name <> Fourth Name)) and ( ( id < 7 or addon = 7) or ( id < 0 or addon = 0))) $set6 -> Search ({'+id|addon' => "6\tit", 'name' => 'Fourth Name', '*id' => '>', '*addon' => '<>', '*name' => '=', '$compconj' => 'and', '$conj' => 'or' }) or die "not ok ($DBI::errstr)" ; SELECT id, name, text FROM t1, t2 WHERE (t1.value=t2.value) and ( ((name = Fourth Name)) or ( ( id > 6 and addon <> 6) or ( id > 0 and addon <> 0))) ; *set7 = DBIx::Recordset -> Search ({ '!DataSource' => "dbi:$Driver:$DB", '!Table' => "t1, t2", '!TabRelation' => "t1.id=t2.id", '!Fields' => 'name, typ'}) or die "not ok ($DBI::errstr)" ; SELECT name, typ FROM t1, t2 WHERE t1.id=t2.id ; %h = ('id' => 22, 'name2' => 'sqlinsert id 22', 'value2'=> 1022) ; *set9 = DBIx::Recordset -> Insert ({%h, ('!DataSource' => "dbi:$Driver:$DB", '!Table' => "$Table[1]")}) or die "not ok ($DBI::errstr)" ; INSERT INTO
(id, name2, value2) VALUES (22, 'sqlinsert id 22', 1022) ; %h = ('id' => 22, 'name2' => 'sqlinsert id 22u', 'value2'=> 2022) ; $set9 -> Update (\%h, 'id=22') or die "not ok ($DBI::errstr)" ; UPDATE
WHERE id=22 SET id=22, name2='sqlinsert id 22u', value2=2022 ; %h = ('id' => 21, 'name2' => 'sqlinsert id 21u', 'value2'=> 2021) ; *set10 = DBIx::Recordset -> Update ({%h, ('!DataSource' => "dbi:$Driver:$DB", '!Table' => "$Table[1]", '!PrimKey' => 'id')}) or die "not ok ($DBI::errstr)" ; UPDATE
WHERE id=21 SET name2='sqlinsert id 21u', value2=2021 ; %h = ('id' => 21, 'name2' => 'Ready for delete 21u', 'value2'=> 202331) ; *set11 = DBIx::Recordset -> Delete ({%h, ('!DataSource' => "dbi:$Driver:$DB", '!Table' => "$Table[1]", '!PrimKey' => 'id')}) or die "not ok ($DBI::errstr)" ; DELETE FROM
WHERE id = 21 ; *set12 = DBIx::Recordset -> Execute ({'id' => 20, '*id' => '<', '!DataSource' => "dbi:$Driver:$DB", '!Table' => "$Table[1]", '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; SELECT * FROM
WHERE id<20 ; *set13 = DBIx::Recordset -> Execute ({'=search' => 'ok', 'name' => 'Fourth Name', '!DataSource' => "dbi:$Driver:$DB", '!Table' => "$Table[0]", '!PrimKey' => 'id'}) or die "not ok ($DBI::errstr)" ; SELECT * FROM
WHERE ((name = Fourth Name)) $set12 -> Execute ({'=insert' => 'ok', 'id' => 31, 'name2' => 'insert by exec', 'value2' => 3031, # Execute should ignore the following params, since it is already setup '!DataSource' => "dbi:$Driver:$DB", '!Table' => "quztr", '!PrimKey' => 'id99'}) or die "not ok ($DBI::errstr)" ; SELECT * FROM
; $set12 -> Execute ({'=update' => 'ok', 'id' => 31, 'name2' => 'update by exec'}) or die "not ok ($DBI::errstr)" ; UPDATE
SET name2=update by exec,id=31 WHERE id=31 ; $set12 -> Execute ({'=insert' => 'ok', 'id' => 32, 'name2' => 'insert/upd by exec', 'value2' => 3032}) or die "not ok ($DBI::errstr)" ; INSERT INTO
(name2,id,value2) VALUES (insert/upd by exec,32,3032) ; $set12 -> Execute ({'=delete' => 'ok', 'id' => 32, 'name2' => 'ins/update by exec', 'value2' => 3032}) or die "not ok ($DBI::errstr)" ; DELETE FROM
WHERE id=32 ; =head1 SUPPORT As far as possible for me, support will be available via the DBI Users' mailing list. (dbi-user@fugue.com) =head1 AUTHOR G.Richter (richter@dev.ecos.de) =head1 SEE ALSO =item Perl(1) =item DBI(3) =item DBIx::Compat(3) =item HTML::Embperl(3) http://perl.apache.org/embperl/ =item Tie::DBI(3) http://stein.cshl.org/~lstein/Tie-DBI/ =cut DBIx-Recordset-0.26/MANIFEST0100644000000000000000000000037407773276730014035 0ustar rootrootChanges MANIFEST Makefile.PL test.pl Recordset.pm Database.pm Compat.pm README TODO eg/README eg/search.pl eg/search.htm Intrors.pod Recordset/FileSeq.pm Recordset/DBSeq.pm META.yml Module meta-data (added by MakeMaker)