package DBIx; #NOTE: dbix_do commits due to multiple records, dbix_do1 does NOT! require Exporter; #eval 'use Oraperl; 1' || die $@ if $] >= 5; #####my $dB; #use DBIf; use DBI; #use DBD::ODBC; @ISA = qw(Exporter); @EXPORT = qw(dbix_package dbix_set_databasetype dbix_setlog dbix_connect dbix_select dbix_do dbix_commit dbix_rollback dbix_err dbix_errstr dbix_disconnect dbix_open dbix_bind dbix_do1 dbix_fetch dbix_close dbix_autocommit dbix_setproxy dbix_fetchall dbix_fetchseq dbix_fetchnextseq); $| = 1; $calling_package = 'main'; $sysdbtype = 'Sprite'; $dbtype = $sysdbtype; $dbi_proxy = ''; $autocommit = 0; $lastdb = undef; $lasterr = undef; ###&inittypes; sub dbix_package { $calling_package = shift || 'main'; } sub dbix_set_databasetype { $dbtype = shift || $sysdbtype; } sub dbix_setlog { my ($mylogfid) = shift; $addsemicolon = shift || 0; # 0=no change to logfile; 1=append ';'; 2=convert ampresands for SQL-PLUS and append ';' close LOGFILE if ($logfid); $logfid = undef; if ($mylogfid) { if (open(LOGFILE,">>$mylogfid")) { $logfid = $mylogfid; return (1); } else { return (0); } } return (1); } sub dbix_setproxy { #$dbi_proxy = 'Proxy:hostname=$rhost;dsn=DBI:'; my $host = shift; if ($host) { $host =~ s/:/;port=/; $dbi_proxy = 'Proxy:hostname='.$host.';dsn=DBI:'; $autocommit = 1; } else { $dbi_proxy = ''; $autocommit = 0; } } sub dbix_connect { my ($connectstr) = shift; my ($attrs) = shift; my $j; my $resptr = {}; $_ = ''; $lasterr = ''; foreach my $i (keys (%$attrs)) { if ($i =~ /^dbix_/) { $j = $i; $j =~ s/^dbix_//; $resptr->{$j} = $attrs->{$i}; $ENV{"\U$j\E"} = $attrs->{$i} if ($j =~ s/^env_//); delete $attrs->{$i}; } } $dbtype = $resptr->{dbtype} if ($resptr->{dbtype}); if ($resptr->{proxy}) { $dbi_proxy = $resptr->{proxy}; $dbi_proxy =~ s/:/;port=/; $dbi_proxy = 'Proxy:hostname='.$dbi_proxy.';dsn=DBI:'; } $dbtype = $1 if ($connectstr =~ s/^(\w+)\://); $dbtype = 'ODBC' if ($dbtype =~ /odbc/i); if ($dbtype =~ /Sybase/i) { $resptr->{noplaceholders} = 1 unless(defined $resptr->{noplaceholders}); } if ($connectstr =~ s/^\s*(?:connect\=|\=)//i) { $dbtype = $1 if ($connectstr =~ /dbi\:${dbi_proxy}(\w+):/); $dbi_err = \$DBI::err; $dbi_errstr = \$DBI::errstr; ($dbuser, $dbpswd) = ($1, $2) if ($connectstr =~ s/\,(.+?)\,(.+)$//); unless ($connectstr =~ /dbi\:proxy\:/i || $dbi_proxy) { $ENV{TWO_TASK} ||= $1 if ($connectstr =~ s/\:Oracle\:(\w+)/\:Oracle\:/); $ENV{ORACLE_HOME} ||= '/home1/oracle/app/oracle/product/7.3.2' if ($connectstr =~ /\:Oracle\:/); } if ($resptr->{oracle8}) { $ENV{ORACLE_HOME} ||= '/app/oracle/product/8.0.6'; $ENV{ORA_NLS} ||= "$ENV{ORACLE_HOME}/ocommon/nls/admin/data"; $ENV{ORA_NLS32} ||= "$ENV{ORACLE_HOME}/ocommon/nls/admin/data"; } else { $ENV{ORACLE_HOME} ||= '/home1/oracle/app/oracle/product/7.3.2' if ($connectstr =~ /\:Oracle\:/); } my $dB = DBI->connect($connectstr,$dbuser,$dbpswd,$attrs); unless ($dB) { $lasterr = $_; return undef; } $resptr->{DBI} = $dB; $dB->{AutoCommit} = $attrs->{-AutoCommit} || $autocommit; $dB->{AutoCommit} = 1 if ($dbtype =~ /mysql/i || ($connectstr =~ /dbi\:proxy\:/i && $DBI::VERSION < 1.21)); $resptr->{dbtype} = $dbtype; return undef if ($DBI::err); $lastdb = $resptr; if ($dbtype =~ /Sybase/i) #ADDED 20030131 TO FIX "OUT OF MEMORY" ERRORS ON SELECTS. { #SEE http://www.peppler.org/cgi-bin/bug-cgi $resptr->{textsize} = 32767 unless (defined($resptr->{textsize})); if ($resptr->{textsize} > 0) { $resptr->{DBI}->do("set TEXTSIZE $resptr->{textsize}"); } } return $resptr; } else { my ($dbname,$dbuser,$dbpswd) = split(/,/,$connectstr); $dbi_err = \$DBI::err; $dbi_errstr = \$DBI::errstr; my $dbid; if ($dbtype =~ /oracle/i) { @dbname = split(/:/,$dbname); $dbname = 'T:' . $dbname if ($#dbname == 1); $ENV{TWO_TASK} = $dbname unless ($dbi_proxy); #if ($dbname =~ /insite/ || $resptr->{oracle8}) #WEBFARM'S SPECIFIC ORACLE-8 STUFF! if ($resptr->{oracle8}) #WEBFARM'S SPECIFIC ORACLE-8 STUFF! { $ENV{ORACLE_HOME} ||= '/app/oracle/product/8.0.6'; $ENV{ORA_NLS} ||= "$ENV{ORACLE_HOME}/ocommon/nls/admin/data"; $ENV{ORA_NLS32} ||= "$ENV{ORACLE_HOME}/ocommon/nls/admin/data"; } else { $ENV{ORACLE_HOME} ||= '/home1/oracle/app/oracle/product/7.3.2'; } $dbid = "dbi:${dbi_proxy}Oracle:"; $dbid .= $dbname if ($dbi_proxy); } else { $dbid = "dbi:${dbi_proxy}$dbtype:$dbname" } my $dB = DBI->connect($dbid,$dbuser,$dbpswd,$attrs); unless ($dB) { $lasterr = $_; return undef; } $resptr->{DBI} = $dB; if ($dbtype =~ /mysql/i || ($dbi_proxy && $DBI::VERSION < 1.21)) { $dB->{AutoCommit} = 1; } else { $dB->{AutoCommit} = $attrs->{-AutoCommit} || $autocommit; } $resptr->{dbtype} = $dbtype; return undef if ($DBI::err); $lastdb = $resptr; if ($dbtype =~ /Sybase/i) #ADDED 20030131 TO FIX "OUT OF MEMORY" ERRORS ON SELECTS. { #SEE http://www.peppler.org/cgi-bin/bug-cgi $resptr->{textsize} = 32767 unless (defined($resptr->{textsize})); if ($resptr->{textsize} > 0) { $resptr->{DBI}->do("set TEXTSIZE $resptr->{textsize}"); } } return $resptr; } return undef; } sub dbix_disconnect { return shift->{DBI}->disconnect; } sub dbix_select { my ($resptr, $sqlstr, @bindvals) = @_; #bindvals ADDED 20000217! local ($_); my ($i, @res, $mycsr, $myexe, @reflistv, @refsclrv); my ($fetchcnt) = 0; my (@myline) = (); my ($cleansql) = $sqlstr; $lastdb = $resptr; #$nores = 1 if ($cleansql =~ s/\binto\b//i); $cleansql =~ s/\binto\b//i; #$nores = 1 unless (wantarray); #20000425: CHANGED 2 NEXT LINE (FIX BUG)! $nores = (wantarray ? 0 : 1); $cleansql =~ s/\:[^\s\,\:]+\,?//g; #if ($mycsr = &ora_open($resptr, $cleansql)) if ($resptr->{noplaceholders}) { $cleansql =~ s/([\'\"])([^\1]*?)\1/ my ($quote) = $1; my ($str) = $2; $str =~ s|\?|\x02\^2jSpR1tE\x02|g; #PROTECT ?'S IN QUOTES. "$quote$str$quote" /egs; my $t; while (@bindvals) { $t = shift(@bindvals); $t =~ s/\'/\'\'/gs; $t =~ s/\?/\x02\^2jSpR1tE\x02/gs; $cleansql =~ s/\?/\'$t\'/s; } $cleansql =~ s/\x02\^2jSpR1tE\x02/\?/gs; } if ($mycsr = $resptr->{DBI}->prepare($cleansql)) { if ($myexe = $mycsr->execute(@bindvals)) { if ($sqlstr =~ /\binto\b/i) { my (@varlistv) = ($sqlstr =~ /:\D\w*/g); foreach $i (@varlistv) { $i =~s/:/${calling_package}::/; $i =~s/,//; @{$i} = (); #INITIALIZE TO CLEAR ANY OLD VALUES! ${$i} = ''; push (@reflistv,\@{$i}); push (@refsclrv,\${$i}); } #while ((@myline) = &ora_fetch($mycsr)) while ((@myline) = $mycsr->fetchrow_array()) { last if (defined($resptr->{DBI}->err) && $resptr->{DBI}->err > 0); #foreach $i (0..$#varlistv) for ($i=0;$i<=$#varlistv;$i++) { ${$refsclrv[$i]} = $myline[$i] unless ($fetchcnt); push(@{$reflistv[$i]},$myline[$i]); } push (@res,[@myline]) unless ($nores); ++$fetchcnt; }; } else { #while (@myline = &ora_fetch($mycsr)) while (@myline = $mycsr->fetchrow_array()) { last if ($resptr->{DBI}->err > 0); push (@res,[@myline]) unless ($nores); ++$fetchcnt; }; } } else { return (); } $mycsr->finish; #$mycsr = undef; #return ( wantarray ? ('a','b','c') : $#res ); return (@res) unless ($nores); return $fetchcnt; } return (); } sub dbix_do { my ($resptr, $sqlstr, $ophref) = @_; #### WARNING: DO *NOT* USE SINGLE-QUOTES AROUND ANY /:PARAMETERS !!!!!!!!! #### (IT DON'T WORK!) my ($interpolate) = $ophref->{-interpolate} || $ophref; local ($_); my ($i, $j, $mycsr, $myexe, $rowcnt, @myvals, @varlistv, $logsql, $isaselect, $myline); my ($maxlistsize) = -1; my ($parmcnt) = 0; $lastdb = $resptr; $rowcnt = 0; #unless ($interpolate == 1 || $interpolate == 2) unless ($interpolate) #20001117: 3RD OPTION (=3): RETURN 0 OR MORE RECORDS FOR EACH KEY { #20001117: (=2) ALWAYS RETURNS 1 OR MORE RECORDS! #$rowcnt = &ora_do($resptr, $sqlstr); $rowcnt = $resptr->{DBI}->do($sqlstr); if ($logfid) { $sqlstr =~ s/\s+$//; $sqlstr =~ s/^\s+//; $sqlstr .= ';' if ($addsemicolon); $sqlstr =~ s/\&/\'\|\|\'\&\'\|\|\'/g if ($addsemicolon == 1); flock LOGFILE, 2; #EXCLUSIVE LOCK. print LOGFILE "$sqlstr\n"; flock LOGFILE, 8; #UNLOCK. } #&ora_commit($resptr); #$resptr->{DBI}->commit; &dbix_commit($resptr) if ($ophref->{-commit}); return (undef) if ($resptr->{DBI}->err < 0); return ($rowcnt); } #while ($sqlstr =~ s#(/\:[a-zA-Z_][^,\)\s\'\"]*)#':' . (++$parmcnt)#e) #while ($sqlstr =~ s#(\/\:[a-zA-Z_][^,\)\s\'\"]*)#\?#) #CHGD TO NEXT LINE 20010321 TO ALLOW '?' FOR BACKWARD COMPATABILITY. my $varliststart = 0; #NEXT 15 ADDED 20011207 TO FIX INCORRECT MAXLISTSIZE ON SELECTS. if ($sqlstr =~ /^\s*select\s/i) #("INTO" VBLES NEEDED BLANKING *BEFORE* MAXLISTSIZE CALCULATED!) { $isaselect = 1; while ($sqlstr =~ s#(into[\s\?\,]+)(\/\:[a-zA-Z_][^,\)\s\"]*)#$1\?#i) { push (@varlistv,$2); ++$parmcnt; } for ($i=0;$i<=$#varlistv;$i++) { $varlistv[$i] =~ s#/:#${calling_package}::#; } $varliststart = scalar(@varlistv); } while ($sqlstr =~ s#\'?(\/\:[a-zA-Z_][^,\)\s\"]*)#\?#) { push (@varlistv,$1); ++$parmcnt; } for ($i=$varliststart;$i<=$#varlistv;$i++) { $varlistv[$i] =~ s#/:#${calling_package}::#; #$varlistv[$i] =~ s/,//; if ($#{$varlistv[$i]} >= 0) { $maxlistsize = $#{$varlistv[$i]} if ($#{$varlistv[$i]} > $maxlistsize); } } $sqlstr =~ s/'(\:\d+)'/$1/g; #$sqlstr =~ s/'\?'/\?/g; #REMOVED 20010125 TO FIX ERROR WHEN USER ENTRY WAS JUST A SINGLE QUESTION-MARK! my ($cleansql) = $sqlstr; $selectargcnt = 0; if ($isaselect) { $cleansql =~ s/\binto\b([\s\?\,]*)//i; my ($t) = $1; ++$selectargcnt while ($t =~ /\?/g); } else { ###########$sqlstr =~ s/\n/ /gs; if ($resptr->{dbtype} =~ /odbc/i && $sqlstr =~ /insert.*\w+\.NEXTVAL/si) { $sqlstr = &fixNEXTVAL($resptr, $sqlstr); } elsif ($resptr->{dbtype} =~ /mysql/i) { $sqlstr =~ s/([\,\(]\s*)\w+\.NEXTVAL(\s*[\,\)])/$1NULL$2/g; #MYSQL DOES SEQUENCES DIFFERENTLY (see AUTO_INCREMENT)! } #ADDED 20030904 TO MAKE SEQUENCE-RETRIEVAL DATABASE-INDEPENDENT. if ($sqlstr =~ /insert\s+into\s+(\w+).*?(\w+)\.NEXTVAL/si) { $resptr->{lastsequencetable} = $1; $resptr->{lastsequencename} = $2; } } for ($i=0;$i<$selectargcnt;$i++) { @{$varlistv[$i]} = (); #INITIALIZE TO CLEAR ANY OLD VALUES ${$varlistv[$i]} = ''; #FROM VBLES TO RECEIVE SELECT INPUTS. } #$mycsr = &ora_open($resptr, $cleansql) if ($selectargcnt); my $cleantemplate; if ($resptr->{noplaceholders}) { $cleansql =~ s/([\'\"])([^\1]*?)\1/ my ($quote) = $1; my ($str) = $2; $str =~ s|\?|\x02\^2jSpR1tE\x02|g; #PROTECT ?'S IN QUOTES. "$quote$str$quote" /egs; $cleansql =~ s/\?/\x02\^3jSpR1tE\x02/gs; #PROTECT BINDING ?S. $cleansql =~ s/\x02\^2jSpR1tE\x02/\?/gs; #UNPROTECT ?'S IN QUOTES. $cleantemplate = $cleansql; } else { $mycsr = $resptr->{DBI}->prepare($cleansql) if ($isaselect); } $maxlistsize = 0 if ($maxlistsize < 0); my ($ii) = 0; my $bindok; #------------------------------------------------------------- local *select_noplaceholders = sub { my $t; for ($i=0;$i<=$maxlistsize;$i++) { $cleansql = $cleantemplate; for ($j=$selectargcnt;$j<=$parmcnt-1;$j++) { $t = defined($#{$varlistv[$j]}) ? ${$varlistv[$j]}[$i] : ${$varlistv[$j]}; $t =~ s/\'/\'\'/gs; $t =~ s/\?/\x02\^3jSpR1tE\x02/gs; $cleansql =~ s/\x02\^3jSpR1tE\x02/\'$t\'/s; } @myline = (); $mycsr = $resptr->{DBI}->prepare($cleansql); return 0 if ($resptr->{DBI}->err); #ADDED 20010517 TO CATCH ERRORS! $res = $mycsr->execute(); return 0 if ($resptr->{DBI}->err); #ADDED 20010517 TO CATCH ERRORS! #return 0 unless ($res > 0 || $mycsr); @myline = $mycsr->fetchrow_array(); if ($#myline >= 0 || $interpolate < 3) #IF ADDED 20001117 { for ($j=0;$j<=$selectargcnt-1;$j++) { $myline[$j] = '' if ($res eq 'OK'); #ADDED 19991011 JWT. if (defined($#{$varlistv[$j]})) { ${$varlistv[$j]}[$ii] = $myline[$j]; } else { ${$varlistv[$j]} = $myline[$j]; } } ++$ii; ++$rowcnt; } if ($interpolate >= 2) #20001117 { II: while (@myline = $mycsr->fetchrow_array()) { for ($j=0;$j<=$selectargcnt-1;$j++) { if (defined($#{$varlistv[$j]})) { ${$varlistv[$j]}[$ii] = $myline[$j]; } else { ${$varlistv[$j]} = $myline[$j]; } } ++$ii; ++$rowcnt; } } $mycsr->finish; if ($ophref->{-commit}) { &dbix_commit($resptr) unless ($rowcnt % 20); } } return 1; }; local *select_placeholders = sub { for ($i=0;$i<=$maxlistsize;$i++) { @myvals = (); for ($j=$selectargcnt;$j<=$parmcnt-1;$j++) { if (defined($#{$varlistv[$j]})) { push(@myvals,${$varlistv[$j]}[$i]); ###$mycsr->bind_param($j1, ${$varlistv[$j]}[$i]) if ($mycsr); } else { push(@myvals,${$varlistv[$j]}); ###$mycsr->bind_param($j1, ${$varlistv[$j]}) if ($mycsr); } } #&ora_bind($mycsr, @myvals) || return 0; $res = $mycsr->execute(@myvals); return 0 if ($resptr->{DBI}->err); #ADDED 20010517 TO CATCH ERRORS! #return 0 unless ($res > 0 || $mycsr); #@myline = (); @myline = $mycsr->fetchrow_array(); if ($#myline >= 0 || $interpolate < 3) #IF ADDED 20001117 { for ($j=0;$j<=$selectargcnt-1;$j++) { $myline[$j] = '' if ($res eq 'OK'); #ADDED 19991011 JWT. if (defined($#{$varlistv[$j]})) { ${$varlistv[$j]}[$ii] = $myline[$j]; } else { ${$varlistv[$j]} = $myline[$j]; } } ++$ii; ++$rowcnt; } if ($interpolate >= 2) #20001117 { II: if (scalar(@myline)) #TEST ADDED 20040211 TO PREVENT RAISE-ERROR ERROR. { while (@myline = $mycsr->fetchrow_array()) { for ($j=0;$j<=$selectargcnt-1;$j++) { if (defined($#{$varlistv[$j]})) { ${$varlistv[$j]}[$ii] = $myline[$j]; } else { ${$varlistv[$j]} = $myline[$j]; } } ++$ii; ++$rowcnt; } } } $mycsr->finish; #??? @myvals = (@myline,@myvals); if ($ophref->{-commit}) { &dbix_commit($resptr) unless ($rowcnt % 20); } } return 1; }; local *nonselect_noplaceholders = sub { my $t; for ($i=0;$i<=$maxlistsize;$i++) { @myvals = (); $cleansql = $cleantemplate; for ($j=$selectargcnt;$j<=$parmcnt-1;$j++) { $t = defined($#{$varlistv[$j]}) ? ${$varlistv[$j]}[$i] : ${$varlistv[$j]}; $t =~ s/\'/\'\'/gs; $t =~ s/\?/\x02\^3jSpR1tE\x02/gs; $cleansql =~ s/\x02\^3jSpR1tE\x02/\'$t\'/s; } #&ora_bind($mycsr, @myvals) || return 0; @myline = (); $res = $resptr->{DBI}->do($cleansql); return 0 unless ($res eq '0E0' || $res > 0); ++$rowcnt; if ($logfid) { $logsql = $cleansql; $logsql =~ s/\s+$//; $logsql =~ s/^\s+//; $logsql .= ';' if ($addsemicolon); $sqlstr =~ s/\&/\'\|\|\'\&\'\|\|\'/g if ($addsemicolon == 1); flock LOGFILE, 2; #EXCLUSIVE LOCK. print LOGFILE "$logsql\n"; flock LOGFILE, 8; #UNLOCK. } if ($ophref->{-commit}) { &dbix_commit($resptr) unless ($rowcnt % 20); } } return 1; }; local *nonselect_placeholders = sub { for ($i=0;$i<=$maxlistsize;$i++) { @myvals = (); for ($j=$selectargcnt;$j<=$parmcnt-1;$j++) { if (defined($#{$varlistv[$j]})) { push(@myvals,${$varlistv[$j]}[$i]); ###$mycsr->bind_param($j1, ${$varlistv[$j]}[$i]) if ($mycsr); } else { push(@myvals,${$varlistv[$j]}); ###$mycsr->bind_param($j1, ${$varlistv[$j]}) if ($mycsr); } } #&ora_bind($mycsr, @myvals) || return 0; @myline = (); $res = $resptr->{DBI}->do($cleansql,{},@myvals); return 0 unless ($res eq '0E0' || $res > 0); ++$rowcnt; if ($logfid) { #my ($j1); $logsql = $cleansql; #for $j (0..$#myvals) for ($j=0;$j<=$#myvals;$j++) { #$j1 = $j + 1; #$logsql =~ s/\:$j1/\'$myvals[$j]\'/g; $logsql =~ s/\?/\'$myvals[$j]\'/; } $logsql =~ s/\s+$//; $logsql =~ s/^\s+//; $logsql .= ';' if ($addsemicolon); $sqlstr =~ s/\&/\'\|\|\'\&\'\|\|\'/g if ($addsemicolon == 1); flock LOGFILE, 2; #EXCLUSIVE LOCK. print LOGFILE "$logsql\n"; flock LOGFILE, 8; #UNLOCK. } if ($ophref->{-commit}) { &dbix_commit($resptr) unless ($rowcnt % 20); } } return 1; }; #------------------------------------------------------------- if ($isaselect) { if ($resptr->{noplaceholders}) { $bindok = &select_noplaceholders(); } else { $bindok = &select_placeholders(); } } else { if ($resptr->{noplaceholders}) { $bindok = &nonselect_noplaceholders(); } else { $bindok = &nonselect_placeholders(); } } if ($bindok) { #$resptr->{DBI}->commit; &dbix_commit($resptr) if ($ophref->{-commit}); #NEXT IF ADDED 20040120 TO SORT VECTORIZED SELECTS (WHICH DON'T SORT #OTHERWISE) WHEN "ORDER-BY" CLAUSE PRESENT. NOTE: ONLY SELECT VECTORS #ARE SORTED (NOT KEY VECTORS) UNLESS INTERPOLATE = 1 (INTERPOLATE = 1 #IMPLIES A PROMISE THAT *ALL* VECTORS WILL BE *SAME* LENGTH!!!!!!!! if ($isaselect && $selectargcnt > 0 && $rowcnt > 1) { if ($sqlstr =~ /\s*order\s+by\s+([\w\.\, ]+)$/is) { my $ordbyclause = $1; my $sortavailable; eval {require 'sort_elements.pl' and $sortavailable = 1;}; if ($sortavailable) { my @sortfields = split(/\,/, $ordbyclause); my @sortorders = (); for (my $i=0;$i<=$#sortfields;$i++) #ASCENDING VS. DESCENDING. { $sortorders[$i] = ($sortfields[$i] =~ s/\s+desc//is) ? '-' : ''; $sortfields[$i] =~ s/\s//g; } if ($sqlstr =~ /select\s+([a-z\.\,_\s]+)into/is) { my $selfields = $1; my @selfields = split(/\,/, $selfields); my @sortvec; for (my $i=0;$i<=$#selfields;$i++) { $selfields[$i] =~ s/\s//g; } #DETERMINE WHICH ARRAYS TO SORT BY (SORTVEC). for (my $j=0;$j<=$#sortfields;$j++) { for (my $i=0;$i<=$#selfields;$i++) { if ($selfields[$i] eq $sortfields[$j]) { push (@sortvec, "$sortorders[$j]$i"); } } } my $sortcmd; if ($interpolate == 1) #SORT *ALL* VECTORS INCL. KEYS! { $sortcmd = "&sort_elements_by_list([" .join(',',@sortvec).'], [], \@' .join(',\@', @varlistv).');'; } else #ONLY SORT NON-KEY VECTORS (KEY VECTORS NOT SAME LENGTH). { #WARNING - KEY VECTORS WILL NOT LINE UP WITH SELECT VECTORS NOW! $sortcmd = "&sort_elements_by_list([" .join(',',@sortvec).'], []'; for (my $i=0;$i<=$#selfields;$i++) { $sortcmd .= ',\@'.$varlistv[$i]; } $sortcmd .= ');'; } eval $sortcmd; } } } } #return $res; return $rowcnt; } else { NOBIND: return undef; } } sub dbix_commit { my ($resptr) = shift; $lastdb = $resptr; #return $resptr->{DBI}->commit; #AUTOCOMMIT IS ON! return $resptr->{DBI}->commit unless ($resptr->{DBI}->{AutoCommit}); #AUTOCOMMIT IS ON! return 1; } sub dbix_rollback { my ($resptr) = shift; return $resptr->{DBI}->rollback unless ($resptr->{DBI}->{AutoCommit}); #AUTOCOMMIT IS ON! return 1; } sub dbix_autocommit #CALL WITH 1 ARG UNLESS DB ALREADY OPEN! { my ($ac) = shift; my ($resptr) = shift; if ($resptr) { $lastdb = $resptr; $resptr->{DBI}->{AutoCommit} = $ac; } else { $autocommit = $ac; } return 1; } sub dbix_err { my ($resptr) = shift || $lastdb; return $resptr->{DBI}->err if ($resptr && $resptr->{DBI} && $resptr->{DBI}->err); my $er; eval { $er = $$dbi_err if ($dbi_err); }; eval { $er = $DBI::err if ($DBI::err); }; $er = $1 if (!defined($er) && $lasterr =~ /(^\-?\d+)/); return $er; } sub dbix_errstr { my ($resptr) = shift || $lastdb; # return 'Not logged in, invalid database, id, password?' unless ($resptr); return $resptr->{DBI}->errstr if ($resptr && $resptr->{DBI} && $resptr->{DBI}->errstr); my $emsg; eval { $emsg = $$dbi_errstr if ($dbi_errstr); }; eval { $emsg = $DBI::errstr if ($DBI::errstr); }; $emsg = $1 if ($lasterr =~ /^\-?\d+\:?(.*)/); return $emsg || $lasterr; } sub dbix_open { my ($resptr, $sqlstr, $xeqflag) = @_; $lastdb = $resptr; #return &ora_open($resptr, $sqlstr); $sqlstr =~ s/\:\d+/\?/g unless ($xeqflag); my ($myres, $cleansql); my ($mycsr) = {}; $cleansql = $sqlstr; if ($sqlstr =~ /^\s*select\s/i) { $mycsr->{select} = 1; if ($sqlstr =~ /\binto\b/i) { my (@varlistv) = ($sqlstr =~ /:\D\w*/g); foreach $i (@varlistv) { $i =~s/:/${calling_package}::/; $i =~s/,//; @{$i} = (); #INITIALIZE TO CLEAR ANY OLD VALUES! ${$i} = ''; push (@{$mycsr->{reflistv}},\@{$i}); push (@{$mycsr->{refsclrv}},\${$i}); } $mycsr->{varcnt} = $#varlistv; $cleansql =~ s/\binto\b//i; $nores = 1 unless (wantarray); #print "-nores=$nores=\n"; $cleansql =~ s/\:[^\s\,\:]+\,?//g; } else { $mycsr->{varcnt} = -1; } } else { $mycsr->{select} = 0; } if ($resptr->{noplaceholders} && !$xeqflag) { $cleansql =~ s/([\'\"])([^\1]*?)\1/ my ($quote) = $1; my ($str) = $2; $str =~ s|\?|\x02\^2jSpR1tE\x02|g; #PROTECT ?'S IN QUOTES. "$quote$str$quote" /egs; $cleansql =~ s/\?/\x02\^3jSpR1tE\x02/gs; #PROTECT OTHER(BIND) ?'S $cleansql =~ s/\x02\^2jSpR1tE\x02/\?/gs; #UNPROTECT ?'S IN QUOTES. $mycsr->{sql} = $cleansql; #ADDED 20010228 FOR USE IN BIND. $mycsr->{cleansql} = $cleansql; #ADDED 20020715 FOR USE IN BIND. $mycsr->{cleansql} =~ s/\x02\^3jSpR1tE\x02/\?/gs; $mycsr->{dB} = $resptr; #ADDED 20010228 FOR USE IN BIND. } else { $mycsr->{cleansql} = $cleansql; #ADDED 20020715 FOR USE IN BIND. $mycsr->{dB} = $resptr; #ADDED 20010228 FOR USE IN BIND. $mycsr->{csr} = $resptr->{DBI}->prepare($cleansql); return undef unless (defined($mycsr->{csr})); if ($xeqflag && $mycsr->{csr}) { $myres = $mycsr->{csr}->execute(); if ($logfid) { $cleansql =~ s/\s+$//; $cleansql =~ s/^\s+//; $cleansql .= ';' if ($addsemicolon); $cleansql =~ s/\&/\'\|\|\'\&\'\|\|\'/g if ($addsemicolon == 1); flock LOGFILE, 2; #EXCLUSIVE LOCK. print LOGFILE "$cleansql\n"; flock LOGFILE, 8; #UNLOCK. } } $cleansql =~ s/\?/\x02\^3jSpR1tE\x02/gs; #PROTECT OTHER(BIND) ?'S $mycsr->{sql} = $cleansql; #ADDED 20010228 FOR USE IN BIND. } return wantarray ? ($mycsr, $myres) : $mycsr; } sub dbix_bind #NOTE: BIND ARGS MUST APPEAR IN ORDER (ie. :1, :2, :3)! { my ($resptr, $mycsr, @bindvals) = @_; my ($t); $lastdb = $resptr; #return (&ora_bind($mycsr, @bindvals)); my $fetchcnt = 0; my $cleansql = $mycsr->{cleansql}; my $sqlstr = $mycsr->{sql}; if ($resptr->{noplaceholders}) { $mycsr->{csr}->finish() if ($mycsr->{csr}); while (@bindvals) { $t = shift(@bindvals); $t =~ s/\'/\'\'/gs; $t =~ s/\?/\x02\^2jSpR1tE\x02/gs; $sqlstr =~ s/\x02\^3jSpR1tE\x02/\'$t\'/s; } $mycsr->{csr} = $resptr->{DBI}->prepare($sqlstr); return undef unless ($mycsr->{csr}); } my ($res) = $mycsr->{csr}->execute(@bindvals); return undef if ($mycsr->{csr}->err); if ($mycsr->{select}) { for (my $i=0;$i<=$mycsr->{varcnt};$i++) { ${${$mycsr->{refsclrv}}[$i]} = ''; @{${$mycsr->{reflistv}}[$i]} = (); } if ($mycsr->{varcnt} >= 0) { my (@myline); $fetchcnt = 0; while ((@myline) = $mycsr->{csr}->fetchrow_array()) { last if ($resptr->{DBI}->err > 0); for (my $i=0;$i<=$mycsr->{varcnt};$i++) { ${${$mycsr->{refsclrv}}[$i]} = $myline[$i] unless ($fetchcnt); #CORRECTED ABOVE BUG 20001205 WHICHED RETURNED LAST RECORD (SHOULD BE 1ST)! push(@{${$mycsr->{reflistv}}[$i]},$myline[$i]); } ++$fetchcnt; }; } } else #ADDED 20010228 TO LOG NON-SELECTS DONE W/BIND! { #NEXT 5 ADDED 20030922 TO MAKE SEQUENCE-RETRIEVAL DATABASE-INDEPENDENT. if ($sqlstr =~ /insert\s+into\s+(\w+).*?(\w+)\.NEXTVAL/si) { $resptr->{lastsequencetable} = $1; $resptr->{lastsequencename} = $2; } if ($logfid) { $sqlstr =~ s/\s+$//; $sqlstr =~ s/^\s+//; for (my $i=0;$i<=$#bindvals;$i++) { $t = $bindvals[$i]; $t =~ s/\'/\'\'/g; $t =~ s/\?/\x02\^2jSpR1tE\x02/gs; $sqlstr =~ s/\x02\^3jSpR1tE\x02/\'$t\'/s; } $sqlstr .= ';' if ($addsemicolon); $sqlstr =~ s/\&/\'\|\|\'\&\'\|\|\'/g if ($addsemicolon == 1); flock LOGFILE, 2; #EXCLUSIVE LOCK. print LOGFILE "$sqlstr\n"; flock LOGFILE, 8; #UNLOCK. } } if ($fetchcnt) #ADDED 20001205 TO FIX ERRONIOUS RESULT FROM ORACLE DBI::PROXY OF 250. { return $fetchcnt; } elsif ($res) { return $mycsr->{csr}->rows ? $mycsr->{csr}->rows : $res; } else { return $res; } } sub dbix_fetch { my ($resptr, $mycsr) = @_; $lastdb = $resptr; my (@myline) = $mycsr->{csr}->fetchrow_array(); if ($mycsr->{select}) { for (my $i=0;$i<=$mycsr->{varcnt};$i++) { ${${$mycsr->{refsclrv}}[$i]} = $myline[$i]; } } return (@myline); } sub dbix_fetchall { my ($resptr, $mycsr) = @_; my ($myline, @res, $nores); $lastdb = $resptr; $nores = 1 unless (wantarray); my ($fetchcnt) = 0; while ((@myline) = $mycsr->{csr}->fetchrow_array()) { #THIS FOR-LOOP IS CURRENTLY DEPRECIATED BECAUSE FETCH WILL NOT RETURN VALUES IF VARCNT > 0 (THEY'VE ALREADY BEEN FETCHED BY DBIX_BIND)! for (my $i=0;$i<=$mycsr->{varcnt};$i++) { ${${$mycsr->{refsclrv}}[$i]} = $myline[$i] unless ($fetchcnt); push (@{${$mycsr->{reflistv}}[$i]}, $myline[$i]); } push (@res, [@myline]) unless ($nores); ++$fetchcnt; } return (@res) unless ($nores); return $fetchcnt; } sub dbix_close { my ($mycsr) = @_; return undef unless ($mycsr->{csr}); return ($mycsr->{csr}->finish()); } sub dbix_do1 { my ($resptr, $sqlstr, @bindvals) = @_; my ($res, $t); #my ($mycsr) = &ora_open($resptr, $sqlstr); $lastdb = $resptr; $sqlstr =~ s/\:\d+/\?/g if ($#bindvals >= 0); if ($resptr->{dbtype} =~ /odbc/i && $sqlstr =~ /insert.*\w+\.NEXTVAL/s) { $sqlstr = &fixNEXTVAL($resptr, $sqlstr); } else { $sqlstr =~ s/([\,\(]\s*)\w+\.NEXTVAL(\s*[\,\)])/$1NULL$2/g if ($resptr->{dbtype} =~ /mysql/i); #MYSQL DOES SEQUENCES DIFFERENTLY (see AUTO_INCREMENT)! } #ADDED 20030904 TO MAKE SEQUENCE-RETRIEVAL DATABASE-INDEPENDENT. if ($sqlstr =~ /insert\s+into\s+(\w+).*?(\w+)\.NEXTVAL/si) { $resptr->{lastsequencetable} = $1; $resptr->{lastsequencename} = $2; } if ($resptr->{noplaceholders}) { $sqlstr =~ s/([\'\"])([^\1]*?)\1/ my ($quote) = $1; my ($str) = $2; $str =~ s|\?|\x02\^2jSpR1tE\x02|g; #PROTECT ?'S IN QUOTES. "$quote$str$quote" /egs; while (@bindvals) { $t = shift(@bindvals); $t =~ s/\'/\'\'/s; $t =~ s/\?/\x02\^2jSpR1tE\x02/gs; $t =~ s/\?/\x02\^2jSpR1tE\x02/gs; $sqlstr =~ s/\?/\'$t\'/s; } $sqlstr =~ s/\x02\^2jSpR1tE\x02/\?/gs; #UNPROTECT ?'S. } my ($mycsr) = $resptr->{DBI}->prepare($sqlstr); if ($mycsr) { #if ($sqlstr =~ /^\s+select/i) #CHGD TO NEXT 20011121! if ($sqlstr =~ /^\s*select/i) { $res = $mycsr->execute(@bindvals); #bindvals ADDED HERE 20000217. return undef unless(defined($res)); if ($res) { $res = $mycsr->rows if ($mycsr->rows); } if ($res > 0 || $res eq '0E0') { #@myline = &ora_fetch($mycsr); @myline = $mycsr->fetchrow_array(); #&ora_close($mycsr); $mycsr->finish; return wantarray ? ($res, @myline) : $res; } } else { #$res = &ora_bind($mycsr, @bindvals); $res = $mycsr->execute(@bindvals); return undef unless(defined($res)); if ($logfid) { $sqlstr =~ s/\s+$//; $sqlstr =~ s/^\s+//; # my @types = @{$mycsr->{TYPE}}; # my @lens = @{$mycsr->{PRECISION}}; # my @names = @{$mycsr->{NAME}}; if ($#bindvals >= 0) { $sqlstr =~ s/([\'\"])([^\1]*?)\1/ my ($quote) = $1; my ($str) = $2; $str =~ s|\?|\x02\^2jSpR1tE\x02|g; #PROTECT ?'S IN QUOTES. "$quote$str$quote" /egs; for (my $i=0;$i<=$#bindvals;$i++) { $t = $bindvals[$i]; $t =~ s/\'/\'\'/g; $t =~ s/\?/\x02\^2jSpR1tE\x02/gs; $sqlstr =~ s/\?/\'$t\'/; } $sqlstr =~ s/\x02\^2jSpR1tE\x02/\?/gs; #UNPROTECT ?'S. } $sqlstr .= ';' if ($addsemicolon); $sqlstr =~ s/\&/\'\|\|\'\&\'\|\|\'/g if ($addsemicolon == 1); flock LOGFILE, 2; #EXCLUSIVE LOCK. print LOGFILE "$sqlstr\n"; flock LOGFILE, 8; #UNLOCK. } if ($res > 0 || $res eq '0E0') { #&ora_close($mycsr); $res = $mycsr->rows if ($mycsr->rows); $mycsr->finish; return wantarray ? ($res) : $res; } } #&ora_close($mycsr); $mycsr->finish; } return undef; } sub dbix_fetchseq #ADDED 20030904 TO MAKE SEQUENCE-RETRIEVAL DATABASE-INDEPENDENT. { my $resptr = shift; my $seqfield = shift; my $seqname = shift || $resptr->{lastsequencename}; my $tablename = shift || $resptr->{lastsequencetable}; #ADD CODE FOR YOUR FAVORITE DATABASE HERE, OR TAKE THE DEFAULT (LAST OPTION)! if ($resptr->{dbtype} eq 'Sprite') { return $resptr->{DBI}->{sprite_insertid} if (defined $resptr->{DBI}->{sprite_insertid} && $resptr->{DBI}->{sprite_insertid} =~ /\d/); } if ($resptr->{dbtype} =~ /(?:Oracle|Sprite)/) { my $mycsr; if ($mycsr = $resptr->{DBI}->prepare("select $seqname.CURRVAL from DUAL")) { my $myexe; if ($myexe = $mycsr->execute()) { ($lastseq) = $mycsr->fetchrow_array(); $mycsr->finish(); return $lastseq if ($lastseq =~ /\d/); } } } elsif ($resptr->{dbtype} =~ /mysql/) { return $resptr->{DBI}->{mysql_insertid} if (defined $resptr->{DBI}->{mysql_insertid} && $resptr->{DBI}->{mysql_insertid} =~ /\d/); } if ($seqfield) #IF ALL ELSE FAILS, FETCH A DESCENDING LIST OF VALUES FOR THE FIELD THE SEQUENCE WAS INSERTED INTO (USER MUST SPECIFY THE FIELD!) { my $sql = <{DBI}->prepare($sql)) { my $myexe; if ($myexe = $mycsr->execute()) { ($lastseq) = $mycsr->fetchrow_array(); $mycsr->finish(); return $lastseq; } else { } } return undef; } return undef; } sub dbix_fetchnextseq { my $resptr = shift; my $seqname = shift || '.dbixseq'; #ADD CODE FOR YOUR FAVORITE DATABASE HERE, OR TAKE THE DEFAULT (LAST OPTION)! if ($resptr->{dbtype} =~ /(?:Oracle|Sprite)/) { my $mycsr; if ($mycsr = $resptr->{DBI}->prepare("select $seqname.NEXTVAL from DUAL")) { my $myexe; if ($myexe = $mycsr->execute()) { ($lastseq) = $mycsr->fetchrow_array(); $mycsr->finish(); return $lastseq if ($lastseq =~ /\d/); } } } elsif ($resptr->{dbtype} =~ /mysql/) { return $resptr->{DBI}->{mysql_insertid} if (defined $resptr->{DBI}->{mysql_insertid} && $resptr->{DBI}->{mysql_insertid} =~ /\d/); } #IF ALL ELSE FAILS, USE OUR OWN SPECIAL SEQUENCE FILE! my $seq_file = $ENV{HOME}; $seq_file .= '/' unless ($seq_file =~ m#\/$#); $seq_file = '' if ($seq_file eq '/'); $seq_file .= $seqname; if (open(T, "<$seq_file")) { my $x = ; chomp($x); my ($seqval, $seqincby) = split(/,/,$x); close (T); } else { $seqval = 0; $seqincby = 1; } if (open (T, ">$seq_file")) { $seqval += ($seqincby || 1); print T "$seqval,$seqincby\n"; close (T); return $seqval; } else { $lasterr = "$@/$? (file:$seq_file)"; } return undef; } sub fixNEXTVAL { my ($resptr, $sqlstr) = @_; if ($sqlstr =~ /^\s*insert\s+into\s+ # Keyword (\S+)\s* # Table (?:\((.+?)\)\s*)? # Keys values\s* # 'values' \((.+)\)\s*$/isxo) { my ($table, $columns, $values) = ($1, $2, $3); my ($origvalues) = $values; $columns =~ s/\s//g; unless ($columns =~ /\S/) { my $csr = $resptr->{DBI}->prepare("select * from $table"); $csr->execute(); $columns = join(',', @{$csr->{NAME}}); $csr->finish(); } my (@columns) = split(/,/, $columns); $columns = ''; $values =~ s/\\\\/\x02/g; #PROTECT "\\" #$values =~ s/\\\'|\'\'/\x03/g; #PROTECT "", \", '', AND \'. $values =~ s/\\\'/\x03/g; #PROTECT "", \", '', AND \'. $values =~ s/\'(.*?)\'/ my ($j)=$1; $j =~ s|,|\x04|g; #PROTECT "," IN QUOTES. "'$j'" /eg; @values = split(/,/,$values); ####$values = ''; for $i (0..$#values) { $values[$i] =~ s/^\s+//; #STRIP LEADING & TRAILING SPACES. $values[$i] =~ s/\s+$//; $values[$i] =~ s/\x03/\'\'/g; #RESTORE PROTECTED SINGLE QUOTES HERE. $values[$i] =~ s/\x02/\\/g; #RESTORE PROTECTED SINGLE QUOTES HERE. $values[$i] =~ s/\x04/,/g; #RESTORE PROTECTED SINGLE QUOTES HERE. if ($values[$i] =~ /\s*(\w+).NEXTVAL\s*$/) { my ($t) = $1; $origvalues =~ s/\s*$t\.NEXTVAL\s*\,?\s*//; } else { $columns .= $columns[$i] . ','; } }; chop ($columns); #$columns =~ s/[\,\s]+$//; $sqlstr = "insert into $table ($columns) values ($origvalues) "; #$sqlstr =~ s/\,\?/\, \?/g; #print "
-!!!!!! fixNEXTVAL: sql adjusted=$sqlstr=\n"; } return ($sqlstr); } #sub dbix_types #{ # my ($resptr) = shift; # my ($rtnchars) = shift; # local ($_); # my (@tps) = &Oraperl::ora_types($resptr); # if ($rtnchars) # { # for(0..$#tps) # { # $tps[$_] = $ctype[$tps[$_]] || '-unknown!-'; # } # } # return (@tps); #} #sub inittypes #NEEDED ONLY BY ORAPERL! #{ # $ctype[1] = 'VARCHAR2'; # $ctype[2] = 'NUMBER'; # $ctype[8] = 'LONG'; # $ctype[11] = 'ROWID'; # $ctype[12] = 'DATE'; # $ctype[23] = 'RAW'; # $ctype[24] = 'LONG RAW'; # $ctype[96] = 'CHAR'; # $ctype[106] = 'MLSLABEL'; #} 1 __END__ =head1 NAME DBIx - Perl extension for DBI, providing a higher-level abstraction for more database-independence. =head1 AUTHOR This module is Copyright (C) 2000 by Jim Turner Email: jim.turner@lmco.com All rights reserved. You may distribute this module under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file. =head1 SYNOPSIS / DESCRIPTION. use DBIx; I<&dbix_package>(__PACKAGE__); #UNLESS PACKAGE IS "main::". I<&dbix_set_databasetype>('Oracle'); I<&dbix_setproxy>('remotehost.domain.com:8016'); #IF USING REMOTE DBI::Proxy. $dB->{LongTruncOk} = 1; #ALLOW SILENT TRUNCATION! I<&dbix_setlog>('/tmp/dbixlogt.txt'); #OPTIONALLY, SET UP A LOG FILE. #B. my $dB = I<&dbix_connect>("dbname,dbuser,dbpswd",{}) || die "-no login: err=".I<&dbix_err>().':'.I<&dbix_errstr>."=\n"); #DO A SIMPLE SELECT, STORING THE RESULTS INTO @f1 AND @f2. THE 1ST RECORD #IS STORED INTO $f1 and $f2. ($res = I<&dbix_select>($dB, "select field1, field2 into :f1, :f2 from test")) || warn 'Could not do select ('.I<&dbix_err>().'.'.I<&dbix_errstr>().')!'); for (my $i=0;$i<=$res;$i++) { print "-For record# $i: field1=$f1[$i], field2=$f2[$i]\n"; } #B. @res = I<&dbix_select>($dB, "select field1, field2 from test"); for (my $i=0;$i<=$#res;$i++) { print "\n-For record# $i: "; for (my $j=0;$j<=$#{$res[$i]};$j++) { print "value =$res[$i]->[$j],\t"; } } #B. $sqlstr = <($dB, $sqlstr, 1); #B. ($res = I<&dbix_do1>($dB, 'insert into test values (?, ?, ?, ?)', 'value1', $value2, $value3, 'value4')) || die ('Could not insert record ('.I<&dbix_err>().'.'.I<&dbix_errstr>().')!'); #B I<&dbix_commit>($dB); #B. $sqlstr = <($dB, $sqlstr); die "Could not open ($sqlstr)!" unless ($csr); #AN OPTIONAL 3RD ARGUMENT TO I CAUSES $dbh->execute() TO BE CALLED IN ADDITION TO $dbh->prepare(). IF THE RETURN VARIABLE IS A LIST, A SECOND VALUE IS RETURNED REPRESENTING THE RESULT RETURNED BY $dbh->execute. ALSO, IF USED FOR A SELECT STATEMENT, VARIABLES CAN BE BOUND JUST AS FOR dbix_select (see ":f1" and ":f2" in prev. examples). for (my $i=0;$i<=$#f1;$i++) { I<&dbix_bind>($dB, $csr, $i, $f1[$i]); } I<&dbix_close>($csr); #B. $sqlstr = <($dB, $sqlstr); die "Could not open ($sqlstr)!" unless ($csr); for (my $i=0;$i<=$#f1;$i++) { I<&dbix_bind>($dB, $csr, $f1[$i]); while (($f3, $f4) = I<&dbix_fetch>($dB, $csr)) { print "-For record# $i (key $f1[$i]): field3=$f3, field4=$f4\n"; } } I<&dbix_close>($csr); #B. I<&dbix_disconnect>(); =head1 TODO Make a more object-oriented version. =head1 KNOWN BUGS -none (yet)- =head1 SEE ALSO DBI(3), perl(1) =cut