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 = <<END_SQL;
			select $seqfield
			from $tablename
			order by $seqfield desc
END_SQL
		if ($mycsr = $resptr->{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 = <T>;
		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 "<BR>-!!!!!! 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:  turnerjw784 - at - yahoo .dot 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<CONNECT TO A DATABASE>.

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<ANOTHER SIMPLE SELECT, RETURNING THE RESULTS INTO @res>.

@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<DO A VECTORIZED SELECT, SELECTING A RECORD FOR EACH KEY VALUE IN @f1>.

$sqlstr = <<END_SQL;

	select field3, field4 into :f3, :f4 

	from test2 

	where field1 = /:f1

END_SQL

$res = I<&dbix_do>($dB, $sqlstr, 1);

#B<INSERT A NEW RECORD>.

($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<COMMIT THE TRANSACTION!>

I<&dbix_commit>($dB);

#B<CURSORS>.

$sqlstr = <<END_SQL;

	update test set field1 = ? 

	where field2 = ? 

END_SQL	

$csr = I<&dbix_open>($dB, $sqlstr);

die "Could not open ($sqlstr)!"  unless ($csr);

#AN OPTIONAL 3RD ARGUMENT TO I<dbix_open> 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<AN EXAMPLE OF SELECT USING CURSORS>.

$sqlstr = <<END_SQL;

	select field3, field4

	from test2 

	where field1 = ? 

END_SQL	

$csr = I<&dbix_open>($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<DISCONNECT FROM THE DATABASE>.

I<&dbix_disconnect>();

=head1 TODO

	Make a more object-oriented version.

=head1 KNOWN BUGS

-none (yet)-

=head1 SEE ALSO

DBI(3), perl(1)

=cut
