Manual is at http://php.weblogs.com/adodb_manual */ if (!defined('_ADODB_LAYER')) { define('_ADODB_LAYER',1); //============================================================================================== // CONSTANT DEFINITIONS //============================================================================================== define('ADODB_BAD_RS','
Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;
'); define('ADODB_FETCH_DEFAULT',0); define('ADODB_FETCH_NUM',1); define('ADODB_FETCH_ASSOC',2); define('ADODB_FETCH_BOTH',3); /* Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names. This currently works only with mssql, odbc, oci8po and ibase derived drivers. 0 = assoc lowercase field names. $rs->fields['orderid'] 1 = assoc uppercase field names. $rs->fields['ORDERID'] 2 = use native-case field names. $rs->fields['OrderID'] */ if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2); // allow [ ] @ ` and . in table names define('ADODB_TABLE_REGEX','([]0-9a-z_\`\.\@\[-]*)'); if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10); /** * Set ADODB_DIR to the directory where this file resides... * This constant was formerly called $ADODB_RootPath */ if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__)); if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100); //============================================================================================== // GLOBAL VARIABLES //============================================================================================== GLOBAL $ADODB_vers, // database version $ADODB_Database, // last database driver used $ADODB_COUNTRECS, // count number of records returned - slows down query $ADODB_CACHE_DIR, // directory to cache recordsets $ADODB_EXTENSION, // ADODB extension installed $ADODB_COMPAT_PATCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF $ADODB_FETCH_MODE; // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default... //============================================================================================== // GLOBAL SETUP //============================================================================================== if (strnatcmp(PHP_VERSION,'4.3.0')>=0) { define('ADODB_PHPVER',0x4300); } else if (strnatcmp(PHP_VERSION,'4.2.0')>=0) { define('ADODB_PHPVER',0x4200); } else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) { define('ADODB_PHPVER',0x4050); } else { define('ADODB_PHPVER',0x4000); } $ADODB_EXTENSION = defined('ADODB_EXTENSION'); //if (extension_loaded('dbx')) define('ADODB_DBX',1); /** Accepts $src and $dest arrays, replacing string $data */ function ADODB_str_replace($src, $dest, $data) { if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data); $s = reset($src); $d = reset($dest); while ($s !== false) { $data = str_replace($s,$d,$data); $s = next($src); $d = next($dest); } return $data; } function ADODB_Setup() { GLOBAL $ADODB_vers, // database version $ADODB_Database, // last database driver used $ADODB_COUNTRECS, // count number of records returned - slows down query $ADODB_CACHE_DIR, // directory to cache recordsets $ADODB_FETCH_MODE; $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT; if (!isset($ADODB_CACHE_DIR)) { $ADODB_CACHE_DIR = '/tmp'; } else { // do not accept url based paths, eg. http:/ or ftp:/ if (strpos($ADODB_CACHE_DIR,'://') !== false) die("Illegal path http:// or ftp://"); } // Initialize random number generator for randomizing cache flushes srand(((double)microtime())*1000000); /** * Name of last database driver loaded into memory. Set by ADOLoadCode(). */ $ADODB_Database = ''; /** * ADODB version as a string. */ $ADODB_vers = 'V3.60 16 June 2003 (c) 2000-2003 John Lim (jlim@natsoft.com.my). All rights reserved. Released BSD & LGPL.'; /** * Determines whether recordset->RecordCount() is used. * Set to false for highest performance -- RecordCount() will always return -1 then * for databases that provide "virtual" recordcounts... */ $ADODB_COUNTRECS = true; } //============================================================================================== // CHANGE NOTHING BELOW UNLESS YOU ARE CODING //============================================================================================== ADODB_Setup(); //============================================================================================== // CLASS ADOFieldObject //============================================================================================== /** * Helper class for FetchFields -- holds info on a column */ class ADOFieldObject { var $name = ''; var $max_length=0; var $type=""; // additional fields by dannym... (danny_milo@yahoo.com) var $not_null = false; // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^ // so we can as well make not_null standard (leaving it at "false" does not harm anyways) var $has_default = false; // this one I have done only in mysql and postgres for now ... // others to come (dannym) var $default_value; // default, if any, and supported. Check has_default first. } function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) { //print "Errorno ($fn errno=$errno m=$errmsg) "; $thisConnection->_transOK = false; if ($thisConnection->_oldRaiseFn) { $fn = $thisConnection->_oldRaiseFn; $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection); } } //============================================================================================== // CLASS ADOConnection //============================================================================================== /** * Connection object. For connecting to databases, and executing queries. */ class ADOConnection { // // PUBLIC VARS // var $dataProvider = 'native'; var $databaseType = ''; /// RDBMS currently in use, eg. odbc, mysql, mssql var $database = ''; /// Name of database to be used. var $host = ''; /// The hostname of the database server var $user = ''; /// The username which is used to connect to the database server. var $password = ''; /// Password for the username. For security, we no longer store it. var $debug = false; /// if set to true will output sql statements var $maxblobsize = 256000; /// maximum size of blobs or large text fields -- some databases die otherwise like foxpro var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase var $fmtDate = "'Y-m-d'"; /// used by DBDate() as the default date format used by the database var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt. var $true = '1'; /// string that represents TRUE for a database var $false = '0'; /// string that represents FALSE for a database var $replaceQuote = "\\'"; /// string to use to replace quotes var $charSet=false; /// character set to use - only for interbase var $metaDatabasesSQL = ''; var $metaTablesSQL = ''; var $uniqueOrderBy = false; /// All order by columns have to be unique var $emptyDate = ' '; //-- var $hasInsertID = false; /// supports autoincrement ID? var $hasAffectedRows = false; /// supports affected rows for update/delete? var $hasTop = false; /// support mssql/access SELECT TOP 10 * FROM TABLE var $hasLimit = false; /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10 var $readOnly = false; /// this is a readonly database - used by phpLens var $hasMoveFirst = false; /// has ability to run MoveFirst(), scrolling backwards var $hasGenID = false; /// can generate sequences using GenID(); var $hasTransactions = true; /// has transactions //-- var $genID = 0; /// sequence id used by GenID(); var $raiseErrorFn = false; /// error function to call var $upperCase = false; /// uppercase function to call for searching/where var $isoDates = false; /// accepts dates in ISO format var $cacheSecs = 3600; /// cache for 1 hour var $sysDate = false; /// name of function that returns the current date var $sysTimeStamp = false; /// name of function that returns the current timestamp var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' ' var $numCacheHits = 0; var $numCacheMisses = 0; var $pageExecuteCountRows = true; var $uniqueSort = false; /// indicates that all fields in order by must be unique var $leftOuter = false; /// operator to use for left outer join in WHERE clause var $rightOuter = false; /// operator to use for right outer join in WHERE clause var $ansiOuter = false; /// whether ansi outer join syntax supported var $autoRollback = false; // autoRollback on PConnect(). var $poorAffectedRows = false; // affectedRows not working or unreliable var $fnExecute = false; var $fnCacheExecute = false; var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char var $dbxDriver = false; // // PRIVATE VARS // var $_oldRaiseFn = false; var $_transOK = null; var $_connectionID = false; /// The returned link identifier whenever a successful database connection is made. var $_errorMsg = ''; /// A variable which was used to keep the returned last error message. The value will /// then returned by the errorMsg() function var $_queryID = false; /// This variable keeps the last created result link identifier var $_isPersistentConnection = false; /// A boolean variable to state whether its a persistent connection or normal connection. */ var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters. var $autoCommit = true; /// do not modify this yourself - actually private var $transOff = 0; /// temporarily disable transactions var $transCnt = 0; /// count of nested transactions var $fetchMode=false; /** * Constructor */ function ADOConnection() { die('Virtual Class -- cannot instantiate'); } /** Get server version info... @returns An array with 2 elements: $arr['string'] is the description string, and $arr[version] is the version (also a string). */ function ServerInfo() { return array('description' => '', 'version' => ''); } function _findvers($str) { if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1]; else return ''; } /** * All error messages go through this bottleneck function. * You can define your own handler by defining the function name in ADODB_OUTP. */ function outp($msg,$newline=true) { global $HTTP_SERVER_VARS; if (defined('ADODB_OUTP')) { $fn = ADODB_OUTP; $fn($msg,$newline); return; } if ($newline) $msg .= "$ss
\n'.$sql.''; exit; } ADOConnection::outp($e .': '. $m ); flush(); } } else { // non-debug version of query $sqlcount++; $sql_start = $this->getmicrotime(); $this->_queryID =@$this->_query($sql,$inputarr,$arg3); $sql_end = $this->getmicrotime(); $elapsed = $sql_end - $sql_start; $totalsql += $elapsed; //$fp = @fopen ($pathtoroot."log.sql", "aw"); $starttime = 0; // by Alex (variable was not defined) $d=time()-$starttime; $t=date("Y-m-d\tH:i:s",$starttime); $e = round($elapsed,5); if(function_exists("LogEntry")) @LogEntry("\t\t$sqlcount|$e:|$sql\n"); } // error handling if query fails if ($this->_queryID === false) { $fn = $this->raiseErrorFn; if ($fn) { $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this); } return false; } else if ($this->_queryID === true) { // return simplified empty recordset for inserts/updates/deletes with lower overhead $rs = new ADORecordSet_empty(); return $rs; } // return real recordset from select statement $rsclass = "ADORecordSet_".$this->databaseType; $rs = new $rsclass($this->_queryID,$this->fetchMode); // &new not supported by older PHP versions $rs->connection = &$this; // Pablo suggestion $rs->Init(); if (is_array($sql)) $rs->sql = $sql[0]; else $rs->sql = $sql; if ($rs->_numOfRows <= 0) { global $ADODB_COUNTRECS; if ($ADODB_COUNTRECS) { if (!$rs->EOF){ $rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql)); $rs->_queryID = $this->_queryID; } else $rs->_numOfRows = 0; } } return $rs; } function CreateSequence($seqname='adodbseq',$startID=1) { if (empty($this->_genSeqSQL)) return false; return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); } function DropSequence($seqname) { if (empty($this->_dropSeqSQL)) return false; return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); } /** * Generates a sequence id and stores it in $this->genID; * GenID is only available if $this->hasGenID = true; * * @param seqname name of sequence to use * @param startID if sequence does not exist, start at this ID * @return 0 if not supported, otherwise a sequence id */ function GenID($seqname='adodbseq',$startID=1) { if (!$this->hasGenID) { return 0; // formerly returns false pre 1.60 } $getnext = sprintf($this->_genIDSQL,$seqname); $rs = @$this->Execute($getnext); if (!$rs) { $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID)); $rs = $this->Execute($getnext); } if ($rs && !$rs->EOF) $this->genID = reset($rs->fields); else $this->genID = 0; // false if ($rs) $rs->Close(); return $this->genID; } /** * @return the last inserted ID. Not all databases support this. */ function Insert_ID() { if ($this->hasInsertID) return $this->_insertid(); if ($this->debug) ADOConnection::outp( '
Error #'.$e.': '.$m.'
'; echo 'Request
'; print_r($_REQUEST); echo 'Back Trace
'; print_r(debug_backtrace()); echo '
Insert_ID error
'); return false; } /** * Portable Insert ID. Pablo RocaAffected_Rows error
',false); return false; } /** * @return the last error message */ function ErrorMsg() { return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg; } /** * @return the last error number. Normally 0 means no error. */ function ErrorNo() { return ($this->_errorMsg) ? -1 : 0; } function MetaError($err=false) { include_once(ADODB_DIR."/adodb-error.inc.php"); if ($err === false) $err = $this->ErrorNo(); return adodb_error($this->dataProvider,$this->databaseType,$err); } function MetaErrorMsg($errno) { include_once(ADODB_DIR."/adodb-error.inc.php"); return adodb_errormsg($errno); } /** * @returns an array with the primary key columns in it. */ function MetaPrimaryKeys($table, $owner=false) { // owner not used in base class - see oci8 $p = array(); $objs =& $this->MetaColumns($table); if ($objs) { foreach($objs as $v) { if (!empty($v->primary_key)) $p[] = $v->name; } } if (sizeof($p)) return $p; return false; } /** * Choose a database to connect to. Many databases do not support this. * * @param dbName is the name of the database to select * @return true or false */ function SelectDB($dbName) {return false;} /** * Will select, getting rows from $offset (1-based), for $nrows. * This simulates the MySQL "select * from table limit $offset,$nrows" , and * the PostgreSQL "select * from table limit $nrows offset $offset". Note that * MySQL and PostgreSQL parameter ordering is the opposite of the other. * eg. * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based) * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based) * * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set) * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set * * @param sql * @param [offset] is the row to start calculations from (1-based) * @param [nrows] is the number of rows to get * @param [inputarr] array of bind variables * @param [arg3] is a private parameter only used by jlim * @param [secs2cache] is a private parameter only used by jlim * @return the recordset ($rs->databaseType == 'array') */ function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0) { if ($this->hasTop && $nrows > 0) { // suggested by Reinhard Balling. Access requires top after distinct // Informix requires first before distinct - F Riosa $ismssql = (strpos($this->databaseType,'mssql') !== false); if ($ismssql) $isaccess = false; else $isaccess = (strpos($this->databaseType,'access') !== false); if ($offset <= 0) { // access includes ties in result if ($isaccess) { $sql = preg_replace( '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); if ($secs2cache>0) return $this->CacheExecute($secs2cache, $sql,$inputarr,$arg3); else return $this->Execute($sql,$inputarr,$arg3); } else if ($ismssql){ $sql = preg_replace( '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); } else { $sql = preg_replace( '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nrows.' ',$sql); } } else { $nn = $nrows + $offset; if ($isaccess || $ismssql) { $sql = preg_replace( '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); } else { $sql = preg_replace( '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql); } } } // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer rows // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS. global $ADODB_COUNTRECS; $savec = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; if ($offset>0){ if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3); else $rs = &$this->Execute($sql,$inputarr,$arg3); } else { if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3); else $rs = &$this->Execute($sql,$inputarr,$arg3); } $ADODB_COUNTRECS = $savec; if ($rs && !$rs->EOF) { return $this->_rs2rs($rs,$nrows,$offset); } //print_r($rs); return $rs; } /** * Convert database recordset to an array recordset * input recordset's cursor should be at beginning, and * old $rs will be closed. * * @param rs the recordset to copy * @param [nrows] number of rows to retrieve (optional) * @param [offset] offset by number of rows (optional) * @return the new recordset */ function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) { if (! $rs) return false; $dbtype = $rs->databaseType; if (!$dbtype) { $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ? return $rs; } if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) { $rs->MoveFirst(); $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ? return $rs; } $flds = array(); for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) { $flds[] = $rs->FetchField($i); } $arr =& $rs->GetArrayLimit($nrows,$offset); //print_r($arr); if ($close) $rs->Close(); $arrayClass = $this->arrayClass; $rs2 = new $arrayClass(); $rs2->connection = &$this; $rs2->sql = $rs->sql; $rs2->dataProvider = $this->dataProvider; $rs2->InitArrayFields($arr,$flds); return $rs2; } function &GetArray($sql, $inputarr=false) { return $this->GetAll($sql,$inputarr); } /** * Return first element of first row of sql statement. Recordset is disposed * for you. * * @param sql SQL statement * @param [inputarr] input bind array */ function GetOne($sql,$inputarr=false) { global $ADODB_COUNTRECS; $crecs = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; $ret = false; $rs = &$this->Execute($sql,$inputarr); if ($rs) { if (!$rs->EOF) $ret = reset($rs->fields); $rs->Close(); } $ADODB_COUNTRECS = $crecs; return $ret; } function CacheGetOne($secs2cache,$sql=false,$inputarr=false) { $ret = false; $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr); if ($rs) { if (!$rs->EOF) $ret = reset($rs->fields); $rs->Close(); } return $ret; } function GetCol($sql, $inputarr = false, $trim = false) { $rv = false; $rs = &$this->Execute($sql, $inputarr); if ($rs) { if ($trim) { while (!$rs->EOF) { $rv[] = trim(reset($rs->fields)); $rs->MoveNext(); } } else { while (!$rs->EOF) { $rv[] = reset($rs->fields); $rs->MoveNext(); } } $rs->Close(); } return $rv; } function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false) { $rv = false; $rs = &$this->CacheExecute($secs, $sql, $inputarr); if ($rs) { if ($trim) { while (!$rs->EOF) { $rv[] = trim(reset($rs->fields)); $rs->MoveNext(); } } else { while (!$rs->EOF) { $rv[] = reset($rs->fields); $rs->MoveNext(); } } $rs->Close(); } return $rv; } /* Calculate the offset of a date for a particular database and generate appropriate SQL. Useful for calculating future/past dates and storing in a database. If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour. */ function OffsetDate($dayFraction,$date=false) { if (!$date) $date = $this->sysDate; return '('.$date.'+'.$dayFraction.')'; } /** * Return all rows. Compat with PEAR DB * * @param sql SQL statement * @param [inputarr] input bind array */ function &GetAll($sql,$inputarr=false) { global $ADODB_COUNTRECS; $savec = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; $rs = $this->Execute($sql,$inputarr); $ADODB_COUNTRECS = $savec; if (!$rs) if (defined('ADODB_PEAR')) return ADODB_PEAR_Error(); else return false; $arr =& $rs->GetArray(); $rs->Close(); return $arr; } function &CacheGetAll($secs2cache,$sql=false,$inputarr=false) { global $ADODB_COUNTRECS; $savec = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; $rs = $this->CacheExecute($secs2cache,$sql,$inputarr); $ADODB_COUNTRECS = $savec; if (!$rs) if (defined('ADODB_PEAR')) return ADODB_PEAR_Error(); else return false; $arr =& $rs->GetArray(); $rs->Close(); return $arr; } /** * Return one row of sql statement. Recordset is disposed for you. * * @param sql SQL statement * @param [inputarr] input bind array */ function &GetRow($sql,$inputarr=false) { global $ADODB_COUNTRECS; $crecs = $ADODB_COUNTRECS; $ADODB_COUNTRECS = false; $rs = $this->Execute($sql,$inputarr); $ADODB_COUNTRECS = $crecs; if ($rs) { $arr = array(); if (!$rs->EOF) $arr = $rs->fields; $rs->Close(); return $arr; } return false; } function &CacheGetRow($secs2cache,$sql=false,$inputarr=false) { $rs = $this->CacheExecute($secs2cache,$sql,$inputarr); if ($rs) { $arr = false; if (!$rs->EOF) $arr = $rs->fields; $rs->Close(); return $arr; } return false; } /** * Insert or replace a single record. Note: this is not the same as MySQL's replace. * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL. * Also note that no table locking is done currently, so it is possible that the * record be inserted twice by two programs... * * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname'); * * $table table name * $fieldArray associative array of data (you must quote strings yourself). * $keyCol the primary key field name or if compound key, array of field names * autoQuote set to true to use a hueristic to quote strings. Works with nulls and numbers * but does not work with dates nor SQL functions. * has_autoinc the primary key is an auto-inc field, so skip in insert. * * Currently blob replace not supported * * returns 0 = fail, 1 = update, 2 = insert */ function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) { if (count($fieldArray) == 0) return 0; $first = true; $uSet = ''; if (!is_array($keyCol)) { $keyCol = array($keyCol); } foreach($fieldArray as $k => $v) { if ($autoQuote && !is_numeric($v) and substr($v,0,1) != "'" and strcasecmp($v,'null')!=0) { $v = $this->qstr($v); $fieldArray[$k] = $v; } if (in_array($k,$keyCol)) continue; // skip UPDATE if is key if ($first) { $first = false; $uSet = "$k=$v"; } else $uSet .= ",$k=$v"; } $first = true; foreach ($keyCol as $v) { if ($first) { $first = false; $where = "$v=$fieldArray[$v]"; } else { $where .= " and $v=$fieldArray[$v]"; } } if ($uSet) { $update = "UPDATE $table SET $uSet WHERE $where"; $rs = $this->Execute($update); if ($rs) { if ($this->poorAffectedRows) { /* The Select count(*) wipes out any errors that the update would have returned. http://phplens.com/lens/lensforum/msgs.php?id=5696 */ if ($this->ErrorNo()<>0) return 0; # affected_rows == 0 if update field values identical to old values # for mysql - which is silly. $cnt = $this->GetOne("select count(*) from $table where $where"); if ($cnt > 0) return 1; // record already exists } else if (($this->Affected_Rows()>0)) return 1; } } // print "Error=".$this->ErrorNo().'
';
$first = true;
foreach($fieldArray as $k => $v) {
if ($has_autoinc && in_array($k,$keyCol)) continue; // skip autoinc col
if ($first) {
$first = false;
$iCols = "$k";
$iVals = "$v";
} else {
$iCols .= ",$k";
$iVals .= ",$v";
}
}
$insert = "INSERT INTO $table ($iCols) VALUES ($iVals)";
$rs = $this->Execute($insert);
return ($rs) ? 2 : 0;
}
/**
* Will select, getting rows from $offset (1-based), for $nrows.
* This simulates the MySQL "select * from table limit $offset,$nrows" , and
* the PostgreSQL "select * from table limit $nrows offset $offset". Note that
* MySQL and PostgreSQL parameter ordering is the opposite of the other.
* eg.
* CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
* CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
*
* BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
*
* @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional
* @param sql
* @param [offset] is the row to start calculations from (1-based)
* @param [nrows] is the number of rows to get
* @param [inputarr] array of bind variables
* @param [arg3] is a private parameter only used by jlim
* @return the recordset ($rs->databaseType == 'array')
*/
function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false)
{
if (!is_numeric($secs2cache)) {
if ($sql === false) $sql = -1;
if ($offset == -1) $offset = false;
// sql, nrows, offset,inputarr,arg3
return $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$inputarr,$this->cacheSecs);
} else {
if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()");
return $this->SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache);
}
}
/**
* Flush cached recordsets that match a particular $sql statement.
* If $sql == false, then we purge all files in the cache.
*/
function CacheFlush($sql=false,$inputarr=false)
{
global $ADODB_CACHE_DIR;
if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
if (strpos(strtoupper(PHP_OS),'WIN') !== false) {
$cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
} else {
$cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/??/adodb_*.cache';
// old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`';
}
if ($this->debug) {
ADOConnection::outp( "CacheFlush: $cmd
\n", system($cmd),""); } else { exec($cmd); } return; } $f = $this->_gencachename($sql.serialize($inputarr),false); adodb_write_file($f,''); // is adodb_write_file needed? @unlink($f); } /** * Private function to generate filename for caching. * Filename is generated based on: * * - sql statement * - database type (oci8, ibase, ifx, etc) * - database name * - userid * * We create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR). * Assuming that we can have 50,000 files per directory with good performance, * then we can scale to 12.8 million unique cached recordsets. Wow! */ function _gencachename($sql,$createdir) { global $ADODB_CACHE_DIR; $m = md5($sql.$this->databaseType.$this->database.$this->user); $dir = $ADODB_CACHE_DIR.'/'.substr($m,0,2); if ($createdir && !file_exists($dir)) { $oldu = umask(0); if (!mkdir($dir,0771)) if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql"); umask($oldu); } return $dir.'/adodb_'.$m.'.cache'; } /** * Execute SQL, caching recordsets. * * @param [secs2cache] seconds to cache data, set to 0 to force query. * This is an optional parameter. * @param sql SQL statement to execute * @param [inputarr] holds the input data to bind to * @param [arg3] reserved for john lim for future use * @return RecordSet or false */ function &CacheExecute($secs2cache,$sql=false,$inputarr=false,$arg3=false) { if (!is_numeric($secs2cache)) { $arg3 = $inputarr; $inputarr = $sql; $sql = $secs2cache; $secs2cache = $this->cacheSecs; } include_once(ADODB_DIR.'/adodb-csvlib.inc.php'); $md5file = $this->_gencachename($sql.serialize($inputarr),true); $err = ''; if ($secs2cache > 0){ $rs = &csv2rs($md5file,$err,$secs2cache); $this->numCacheHits += 1; } else { $err='Timeout 1'; $rs = false; $this->numCacheMisses += 1; } if (!$rs) { // no cached rs found if ($this->debug) { if (get_magic_quotes_runtime()) { ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :("); } ADOConnection::outp( " $md5file cache failure: $err (see sql below)"); } $rs = &$this->Execute($sql,$inputarr,$arg3); if ($rs) { $eof = $rs->EOF; $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately $txt = _rs2serialize($rs,false,$sql); // serialize if (!adodb_write_file($md5file,$txt,$this->debug)) { if ($fn = $this->raiseErrorFn) { $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this); } if ($this->debug) ADOConnection::outp( " Cache write error"); } if ($rs->EOF && !$eof) { $rs->MoveFirst(); //$rs = &csv2rs($md5file,$err); $rs->connection = &$this; // Pablo suggestion } } else @unlink($md5file); } else { if ($this->fnCacheExecute) { $fn = $this->fnCacheExecute; $fn($this, $secs2cache, $sql, $inputarr); } // ok, set cached object found $rs->connection = &$this; // Pablo suggestion if ($this->debug){ global $HTTP_SERVER_VARS; $inBrowser = isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']); $ttl = $rs->timeCreated + $secs2cache - time(); $s = is_array($sql) ? $sql[0] : $sql; if ($inBrowser) $s = ''.htmlspecialchars($s).''; ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]"); } } return $rs; } /** * Generates an Update Query based on an existing recordset. * $arrFields is an associative array of fields with the value * that should be assigned. * * Note: This function should only be used on a recordset * that is run against a single table and sql should only * be a simple select stmt with no groupby/orderby/limit * * "Jonathan Younger"
ADONewConnection: Unable to load database driver '$db'
",false); return false; } $cls = 'ADODB_'.$ADODB_Database; $obj = new $cls(); if ($errorfn) $obj->raiseErrorFn = $errorfn; return $obj; } function &NewDataDictionary(&$conn) { $provider = $conn->dataProvider; $drivername = $conn->databaseType; if ($provider !== 'native' && $provider != 'odbc' && $provider != 'ado') $drivername = $conn->dataProvider; else { if (substr($drivername,0,5) == 'odbc_') $drivername = substr($drivername,5); else if (substr($drivername,0,4) == 'ado_') $drivername = substr($drivername,4); else switch($drivername) { case 'oracle': $drivername = 'oci8';break; case 'sybase': $drivername = 'mssql';break; case 'access': case 'db2': break; default: $drivername = 'generic'; break; } } include_once(ADODB_DIR.'/adodb-lib.inc.php'); include_once(ADODB_DIR.'/adodb-datadict.inc.php'); $path = ADODB_DIR."/datadict/datadict-$drivername.inc.php"; if (!file_exists($path)) { ADOConnection::outp("Database driver '$path' not available"); return false; } include_once($path); $class = "ADODB2_$drivername"; $dict =& new $class(); $dict->dataProvider = $conn->dataProvider; $dict->connection = &$conn; $dict->upperName = strtoupper($drivername); if (is_resource($conn->_connectionID)) $dict->serverInfo = $conn->ServerInfo(); return $dict; } /** * Save a file $filename and its $contents (normally for caching) with file locking */ function adodb_write_file($filename, $contents,$debug=false) { # http://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows # So to simulate locking, we assume that rename is an atomic operation. # First we delete $filename, then we create a $tempfile write to it and # rename to the desired $filename. If the rename works, then we successfully # modified the file exclusively. # What a stupid need - having to simulate locking. # Risks: # 1. $tempfile name is not unique -- very very low # 2. unlink($filename) fails -- ok, rename will fail # 3. adodb reads stale file because unlink fails -- ok, $rs timeout occurs # 4. another process creates $filename between unlink() and rename() -- ok, rename() fails and cache updated if (strpos(strtoupper(PHP_OS),'WIN') !== false) { // skip the decimal place $mtime = substr(str_replace(' ','_',microtime()),2); // unlink will let some latencies develop, so uniqid() is more random @unlink($filename); // getmypid() actually returns 0 on Win98 - never mind! $tmpname = $filename.uniqid($mtime).getmypid(); if (!($fd = fopen($tmpname,'a'))) return false; $ok = ftruncate($fd,0); if (!fwrite($fd,$contents)) $ok = false; fclose($fd); chmod($tmpname,0644); if (!@rename($tmpname,$filename)) { unlink($tmpname); $ok = false; } if (!$ok) { if ($debug) ADOConnection::outp( " Rename $tmpname ".($ok? 'ok' : 'failed')); } return $ok; } if (!($fd = fopen($filename, 'a'))) return false; if (flock($fd, LOCK_EX) && ftruncate($fd, 0)) { $ok = fwrite( $fd, $contents ); fclose($fd); chmod($filename,0644); }else { fclose($fd); if ($debug)ADOConnection::outp( " Failed acquiring lock for $filename'; $traceArr = debug_backtrace(); array_shift($traceArr); $tabs = sizeof($traceArr)-1; foreach ($traceArr as $arr) { $args = array(); for ($i=0; $i < $tabs; $i++) $s .= ' '; $tabs -= 1; $s .= ''; if (isset($arr['class'])) $s .= $arr['class'].'.'; if (isset($arr['args'])) foreach($arr['args'] as $v) { if (is_null($v)) $args[] = 'null'; else if (is_array($v)) $args[] = 'Array['.sizeof($v).']'; else if (is_object($v)) $args[] = 'Object:'.get_class($v); else if (is_bool($v)) $args[] = $v ? 'true' : 'false'; else { $v = (string) @$v; $str = htmlspecialchars(substr($v,0,$MAXSTRLEN)); if (strlen($v) > $MAXSTRLEN) $str .= '...'; $args[] = $str; } } $s .= $arr['function'].'('.implode(', ',$args).')'; $s .= @sprintf(" # line %4d, file: %s", $arr['line'],$arr['file'],$arr['file']); $s .= "\n"; } $s .= ''; if ($print) print $s; } return $s; } } // defined ?>