mirror of
https://github.com/moodle/moodle.git
synced 2025-01-22 16:18:24 +01:00
3355 lines
94 KiB
PHP
3355 lines
94 KiB
PHP
<?php
|
||
|
||
/*
|
||
* Set tabs to 4 for best viewing.
|
||
*
|
||
* Latest version is available at http://php.weblogs.com
|
||
*
|
||
* This is the main include file for ADOdb.
|
||
* Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
|
||
*
|
||
* The ADOdb files are formatted so that doxygen can be used to generate documentation.
|
||
* Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/
|
||
*/
|
||
|
||
/**
|
||
\mainpage
|
||
|
||
@version V3.60 16 June 2003 (c) 2000-2003 John Lim (jlim\@natsoft.com.my). All rights reserved.
|
||
|
||
Released under both BSD license and Lesser GPL library license.
|
||
Whenever there is any discrepancy between the two licenses,
|
||
the BSD license will take precedence.
|
||
|
||
PHP's database access functions are not standardised. This creates a need for a database
|
||
class library to hide the differences between the different database API's (encapsulate
|
||
the differences) so we can easily switch databases.
|
||
|
||
We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere,
|
||
Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
|
||
ADO and ODBC. We have had successful reports of connecting to Progress and DB2 via ODBC.
|
||
We hope more people will contribute drivers to support other databases.
|
||
|
||
Latest Download at http://php.weblogs.com/adodb<br>
|
||
Manual is at http://php.weblogs.com/adodb_manual
|
||
|
||
*/
|
||
|
||
if (!defined('_ADODB_LAYER')) {
|
||
define('_ADODB_LAYER',1);
|
||
|
||
/* ============================================================================================== */
|
||
/* CONSTANT DEFINITIONS */
|
||
/* ============================================================================================== */
|
||
|
||
define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
|
||
|
||
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 .= "<br>\n";
|
||
|
||
if (isset($HTTP_SERVER_VARS['HTTP_USER_AGENT'])) echo $msg;
|
||
else echo strip_tags($msg);
|
||
flush();
|
||
}
|
||
|
||
/**
|
||
* Connect to database
|
||
*
|
||
* @param [argHostname] Host to connect to
|
||
* @param [argUsername] Userid to login
|
||
* @param [argPassword] Associated password
|
||
* @param [argDatabaseName] database
|
||
* @param [forceNew] force new connection
|
||
*
|
||
* @return true or false
|
||
*/
|
||
function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false)
|
||
{
|
||
if ($argHostname != "") $this->host = $argHostname;
|
||
if ($argUsername != "") $this->user = $argUsername;
|
||
if ($argPassword != "") $this->password = $argPassword; /* not stored for security reasons */
|
||
if ($argDatabaseName != "") $this->database = $argDatabaseName;
|
||
|
||
$this->_isPersistentConnection = false;
|
||
if ($fn = $this->raiseErrorFn) {
|
||
if ($forceNew) {
|
||
if ($this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
|
||
} else {
|
||
if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
|
||
}
|
||
$err = $this->ErrorMsg();
|
||
if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
|
||
$fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
|
||
} else {
|
||
if ($forceNew) {
|
||
if ($this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
|
||
} else {
|
||
if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
|
||
}
|
||
}
|
||
if ($this->debug) ADOConnection::outp( $this->host.': '.$this->ErrorMsg());
|
||
return false;
|
||
}
|
||
|
||
function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
|
||
{
|
||
return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
|
||
}
|
||
|
||
|
||
/**
|
||
* Always force a new connection to database - currently only works with oracle
|
||
*
|
||
* @param [argHostname] Host to connect to
|
||
* @param [argUsername] Userid to login
|
||
* @param [argPassword] Associated password
|
||
* @param [argDatabaseName] database
|
||
*
|
||
* @return true or false
|
||
*/
|
||
function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
|
||
{
|
||
return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
|
||
}
|
||
|
||
/**
|
||
* Establish persistent connect to database
|
||
*
|
||
* @param [argHostname] Host to connect to
|
||
* @param [argUsername] Userid to login
|
||
* @param [argPassword] Associated password
|
||
* @param [argDatabaseName] database
|
||
*
|
||
* @return return true or false
|
||
*/
|
||
function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
|
||
{
|
||
if (defined('ADODB_NEVER_PERSIST'))
|
||
return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
|
||
|
||
if ($argHostname != "") $this->host = $argHostname;
|
||
if ($argUsername != "") $this->user = $argUsername;
|
||
if ($argPassword != "") $this->password = $argPassword;
|
||
if ($argDatabaseName != "") $this->database = $argDatabaseName;
|
||
|
||
$this->_isPersistentConnection = true;
|
||
|
||
if ($fn = $this->raiseErrorFn) {
|
||
if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
|
||
$err = $this->ErrorMsg();
|
||
if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
|
||
$fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
|
||
} else
|
||
if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
|
||
|
||
if ($this->debug) ADOConnection::outp( $this->host.': '.$this->ErrorMsg());
|
||
return false;
|
||
}
|
||
|
||
/* Format date column in sql string given an input format that understands Y M D */
|
||
function SQLDate($fmt, $col=false)
|
||
{
|
||
if (!$col) $col = $this->sysDate;
|
||
return $col; /* child class implement */
|
||
}
|
||
|
||
/**
|
||
* Should prepare the sql statement and return the stmt resource.
|
||
* For databases that do not support this, we return the $sql. To ensure
|
||
* compatibility with databases that do not support prepare:
|
||
*
|
||
* $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
|
||
* $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
|
||
* $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
|
||
*
|
||
* @param sql SQL to send to database
|
||
*
|
||
* @return return FALSE, or the prepared statement, or the original sql if
|
||
* if the database does not support prepare.
|
||
*
|
||
*/
|
||
function Prepare($sql)
|
||
{
|
||
return $sql;
|
||
}
|
||
|
||
/**
|
||
* Some databases, eg. mssql require a different function for preparing
|
||
* stored procedures. So we cannot use Prepare().
|
||
*
|
||
* Should prepare the stored procedure and return the stmt resource.
|
||
* For databases that do not support this, we return the $sql. To ensure
|
||
* compatibility with databases that do not support prepare:
|
||
*
|
||
* @param sql SQL to send to database
|
||
*
|
||
* @return return FALSE, or the prepared statement, or the original sql if
|
||
* if the database does not support prepare.
|
||
*
|
||
*/
|
||
function PrepareSP($sql)
|
||
{
|
||
return $this->Prepare($sql);
|
||
}
|
||
|
||
/**
|
||
* PEAR DB Compat
|
||
*/
|
||
function Quote($s)
|
||
{
|
||
return $this->qstr($s,false);
|
||
}
|
||
|
||
function q(&$s)
|
||
{
|
||
$s = $this->qstr($s,false);
|
||
}
|
||
|
||
/**
|
||
* PEAR DB Compat - do not use internally.
|
||
*/
|
||
function ErrorNative()
|
||
{
|
||
return $this->ErrorNo();
|
||
}
|
||
|
||
|
||
/**
|
||
* PEAR DB Compat - do not use internally.
|
||
*/
|
||
function nextId($seq_name)
|
||
{
|
||
return $this->GenID($seq_name);
|
||
}
|
||
|
||
/**
|
||
* Lock a row, will escalate and lock the table if row locking not supported
|
||
* will normally free the lock at the end of the transaction
|
||
*
|
||
* @param $table name of table to lock
|
||
* @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
|
||
*/
|
||
function RowLock($table,$where)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
function CommitLock($table)
|
||
{
|
||
return $this->CommitTrans();
|
||
}
|
||
|
||
function RollbackLock($table)
|
||
{
|
||
return $this->RollbackTrans();
|
||
}
|
||
|
||
/**
|
||
* PEAR DB Compat - do not use internally.
|
||
*
|
||
* The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
|
||
* for easy porting :-)
|
||
*
|
||
* @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
|
||
* @returns The previous fetch mode
|
||
*/
|
||
function SetFetchMode($mode)
|
||
{
|
||
$old = $this->fetchMode;
|
||
$this->fetchMode = $mode;
|
||
|
||
if ($old === false) {
|
||
global $ADODB_FETCH_MODE;
|
||
return $ADODB_FETCH_MODE;
|
||
}
|
||
return $old;
|
||
}
|
||
|
||
|
||
/**
|
||
* PEAR DB Compat - do not use internally.
|
||
*/
|
||
function &Query($sql, $inputarr=false)
|
||
{
|
||
$rs = &$this->Execute($sql, $inputarr);
|
||
if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
|
||
return $rs;
|
||
}
|
||
|
||
|
||
/**
|
||
* PEAR DB Compat - do not use internally
|
||
*/
|
||
function &LimitQuery($sql, $offset, $count)
|
||
{
|
||
$rs = &$this->SelectLimit($sql, $count, $offset); /* swap */
|
||
if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
|
||
return $rs;
|
||
}
|
||
|
||
|
||
/**
|
||
* PEAR DB Compat - do not use internally
|
||
*/
|
||
function Disconnect()
|
||
{
|
||
return $this->Close();
|
||
}
|
||
|
||
/*
|
||
Usage in oracle
|
||
$stmt = $db->Prepare('select * from table where id =:myid and group=:group');
|
||
$db->Parameter($stmt,$id,'myid');
|
||
$db->Parameter($stmt,$group,'group',64);
|
||
$db->Execute();
|
||
|
||
@param $stmt Statement returned by Prepare() or PrepareSP().
|
||
@param $var PHP variable to bind to
|
||
@param $name Name of stored procedure variable name to bind to.
|
||
@param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
|
||
@param [$maxLen] Holds an maximum length of the variable.
|
||
@param [$type] The data type of $var. Legal values depend on driver.
|
||
|
||
*/
|
||
function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
Improved method of initiating a transaction. Used together with CompleteTrans().
|
||
Advantages include:
|
||
|
||
a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
|
||
Only the outermost block is treated as a transaction.<br>
|
||
b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
|
||
c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
|
||
are disabled, making it backward compatible.
|
||
*/
|
||
function StartTrans($errfn = 'ADODB_TransMonitor')
|
||
{
|
||
|
||
if ($this->transOff > 0) {
|
||
$this->transOff += 1;
|
||
return;
|
||
}
|
||
|
||
$this->_oldRaiseFn = $this->raiseErrorFn;
|
||
$this->raiseErrorFn = $errfn;
|
||
$this->_transOK = true;
|
||
|
||
if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
|
||
$this->BeginTrans();
|
||
$this->transOff = 1;
|
||
}
|
||
|
||
/**
|
||
Used together with StartTrans() to end a transaction. Monitors connection
|
||
for sql errors, and will commit or rollback as appropriate.
|
||
|
||
@autoComplete if true, monitor sql errors and commit and rollback as appropriate,
|
||
and if set to false force rollback even if no SQL error detected.
|
||
@returns true on commit, false on rollback.
|
||
*/
|
||
function CompleteTrans($autoComplete = true)
|
||
{
|
||
if ($this->transOff > 1) {
|
||
$this->transOff -= 1;
|
||
return true;
|
||
}
|
||
$this->raiseErrorFn = $this->_oldRaiseFn;
|
||
|
||
$this->transOff = 0;
|
||
if ($this->_transOK && $autoComplete) $this->CommitTrans();
|
||
else $this->RollbackTrans();
|
||
|
||
return $this->_transOK;
|
||
}
|
||
|
||
/*
|
||
At the end of a StartTrans/CompleteTrans block, perform a rollback.
|
||
*/
|
||
function FailTrans()
|
||
{
|
||
if ($this->debug && $this->transOff == 0) {
|
||
ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
|
||
}
|
||
$this->_transOK = false;
|
||
}
|
||
/**
|
||
* Execute SQL
|
||
*
|
||
* @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
|
||
* @param [inputarr] holds the input data to bind to. Null elements will be set to null.
|
||
* @param [arg3] reserved for john lim for future use
|
||
* @return RecordSet or false
|
||
*/
|
||
function &Execute($sql,$inputarr=false,$arg3=false)
|
||
{
|
||
if ($this->fnExecute) {
|
||
$fn = $this->fnExecute;
|
||
$fn($this,$sql,$inputarr);
|
||
}
|
||
if (!$this->_bindInputArray && $inputarr) {
|
||
$sqlarr = explode('?',$sql);
|
||
$sql = '';
|
||
$i = 0;
|
||
foreach($inputarr as $v) {
|
||
|
||
$sql .= $sqlarr[$i];
|
||
/* from Ron Baldwin <ron.baldwin@sourceprose.com> */
|
||
/* Only quote string types */
|
||
if (gettype($v) == 'string')
|
||
$sql .= $this->qstr($v);
|
||
else if ($v === null)
|
||
$sql .= 'NULL';
|
||
else
|
||
$sql .= $v;
|
||
$i += 1;
|
||
|
||
}
|
||
$sql .= $sqlarr[$i];
|
||
if ($i+1 != sizeof($sqlarr))
|
||
ADOConnection::outp( "Input Array does not match ?: ".htmlspecialchars($sql));
|
||
$inputarr = false;
|
||
}
|
||
/* debug version of query */
|
||
if ($this->debug) {
|
||
global $HTTP_SERVER_VARS;
|
||
|
||
$ss = '';
|
||
if ($inputarr) {
|
||
foreach ($inputarr as $kk => $vv) {
|
||
if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...';
|
||
$ss .= "($kk=>'$vv') ";
|
||
}
|
||
$ss = "[ $ss ]";
|
||
}
|
||
$sqlTxt = str_replace(',',', ',is_array($sql) ?$sql[0] : $sql);
|
||
|
||
/* check if running from browser or command-line */
|
||
$inBrowser = isset($HTTP_SERVER_VARS['HTTP_USER_AGENT']);
|
||
|
||
if ($inBrowser)
|
||
ADOConnection::outp( "<hr />\n($this->databaseType): ".htmlspecialchars($sqlTxt)." <code>$ss</code>\n<hr />\n",false);
|
||
else
|
||
ADOConnection::outp( "=----\n($this->databaseType): ".($sqlTxt)." \n-----\n",false);
|
||
flush();
|
||
|
||
$this->_queryID = $this->_query($sql,$inputarr,$arg3);
|
||
|
||
/*
|
||
Alexios Fakios notes that ErrorMsg() must be called before ErrorNo() for mssql
|
||
because ErrorNo() calls Execute('SELECT @ERROR'), causing recure
|
||
*/
|
||
if ($this->databaseType == 'mssql') {
|
||
/* ErrorNo is a slow function call in mssql, and not reliable */
|
||
/* in PHP 4.0.6 */
|
||
if($emsg = $this->ErrorMsg()) {
|
||
$err = $this->ErrorNo();
|
||
if ($err) {
|
||
ADOConnection::outp($err.': '.$emsg);
|
||
flush();
|
||
}
|
||
}
|
||
} else
|
||
if (!$this->_queryID) {
|
||
$e = $this->ErrorNo();
|
||
$m = $this->ErrorMsg();
|
||
ADOConnection::outp($e .': '. $m );
|
||
flush();
|
||
}
|
||
} else {
|
||
/* non-debug version of query */
|
||
|
||
$this->_queryID =@$this->_query($sql,$inputarr,$arg3);
|
||
|
||
}
|
||
/* 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( '<p>Insert_ID error</p>');
|
||
return false;
|
||
}
|
||
|
||
|
||
/**
|
||
* Portable Insert ID. Pablo Roca <pabloroca@mvps.org>
|
||
*
|
||
* @return the last inserted ID. All databases support this. But aware possible
|
||
* problems in multiuser environments. Heavy test this before deploying.
|
||
*/
|
||
function PO_Insert_ID($table="", $id="")
|
||
{
|
||
if ($this->hasInsertID){
|
||
return $this->Insert_ID();
|
||
} else {
|
||
return $this->GetOne("SELECT MAX($id) FROM $table");
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @return # rows affected by UPDATE/DELETE
|
||
*/
|
||
function Affected_Rows()
|
||
{
|
||
if ($this->hasAffectedRows) {
|
||
$val = $this->_affectedrows();
|
||
return ($val < 0) ? false : $val;
|
||
}
|
||
|
||
if ($this->debug) ADOConnection::outp( '<p>Affected_Rows error</p>',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 "<p>Error=".$this->ErrorNo().'<p>'; */
|
||
$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<br><pre>\n", system($cmd),"</pre>");
|
||
} 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 = '<i>'.htmlspecialchars($s).'</i>';
|
||
|
||
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" <jyounger@unilab.com>
|
||
*/
|
||
function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false)
|
||
{
|
||
include_once(ADODB_DIR.'/adodb-lib.inc.php');
|
||
return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq);
|
||
}
|
||
|
||
|
||
/**
|
||
* Generates an Insert 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.
|
||
*/
|
||
function GetInsertSQL(&$rs, $arrFields,$magicq=false)
|
||
{
|
||
include_once(ADODB_DIR.'/adodb-lib.inc.php');
|
||
return _adodb_getinsertsql($this,$rs,$arrFields,$magicq);
|
||
}
|
||
|
||
|
||
/**
|
||
* Update a blob column, given a where clause. There are more sophisticated
|
||
* blob handling functions that we could have implemented, but all require
|
||
* a very complex API. Instead we have chosen something that is extremely
|
||
* simple to understand and use.
|
||
*
|
||
* Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
|
||
*
|
||
* Usage to update a $blobvalue which has a primary key blob_id=1 into a
|
||
* field blobtable.blobcolumn:
|
||
*
|
||
* UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
|
||
*
|
||
* Insert example:
|
||
*
|
||
* $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
|
||
* $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
|
||
*/
|
||
|
||
function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
|
||
{
|
||
return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
|
||
}
|
||
|
||
/**
|
||
* Usage:
|
||
* UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
|
||
*
|
||
* $blobtype supports 'BLOB' and 'CLOB'
|
||
*
|
||
* $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
|
||
* $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
|
||
*/
|
||
function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
|
||
{
|
||
$fd = fopen($path,'rb');
|
||
if ($fd === false) return false;
|
||
$val = fread($fd,filesize($path));
|
||
fclose($fd);
|
||
return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
|
||
}
|
||
|
||
function BlobDecode($blob)
|
||
{
|
||
return $blob;
|
||
}
|
||
|
||
function BlobEncode($blob)
|
||
{
|
||
return $blob;
|
||
}
|
||
|
||
/**
|
||
* Usage:
|
||
* UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
|
||
*
|
||
* $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
|
||
* $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
|
||
*/
|
||
function UpdateClob($table,$column,$val,$where)
|
||
{
|
||
return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
|
||
}
|
||
|
||
|
||
/**
|
||
* $meta contains the desired type, which could be...
|
||
* C for character. You will have to define the precision yourself.
|
||
* X for teXt. For unlimited character lengths.
|
||
* B for Binary
|
||
* F for floating point, with no need to define scale and precision
|
||
* N for decimal numbers, you will have to define the (scale, precision) yourself
|
||
* D for date
|
||
* T for timestamp
|
||
* L for logical/Boolean
|
||
* I for integer
|
||
* R for autoincrement counter/integer
|
||
* and if you want to use double-byte, add a 2 to the end, like C2 or X2.
|
||
*
|
||
*
|
||
* @return the actual type of the data or false if no such type available
|
||
*/
|
||
function ActualType($meta)
|
||
{
|
||
switch($meta) {
|
||
case 'C':
|
||
case 'X':
|
||
return 'VARCHAR';
|
||
case 'B':
|
||
|
||
case 'D':
|
||
case 'T':
|
||
case 'L':
|
||
|
||
case 'R':
|
||
|
||
case 'I':
|
||
case 'N':
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Maximum size of C field
|
||
*/
|
||
function CharMax()
|
||
{
|
||
return 255; /* make it conservative if not defined */
|
||
}
|
||
|
||
|
||
/*
|
||
* Maximum size of X field
|
||
*/
|
||
function TextMax()
|
||
{
|
||
return 4000; /* make it conservative if not defined */
|
||
}
|
||
|
||
/**
|
||
* Close Connection
|
||
*/
|
||
function Close()
|
||
{
|
||
return $this->_close();
|
||
|
||
/* "Simon Lee" <simon@mediaroad.com> reports that persistent connections need */
|
||
/* to be closed too! */
|
||
/* if ($this->_isPersistentConnection != true) return $this->_close(); */
|
||
/* else return true; */
|
||
}
|
||
|
||
/**
|
||
* Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
|
||
*
|
||
* @return true if succeeded or false if database does not support transactions
|
||
*/
|
||
function BeginTrans() {return false;}
|
||
|
||
|
||
/**
|
||
* If database does not support transactions, always return true as data always commited
|
||
*
|
||
* @param $ok set to false to rollback transaction, true to commit
|
||
*
|
||
* @return true/false.
|
||
*/
|
||
function CommitTrans($ok=true)
|
||
{ return true;}
|
||
|
||
|
||
/**
|
||
* If database does not support transactions, rollbacks always fail, so return false
|
||
*
|
||
* @return true/false.
|
||
*/
|
||
function RollbackTrans()
|
||
{ return false;}
|
||
|
||
|
||
/**
|
||
* return the databases that the driver can connect to.
|
||
* Some databases will return an empty array.
|
||
*
|
||
* @return an array of database names.
|
||
*/
|
||
function MetaDatabases()
|
||
{
|
||
global $ADODB_FETCH_MODE;
|
||
|
||
if ($this->metaDatabasesSQL) {
|
||
$save = $ADODB_FETCH_MODE;
|
||
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
|
||
|
||
if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
|
||
|
||
$arr = $this->GetCol($this->metaDatabasesSQL);
|
||
if (isset($savem)) $this->SetFetchMode($savem);
|
||
$ADODB_FETCH_MODE = $save;
|
||
|
||
return $arr;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* @return array of tables for current database.
|
||
*/
|
||
function &MetaTables()
|
||
{
|
||
global $ADODB_FETCH_MODE;
|
||
|
||
if ($this->metaTablesSQL) {
|
||
/* complicated state saving by the need for backward compat */
|
||
$save = $ADODB_FETCH_MODE;
|
||
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
|
||
|
||
if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
|
||
|
||
$rs = $this->Execute($this->metaTablesSQL);
|
||
if (isset($savem)) $this->SetFetchMode($savem);
|
||
$ADODB_FETCH_MODE = $save;
|
||
|
||
if ($rs === false) return false;
|
||
$arr =& $rs->GetArray();
|
||
$arr2 = array();
|
||
for ($i=0; $i < sizeof($arr); $i++) {
|
||
$arr2[] = $arr[$i][0];
|
||
}
|
||
$rs->Close();
|
||
return $arr2;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
|
||
/**
|
||
* List columns in a database as an array of ADOFieldObjects.
|
||
* See top of file for definition of object.
|
||
*
|
||
* @param table table name to query
|
||
* @param upper uppercase table name (required by some databases)
|
||
*
|
||
* @return array of ADOFieldObjects for current table.
|
||
*/
|
||
function &MetaColumns($table,$upper=true)
|
||
{
|
||
global $ADODB_FETCH_MODE;
|
||
|
||
if (!empty($this->metaColumnsSQL)) {
|
||
$save = $ADODB_FETCH_MODE;
|
||
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
|
||
if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
|
||
$rs = $this->Execute(sprintf($this->metaColumnsSQL,($upper)?strtoupper($table):$table));
|
||
if (isset($savem)) $this->SetFetchMode($savem);
|
||
$ADODB_FETCH_MODE = $save;
|
||
if ($rs === false) return false;
|
||
|
||
$retarr = array();
|
||
while (!$rs->EOF) { /* print_r($rs->fields); */
|
||
$fld = new ADOFieldObject();
|
||
$fld->name = $rs->fields[0];
|
||
$fld->type = $rs->fields[1];
|
||
if (isset($rs->fields[3]) && $rs->fields[3]) {
|
||
if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3];
|
||
$fld->scale = $rs->fields[4];
|
||
if ($fld->scale>0) $fld->max_length += 1;
|
||
} else
|
||
$fld->max_length = $rs->fields[2];
|
||
|
||
$retarr[strtoupper($fld->name)] = $fld;
|
||
|
||
$rs->MoveNext();
|
||
}
|
||
$rs->Close();
|
||
return $retarr;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* List columns names in a table as an array.
|
||
* @param table table name to query
|
||
*
|
||
* @return array of column names for current table.
|
||
*/
|
||
function &MetaColumnNames($table)
|
||
{
|
||
$objarr =& $this->MetaColumns($table);
|
||
if (!is_array($objarr)) return false;
|
||
|
||
$arr = array();
|
||
foreach($objarr as $v) {
|
||
$arr[] = $v->name;
|
||
}
|
||
return $arr;
|
||
}
|
||
|
||
/**
|
||
* Different SQL databases used different methods to combine strings together.
|
||
* This function provides a wrapper.
|
||
*
|
||
* param s variable number of string parameters
|
||
*
|
||
* Usage: $db->Concat($str1,$str2);
|
||
*
|
||
* @return concatenated string
|
||
*/
|
||
function Concat()
|
||
{
|
||
$arr = func_get_args();
|
||
return implode($this->concat_operator, $arr);
|
||
}
|
||
|
||
|
||
/**
|
||
* Converts a date "d" to a string that the database can understand.
|
||
*
|
||
* @param d a date in Unix date time format.
|
||
*
|
||
* @return date string in database date format
|
||
*/
|
||
function DBDate($d)
|
||
{
|
||
|
||
if (empty($d) && $d !== 0) return 'null';
|
||
|
||
if (is_string($d) && !is_numeric($d)) {
|
||
if ($d === 'null') return $d;
|
||
if ($this->isoDates) return "'$d'";
|
||
$d = ADOConnection::UnixDate($d);
|
||
}
|
||
|
||
return adodb_date($this->fmtDate,$d);
|
||
}
|
||
|
||
|
||
/**
|
||
* Converts a timestamp "ts" to a string that the database can understand.
|
||
*
|
||
* @param ts a timestamp in Unix date time format.
|
||
*
|
||
* @return timestamp string in database timestamp format
|
||
*/
|
||
function DBTimeStamp($ts)
|
||
{
|
||
if (empty($ts) && $ts !== 0) return 'null';
|
||
|
||
if (is_string($ts) && !is_numeric($ts)) {
|
||
if ($ts === 'null') return $ts;
|
||
if ($this->isoDates) return "'$ts'";
|
||
else $ts = ADOConnection::UnixTimeStamp($ts);
|
||
}
|
||
|
||
return adodb_date($this->fmtTimeStamp,$ts);
|
||
}
|
||
|
||
/**
|
||
* Also in ADORecordSet.
|
||
* @param $v is a date string in YYYY-MM-DD format
|
||
*
|
||
* @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
|
||
*/
|
||
function UnixDate($v)
|
||
{
|
||
if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|",
|
||
($v), $rr)) return false;
|
||
|
||
if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0;
|
||
/* h-m-s-MM-DD-YY */
|
||
return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
|
||
}
|
||
|
||
|
||
/**
|
||
* Also in ADORecordSet.
|
||
* @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
|
||
*
|
||
* @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
|
||
*/
|
||
function UnixTimeStamp($v)
|
||
{
|
||
if (!preg_match(
|
||
"|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
|
||
($v), $rr)) return false;
|
||
if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0;
|
||
|
||
/* h-m-s-MM-DD-YY */
|
||
if (!isset($rr[5])) return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
|
||
return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
|
||
}
|
||
|
||
/**
|
||
* Also in ADORecordSet.
|
||
*
|
||
* Format database date based on user defined format.
|
||
*
|
||
* @param v is the character date in YYYY-MM-DD format, returned by database
|
||
* @param fmt is the format to apply to it, using date()
|
||
*
|
||
* @return a date formated as user desires
|
||
*/
|
||
|
||
function UserDate($v,$fmt='Y-m-d')
|
||
{
|
||
$tt = $this->UnixDate($v);
|
||
/* $tt == -1 if pre TIMESTAMP_FIRST_YEAR */
|
||
if (($tt === false || $tt == -1) && $v != false) return $v;
|
||
else if ($tt == 0) return $this->emptyDate;
|
||
else if ($tt == -1) { /* pre-TIMESTAMP_FIRST_YEAR */
|
||
}
|
||
|
||
return adodb_date($fmt,$tt);
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* Correctly quotes a string so that all strings are escaped. We prefix and append
|
||
* to the string single-quotes.
|
||
* An example is $db->qstr("Don't bother",magic_quotes_runtime());
|
||
*
|
||
* @param s the string to quote
|
||
* @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
|
||
* This undoes the stupidity of magic quotes for GPC.
|
||
*
|
||
* @return quoted string to be sent back to database
|
||
*/
|
||
function qstr($s,$magic_quotes=false)
|
||
{
|
||
if (!$magic_quotes) {
|
||
|
||
if ($this->replaceQuote[0] == '\\'){
|
||
/* only since php 4.0.5 */
|
||
$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
|
||
/* $s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s)); */
|
||
}
|
||
return "'".str_replace("'",$this->replaceQuote,$s)."'";
|
||
}
|
||
|
||
/* undo magic quotes for " */
|
||
$s = str_replace('\\"','"',$s);
|
||
|
||
if ($this->replaceQuote == "\\'") /* ' already quoted, no need to change anything */
|
||
return "'$s'";
|
||
else {/* change \' to '' for sybase/mssql */
|
||
$s = str_replace('\\\\','\\',$s);
|
||
return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Will select the supplied $page number from a recordset, given that it is paginated in pages of
|
||
* $nrows rows per page. It also saves two boolean values saying if the given page is the first
|
||
* and/or last one of the recordset. Added by Iv<49>n Oliva to provide recordset pagination.
|
||
*
|
||
* See readme.htm#ex8 for an example of usage.
|
||
*
|
||
* @param sql
|
||
* @param nrows is the number of rows per page to get
|
||
* @param page is the page number to get (1-based)
|
||
* @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')
|
||
*
|
||
* NOTE: phpLens uses a different algorithm and does not use PageExecute().
|
||
*
|
||
*/
|
||
function &PageExecute($sql, $nrows, $page, $inputarr=false, $arg3=false, $secs2cache=0)
|
||
{
|
||
include_once(ADODB_DIR.'/adodb-lib.inc.php');
|
||
if ($this->pageExecuteCountRows) return _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $arg3, $secs2cache);
|
||
return _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $arg3, $secs2cache);
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* Will select the supplied $page number from a recordset, given that it is paginated in pages of
|
||
* $nrows rows per page. It also saves two boolean values saying if the given page is the first
|
||
* and/or last one of the recordset. Added by Iv<49>n Oliva to provide recordset pagination.
|
||
*
|
||
* @param secs2cache seconds to cache data, set to 0 to force query
|
||
* @param sql
|
||
* @param nrows is the number of rows per page to get
|
||
* @param page is the page number to get (1-based)
|
||
* @param [inputarr] array of bind variables
|
||
* @param [arg3] is a private parameter only used by jlim
|
||
* @return the recordset ($rs->databaseType == 'array')
|
||
*/
|
||
function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false, $arg3=false)
|
||
{
|
||
/*switch($this->dataProvider) {
|
||
case 'postgres':
|
||
case 'mysql':
|
||
break;
|
||
default: $secs2cache = 0; break;
|
||
}*/
|
||
return $this->PageExecute($sql,$nrows,$page,$inputarr,$arg3,$secs2cache);
|
||
}
|
||
|
||
} /* end class ADOConnection */
|
||
|
||
|
||
|
||
/* ============================================================================================== */
|
||
/* CLASS ADOFetchObj */
|
||
/* ============================================================================================== */
|
||
|
||
/**
|
||
* Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
|
||
*/
|
||
class ADOFetchObj {
|
||
};
|
||
|
||
/* ============================================================================================== */
|
||
/* CLASS ADORecordSet_empty */
|
||
/* ============================================================================================== */
|
||
|
||
/**
|
||
* Lightweight recordset when there are no records to be returned
|
||
*/
|
||
class ADORecordSet_empty
|
||
{
|
||
var $dataProvider = 'empty';
|
||
var $databaseType = false;
|
||
var $EOF = true;
|
||
var $_numOfRows = 0;
|
||
var $fields = false;
|
||
var $connection = false;
|
||
function RowCount() {return 0;}
|
||
function RecordCount() {return 0;}
|
||
function PO_RecordCount(){return 0;}
|
||
function Close(){return true;}
|
||
function FetchRow() {return false;}
|
||
function FieldCount(){ return 0;}
|
||
}
|
||
|
||
/* ============================================================================================== */
|
||
/* DATE AND TIME FUNCTIONS */
|
||
/* ============================================================================================== */
|
||
include_once(ADODB_DIR.'/adodb-time.inc.php');
|
||
|
||
/* ============================================================================================== */
|
||
/* CLASS ADORecordSet */
|
||
/* ============================================================================================== */
|
||
|
||
|
||
/**
|
||
* RecordSet class that represents the dataset returned by the database.
|
||
* To keep memory overhead low, this class holds only the current row in memory.
|
||
* No prefetching of data is done, so the RecordCount() can return -1 ( which
|
||
* means recordcount not known).
|
||
*/
|
||
class ADORecordSet {
|
||
/*
|
||
* public variables
|
||
*/
|
||
var $dataProvider = "native";
|
||
var $fields = false; /* / holds the current row data */
|
||
var $blobSize = 100; /* / any varchar/char field this size or greater is treated as a blob */
|
||
/* / in other words, we use a text area for editting. */
|
||
var $canSeek = false; /* / indicates that seek is supported */
|
||
var $sql; /* / sql text */
|
||
var $EOF = false; /* / Indicates that the current record position is after the last record in a Recordset object. */
|
||
|
||
var $emptyTimeStamp = ' '; /* / what to display when $time==0 */
|
||
var $emptyDate = ' '; /* / what to display when $time==0 */
|
||
var $debug = false;
|
||
var $timeCreated=0; /* / datetime in Unix format rs created -- for cached recordsets */
|
||
|
||
var $bind = false; /* / used by Fields() to hold array - should be private? */
|
||
var $fetchMode; /* / default fetch mode */
|
||
var $connection = false; /* / the parent connection */
|
||
/*
|
||
* private variables
|
||
*/
|
||
var $_numOfRows = -1; /** number of rows, or -1 */
|
||
var $_numOfFields = -1; /** number of fields in recordset */
|
||
var $_queryID = -1; /** This variable keeps the result link identifier. */
|
||
var $_currentRow = -1; /** This variable keeps the current row in the Recordset. */
|
||
var $_closed = false; /** has recordset been closed */
|
||
var $_inited = false; /** Init() should only be called once */
|
||
var $_obj; /** Used by FetchObj */
|
||
var $_names; /** Used by FetchObj */
|
||
|
||
var $_currentPage = -1; /** Added by Iv<49>n Oliva to implement recordset pagination */
|
||
var $_atFirstPage = false; /** Added by Iv<49>n Oliva to implement recordset pagination */
|
||
var $_atLastPage = false; /** Added by Iv<49>n Oliva to implement recordset pagination */
|
||
var $_lastPageNo = -1;
|
||
var $_maxRecordCount = 0;
|
||
var $dateHasTime = false;
|
||
|
||
/**
|
||
* Constructor
|
||
*
|
||
* @param queryID this is the queryID returned by ADOConnection->_query()
|
||
*
|
||
*/
|
||
function ADORecordSet($queryID)
|
||
{
|
||
$this->_queryID = $queryID;
|
||
}
|
||
|
||
|
||
|
||
function Init()
|
||
{
|
||
if ($this->_inited) return;
|
||
$this->_inited = true;
|
||
if ($this->_queryID) @$this->_initrs();
|
||
else {
|
||
$this->_numOfRows = 0;
|
||
$this->_numOfFields = 0;
|
||
}
|
||
|
||
if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
|
||
|
||
$this->_currentRow = 0;
|
||
if ($this->EOF = ($this->_fetch() === false)) {
|
||
$this->_numOfRows = 0; /* _numOfRows could be -1 */
|
||
}
|
||
} else {
|
||
$this->EOF = true;
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Generate a SELECT tag string from a recordset, and return the string.
|
||
* If the recordset has 2 cols, we treat the 1st col as the containing
|
||
* the text to display to the user, and 2nd col as the return value. Default
|
||
* strings are compared with the FIRST column.
|
||
*
|
||
* @param name name of SELECT tag
|
||
* @param [defstr] the value to hilite. Use an array for multiple hilites for listbox.
|
||
* @param [blank1stItem] true to leave the 1st item in list empty
|
||
* @param [multiple] true for listbox, false for popup
|
||
* @param [size] #rows to show for listbox. not used by popup
|
||
* @param [selectAttr] additional attributes to defined for SELECT tag.
|
||
* useful for holding javascript onChange='...' handlers.
|
||
& @param [compareFields0] when we have 2 cols in recordset, we compare the defstr with
|
||
* column 0 (1st col) if this is true. This is not documented.
|
||
*
|
||
* @return HTML
|
||
*
|
||
* changes by glen.davies@cce.ac.nz to support multiple hilited items
|
||
*/
|
||
function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
|
||
$size=0, $selectAttr='',$compareFields0=true)
|
||
{
|
||
include_once(ADODB_DIR.'/adodb-lib.inc.php');
|
||
return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
|
||
$size, $selectAttr,$compareFields0);
|
||
}
|
||
|
||
/**
|
||
* Generate a SELECT tag string from a recordset, and return the string.
|
||
* If the recordset has 2 cols, we treat the 1st col as the containing
|
||
* the text to display to the user, and 2nd col as the return value. Default
|
||
* strings are compared with the SECOND column.
|
||
*
|
||
*/
|
||
function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')
|
||
{
|
||
include_once(ADODB_DIR.'/adodb-lib.inc.php');
|
||
return _adodb_getmenu($this,$name,$defstr,$blank1stItem,$multiple,
|
||
$size, $selectAttr,false);
|
||
}
|
||
|
||
|
||
/**
|
||
* return recordset as a 2-dimensional array.
|
||
*
|
||
* @param [nRows] is the number of rows to return. -1 means every row.
|
||
*
|
||
* @return an array indexed by the rows (0-based) from the recordset
|
||
*/
|
||
function &GetArray($nRows = -1)
|
||
{
|
||
global $ADODB_EXTENSION; if ($ADODB_EXTENSION) return adodb_getall($this,$nRows);
|
||
|
||
$results = array();
|
||
$cnt = 0;
|
||
while (!$this->EOF && $nRows != $cnt) {
|
||
$results[] = $this->fields;
|
||
$this->MoveNext();
|
||
$cnt++;
|
||
}
|
||
return $results;
|
||
}
|
||
|
||
/*
|
||
* Some databases allow multiple recordsets to be returned. This function
|
||
* will return true if there is a next recordset, or false if no more.
|
||
*/
|
||
function NextRecordSet()
|
||
{
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* return recordset as a 2-dimensional array.
|
||
* Helper function for ADOConnection->SelectLimit()
|
||
*
|
||
* @param offset is the row to start calculations from (1-based)
|
||
* @param [nrows] is the number of rows to return
|
||
*
|
||
* @return an array indexed by the rows (0-based) from the recordset
|
||
*/
|
||
function &GetArrayLimit($nrows,$offset=-1)
|
||
{
|
||
if ($offset <= 0) {
|
||
return $this->GetArray($nrows);
|
||
}
|
||
|
||
$this->Move($offset);
|
||
|
||
$results = array();
|
||
$cnt = 0;
|
||
while (!$this->EOF && $nrows != $cnt) {
|
||
$results[$cnt++] = $this->fields;
|
||
$this->MoveNext();
|
||
}
|
||
|
||
return $results;
|
||
}
|
||
|
||
|
||
/**
|
||
* Synonym for GetArray() for compatibility with ADO.
|
||
*
|
||
* @param [nRows] is the number of rows to return. -1 means every row.
|
||
*
|
||
* @return an array indexed by the rows (0-based) from the recordset
|
||
*/
|
||
function &GetRows($nRows = -1)
|
||
{
|
||
return $this->GetArray($nRows);
|
||
}
|
||
|
||
/**
|
||
* return whole recordset as a 2-dimensional associative array if there are more than 2 columns.
|
||
* The first column is treated as the key and is not included in the array.
|
||
* If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
|
||
* $force_array == true.
|
||
*
|
||
* @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
|
||
* array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
|
||
* read the source.
|
||
*
|
||
* @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and
|
||
* instead of returning array[col0] => array(remaining cols), return array[col0] => col1
|
||
*
|
||
* @return an associative array indexed by the first column of the array,
|
||
* or false if the data has less than 2 cols.
|
||
*/
|
||
function &GetAssoc($force_array = false, $first2cols = false) {
|
||
$cols = $this->_numOfFields;
|
||
if ($cols < 2) {
|
||
return false;
|
||
}
|
||
$numIndex = isset($this->fields[0]);
|
||
$results = array();
|
||
|
||
if (!$first2cols && ($cols > 2 || $force_array)) {
|
||
if ($numIndex) {
|
||
while (!$this->EOF) {
|
||
$results[trim($this->fields[0])] = array_slice($this->fields, 1);
|
||
$this->MoveNext();
|
||
}
|
||
} else {
|
||
while (!$this->EOF) {
|
||
$results[trim(reset($this->fields))] = array_slice($this->fields, 1);
|
||
$this->MoveNext();
|
||
}
|
||
}
|
||
} else {
|
||
/* return scalar values */
|
||
if ($numIndex) {
|
||
while (!$this->EOF) {
|
||
/* some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string */
|
||
$results[trim(($this->fields[0]))] = $this->fields[1];
|
||
$this->MoveNext();
|
||
}
|
||
} else {
|
||
while (!$this->EOF) {
|
||
/* some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string */
|
||
$v1 = trim(reset($this->fields));
|
||
$v2 = ''.next($this->fields);
|
||
$results[$v1] = $v2;
|
||
$this->MoveNext();
|
||
}
|
||
}
|
||
}
|
||
return $results;
|
||
}
|
||
|
||
|
||
/**
|
||
*
|
||
* @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format
|
||
* @param fmt is the format to apply to it, using date()
|
||
*
|
||
* @return a timestamp formated as user desires
|
||
*/
|
||
function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
|
||
{
|
||
$tt = $this->UnixTimeStamp($v);
|
||
/* $tt == -1 if pre TIMESTAMP_FIRST_YEAR */
|
||
if (($tt === false || $tt == -1) && $v != false) return $v;
|
||
if ($tt == 0) return $this->emptyTimeStamp;
|
||
return adodb_date($fmt,$tt);
|
||
}
|
||
|
||
|
||
/**
|
||
* @param v is the character date in YYYY-MM-DD format, returned by database
|
||
* @param fmt is the format to apply to it, using date()
|
||
*
|
||
* @return a date formated as user desires
|
||
*/
|
||
function UserDate($v,$fmt='Y-m-d')
|
||
{
|
||
$tt = $this->UnixDate($v);
|
||
/* $tt == -1 if pre TIMESTAMP_FIRST_YEAR */
|
||
if (($tt === false || $tt == -1) && $v != false) return $v;
|
||
else if ($tt == 0) return $this->emptyDate;
|
||
else if ($tt == -1) { /* pre-TIMESTAMP_FIRST_YEAR */
|
||
}
|
||
return adodb_date($fmt,$tt);
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* @param $v is a date string in YYYY-MM-DD format
|
||
*
|
||
* @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
|
||
*/
|
||
function UnixDate($v)
|
||
{
|
||
|
||
if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|",
|
||
($v), $rr)) return false;
|
||
|
||
if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0;
|
||
/* h-m-s-MM-DD-YY */
|
||
return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
|
||
}
|
||
|
||
|
||
/**
|
||
* @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
|
||
*
|
||
* @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
|
||
*/
|
||
function UnixTimeStamp($v)
|
||
{
|
||
|
||
if (!preg_match(
|
||
"|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
|
||
($v), $rr)) return false;
|
||
if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0;
|
||
|
||
/* h-m-s-MM-DD-YY */
|
||
if (!isset($rr[5])) return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
|
||
return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
|
||
}
|
||
|
||
|
||
/**
|
||
* PEAR DB Compat - do not use internally
|
||
*/
|
||
function Free()
|
||
{
|
||
return $this->Close();
|
||
}
|
||
|
||
|
||
/**
|
||
* PEAR DB compat, number of rows
|
||
*/
|
||
function NumRows()
|
||
{
|
||
return $this->_numOfRows;
|
||
}
|
||
|
||
|
||
/**
|
||
* PEAR DB compat, number of cols
|
||
*/
|
||
function NumCols()
|
||
{
|
||
return $this->_numOfFields;
|
||
}
|
||
|
||
/**
|
||
* Fetch a row, returning false if no more rows.
|
||
* This is PEAR DB compat mode.
|
||
*
|
||
* @return false or array containing the current record
|
||
*/
|
||
function FetchRow()
|
||
{
|
||
if ($this->EOF) return false;
|
||
$arr = $this->fields;
|
||
$this->_currentRow++;
|
||
if (!$this->_fetch()) $this->EOF = true;
|
||
return $arr;
|
||
}
|
||
|
||
|
||
/**
|
||
* Fetch a row, returning PEAR_Error if no more rows.
|
||
* This is PEAR DB compat mode.
|
||
*
|
||
* @return DB_OK or error object
|
||
*/
|
||
function FetchInto(&$arr)
|
||
{
|
||
if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
|
||
$arr = $this->fields;
|
||
$this->MoveNext();
|
||
return 1; /* DB_OK */
|
||
}
|
||
|
||
|
||
/**
|
||
* Move to the first row in the recordset. Many databases do NOT support this.
|
||
*
|
||
* @return true or false
|
||
*/
|
||
function MoveFirst()
|
||
{
|
||
if ($this->_currentRow == 0) return true;
|
||
return $this->Move(0);
|
||
}
|
||
|
||
|
||
/**
|
||
* Move to the last row in the recordset.
|
||
*
|
||
* @return true or false
|
||
*/
|
||
function MoveLast()
|
||
{
|
||
if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
|
||
if ($this->EOF) return false;
|
||
while (!$this->EOF) {
|
||
$f = $this->fields;
|
||
$this->MoveNext();
|
||
}
|
||
$this->fields = $f;
|
||
$this->EOF = false;
|
||
return true;
|
||
}
|
||
|
||
|
||
/**
|
||
* Move to next record in the recordset.
|
||
*
|
||
* @return true if there still rows available, or false if there are no more rows (EOF).
|
||
*/
|
||
function MoveNext()
|
||
{
|
||
if (!$this->EOF) {
|
||
$this->_currentRow++;
|
||
if ($this->_fetch()) return true;
|
||
}
|
||
$this->EOF = true;
|
||
/* -- tested error handling when scrolling cursor -- seems useless.
|
||
$conn = $this->connection;
|
||
if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
|
||
$fn = $conn->raiseErrorFn;
|
||
$fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
|
||
}
|
||
*/
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Random access to a specific row in the recordset. Some databases do not support
|
||
* access to previous rows in the databases (no scrolling backwards).
|
||
*
|
||
* @param rowNumber is the row to move to (0-based)
|
||
*
|
||
* @return true if there still rows available, or false if there are no more rows (EOF).
|
||
*/
|
||
function Move($rowNumber = 0)
|
||
{
|
||
$this->EOF = false;
|
||
if ($rowNumber == $this->_currentRow) return true;
|
||
if ($rowNumber >= $this->_numOfRows)
|
||
if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2;
|
||
|
||
if ($this->canSeek) {
|
||
|
||
if ($this->_seek($rowNumber)) {
|
||
$this->_currentRow = $rowNumber;
|
||
if ($this->_fetch()) {
|
||
return true;
|
||
}
|
||
} else {
|
||
$this->EOF = true;
|
||
return false;
|
||
}
|
||
} else {
|
||
if ($rowNumber < $this->_currentRow) return false;
|
||
global $ADODB_EXTENSION;
|
||
if ($ADODB_EXTENSION) {
|
||
while (!$this->EOF && $this->_currentRow < $rowNumber) {
|
||
adodb_movenext($this);
|
||
}
|
||
} else {
|
||
|
||
while (! $this->EOF && $this->_currentRow < $rowNumber) {
|
||
$this->_currentRow++;
|
||
|
||
if (!$this->_fetch()) $this->EOF = true;
|
||
}
|
||
}
|
||
return !($this->EOF);
|
||
}
|
||
|
||
$this->fields = false;
|
||
$this->EOF = true;
|
||
return false;
|
||
}
|
||
|
||
|
||
/**
|
||
* Get the value of a field in the current row by column name.
|
||
* Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
|
||
*
|
||
* @param colname is the field to access
|
||
*
|
||
* @return the value of $colname column
|
||
*/
|
||
function Fields($colname)
|
||
{
|
||
return $this->fields[$colname];
|
||
}
|
||
|
||
function GetAssocKeys($upper=true)
|
||
{
|
||
$this->bind = array();
|
||
for ($i=0; $i < $this->_numOfFields; $i++) {
|
||
$o = $this->FetchField($i);
|
||
if ($upper === 2) $this->bind[$o->name] = $i;
|
||
else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Use associative array to get fields array for databases that do not support
|
||
* associative arrays. Submitted by Paolo S. Asioli paolo.asioli@libero.it
|
||
*
|
||
* If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
|
||
* before you execute your SQL statement, and access $rs->fields['col'] directly.
|
||
*
|
||
* $upper 0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField
|
||
*/
|
||
function GetRowAssoc($upper=1)
|
||
{
|
||
|
||
if (!$this->bind) {
|
||
$this->GetAssocKeys($upper);
|
||
}
|
||
|
||
$record = array();
|
||
foreach($this->bind as $k => $v) {
|
||
$record[$k] = $this->fields[$v];
|
||
}
|
||
|
||
return $record;
|
||
}
|
||
|
||
|
||
/**
|
||
* Clean up recordset
|
||
*
|
||
* @return true or false
|
||
*/
|
||
function Close()
|
||
{
|
||
/* free connection object - this seems to globally free the object */
|
||
/* and not merely the reference, so don't do this... */
|
||
/* $this->connection = false; */
|
||
if (!$this->_closed) {
|
||
$this->_closed = true;
|
||
return $this->_close();
|
||
} else
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* synonyms RecordCount and RowCount
|
||
*
|
||
* @return the number of rows or -1 if this is not supported
|
||
*/
|
||
function RecordCount() {return $this->_numOfRows;}
|
||
|
||
|
||
/*
|
||
* If we are using PageExecute(), this will return the maximum possible rows
|
||
* that can be returned when paging a recordset.
|
||
*/
|
||
function MaxRecordCount()
|
||
{
|
||
return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
|
||
}
|
||
|
||
/**
|
||
* synonyms RecordCount and RowCount
|
||
*
|
||
* @return the number of rows or -1 if this is not supported
|
||
*/
|
||
function RowCount() {return $this->_numOfRows;}
|
||
|
||
|
||
/**
|
||
* Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
|
||
*
|
||
* @return the number of records from a previous SELECT. All databases support this.
|
||
*
|
||
* But aware possible problems in multiuser environments. For better speed the table
|
||
* must be indexed by the condition. Heavy test this before deploying.
|
||
*/
|
||
function PO_RecordCount($table="", $condition="") {
|
||
|
||
$lnumrows = $this->_numOfRows;
|
||
/* the database doesn't support native recordcount, so we do a workaround */
|
||
if ($lnumrows == -1 && $this->connection) {
|
||
IF ($table) {
|
||
if ($condition) $condition = " WHERE " . $condition;
|
||
$resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
|
||
if ($resultrows) $lnumrows = reset($resultrows->fields);
|
||
}
|
||
}
|
||
return $lnumrows;
|
||
}
|
||
|
||
/**
|
||
* @return the current row in the recordset. If at EOF, will return the last row. 0-based.
|
||
*/
|
||
function CurrentRow() {return $this->_currentRow;}
|
||
|
||
/**
|
||
* synonym for CurrentRow -- for ADO compat
|
||
*
|
||
* @return the current row in the recordset. If at EOF, will return the last row. 0-based.
|
||
*/
|
||
function AbsolutePosition() {return $this->_currentRow;}
|
||
|
||
/**
|
||
* @return the number of columns in the recordset. Some databases will set this to 0
|
||
* if no records are returned, others will return the number of columns in the query.
|
||
*/
|
||
function FieldCount() {return $this->_numOfFields;}
|
||
|
||
|
||
/**
|
||
* Get the ADOFieldObject of a specific column.
|
||
*
|
||
* @param fieldoffset is the column position to access(0-based).
|
||
*
|
||
* @return the ADOFieldObject for that column, or false.
|
||
*/
|
||
function &FetchField($fieldoffset)
|
||
{
|
||
/* must be defined by child class */
|
||
}
|
||
|
||
/**
|
||
* Get the ADOFieldObjects of all columns in an array.
|
||
*
|
||
*/
|
||
function FieldTypesArray()
|
||
{
|
||
$arr = array();
|
||
for ($i=0, $max=$this->_numOfFields; $i < $max; $i++)
|
||
$arr[] = $this->FetchField($i);
|
||
return $arr;
|
||
}
|
||
|
||
/**
|
||
* Return the fields array of the current row as an object for convenience.
|
||
* The default case is lowercase field names.
|
||
*
|
||
* @return the object with the properties set to the fields of the current row
|
||
*/
|
||
function &FetchObj()
|
||
{
|
||
return FetchObject(false);
|
||
}
|
||
|
||
/**
|
||
* Return the fields array of the current row as an object for convenience.
|
||
* The default case is uppercase.
|
||
*
|
||
* @param $isupper to set the object property names to uppercase
|
||
*
|
||
* @return the object with the properties set to the fields of the current row
|
||
*/
|
||
function &FetchObject($isupper=true)
|
||
{
|
||
if (empty($this->_obj)) {
|
||
$this->_obj = new ADOFetchObj();
|
||
$this->_names = array();
|
||
for ($i=0; $i <$this->_numOfFields; $i++) {
|
||
$f = $this->FetchField($i);
|
||
$this->_names[] = $f->name;
|
||
}
|
||
}
|
||
$i = 0;
|
||
$o = &$this->_obj;
|
||
for ($i=0; $i <$this->_numOfFields; $i++) {
|
||
$name = $this->_names[$i];
|
||
if ($isupper) $n = strtoupper($name);
|
||
else $n = $name;
|
||
|
||
$o->$n = $this->Fields($name);
|
||
}
|
||
return $o;
|
||
}
|
||
|
||
/**
|
||
* Return the fields array of the current row as an object for convenience.
|
||
* The default is lower-case field names.
|
||
*
|
||
* @return the object with the properties set to the fields of the current row,
|
||
* or false if EOF
|
||
*
|
||
* Fixed bug reported by tim@orotech.net
|
||
*/
|
||
function &FetchNextObj()
|
||
{
|
||
return $this->FetchNextObject(false);
|
||
}
|
||
|
||
|
||
/**
|
||
* Return the fields array of the current row as an object for convenience.
|
||
* The default is upper case field names.
|
||
*
|
||
* @param $isupper to set the object property names to uppercase
|
||
*
|
||
* @return the object with the properties set to the fields of the current row,
|
||
* or false if EOF
|
||
*
|
||
* Fixed bug reported by tim@orotech.net
|
||
*/
|
||
function &FetchNextObject($isupper=true)
|
||
{
|
||
$o = false;
|
||
if ($this->_numOfRows != 0 && !$this->EOF) {
|
||
$o = $this->FetchObject($isupper);
|
||
$this->_currentRow++;
|
||
if ($this->_fetch()) return $o;
|
||
}
|
||
$this->EOF = true;
|
||
return $o;
|
||
}
|
||
|
||
/**
|
||
* Get the metatype of the column. This is used for formatting. This is because
|
||
* many databases use different names for the same type, so we transform the original
|
||
* type to our standardised version which uses 1 character codes:
|
||
*
|
||
* @param t is the type passed in. Normally is ADOFieldObject->type.
|
||
* @param len is the maximum length of that field. This is because we treat character
|
||
* fields bigger than a certain size as a 'B' (blob).
|
||
* @param fieldobj is the field object returned by the database driver. Can hold
|
||
* additional info (eg. primary_key for mysql).
|
||
*
|
||
* @return the general type of the data:
|
||
* C for character < 200 chars
|
||
* X for teXt (>= 200 chars)
|
||
* B for Binary
|
||
* N for numeric floating point
|
||
* D for date
|
||
* T for timestamp
|
||
* L for logical/Boolean
|
||
* I for integer
|
||
* R for autoincrement counter/integer
|
||
*
|
||
*
|
||
*/
|
||
function MetaType($t,$len=-1,$fieldobj=false)
|
||
{
|
||
if (is_object($t)) {
|
||
$fieldobj = $t;
|
||
$t = $fieldobj->type;
|
||
$len = $fieldobj->max_length;
|
||
}
|
||
/* changed in 2.32 to hashing instead of switch stmt for speed... */
|
||
static $typeMap = array(
|
||
'VARCHAR' => 'C',
|
||
'VARCHAR2' => 'C',
|
||
'CHAR' => 'C',
|
||
'C' => 'C',
|
||
'STRING' => 'C',
|
||
'NCHAR' => 'C',
|
||
'NVARCHAR' => 'C',
|
||
'VARYING' => 'C',
|
||
'BPCHAR' => 'C',
|
||
'CHARACTER' => 'C',
|
||
'INTERVAL' => 'C', # Postgres
|
||
##
|
||
'LONGCHAR' => 'X',
|
||
'TEXT' => 'X',
|
||
'NTEXT' => 'X',
|
||
'M' => 'X',
|
||
'X' => 'X',
|
||
'CLOB' => 'X',
|
||
'NCLOB' => 'X',
|
||
'LVARCHAR' => 'X',
|
||
##
|
||
'BLOB' => 'B',
|
||
'IMAGE' => 'B',
|
||
'BINARY' => 'B',
|
||
'VARBINARY' => 'B',
|
||
'LONGBINARY' => 'B',
|
||
'B' => 'B',
|
||
##
|
||
'YEAR' => 'D', /* mysql */
|
||
'DATE' => 'D',
|
||
'D' => 'D',
|
||
##
|
||
'TIME' => 'T',
|
||
'TIMESTAMP' => 'T',
|
||
'DATETIME' => 'T',
|
||
'TIMESTAMPTZ' => 'T',
|
||
'T' => 'T',
|
||
##
|
||
'BOOLEAN' => 'L',
|
||
'BIT' => 'L',
|
||
'L' => 'L',
|
||
##
|
||
'COUNTER' => 'R',
|
||
'R' => 'R',
|
||
'SERIAL' => 'R', /* ifx */
|
||
'INT IDENTITY' => 'R',
|
||
##
|
||
'INT' => 'I',
|
||
'INTEGER' => 'I',
|
||
'SHORT' => 'I',
|
||
'TINYINT' => 'I',
|
||
'SMALLINT' => 'I',
|
||
'I' => 'I',
|
||
##
|
||
'LONG' => 'N', /* interbase is numeric, oci8 is blob */
|
||
'BIGINT' => 'N', /* this is bigger than PHP 32-bit integers */
|
||
'DECIMAL' => 'N',
|
||
'DEC' => 'N',
|
||
'REAL' => 'N',
|
||
'DOUBLE' => 'N',
|
||
'DOUBLE PRECISION' => 'N',
|
||
'SMALLFLOAT' => 'N',
|
||
'FLOAT' => 'N',
|
||
'NUMBER' => 'N',
|
||
'NUM' => 'N',
|
||
'NUMERIC' => 'N',
|
||
'MONEY' => 'N',
|
||
|
||
## informix 9.2
|
||
'SQLINT' => 'I',
|
||
'SQLSERIAL' => 'I',
|
||
'SQLSMINT' => 'I',
|
||
'SQLSMFLOAT' => 'N',
|
||
'SQLFLOAT' => 'N',
|
||
'SQLMONEY' => 'N',
|
||
'SQLDECIMAL' => 'N',
|
||
'SQLDATE' => 'D',
|
||
'SQLVCHAR' => 'C',
|
||
'SQLCHAR' => 'C',
|
||
'SQLDTIME' => 'T',
|
||
'SQLINTERVAL' => 'N',
|
||
'SQLBYTES' => 'B',
|
||
'SQLTEXT' => 'X'
|
||
);
|
||
|
||
$tmap = false;
|
||
$t = strtoupper($t);
|
||
$tmap = @$typeMap[$t];
|
||
switch ($tmap) {
|
||
case 'C':
|
||
|
||
/* is the char field is too long, return as text field... */
|
||
if (!empty($this->blobSize)) {
|
||
if ($len > $this->blobSize) return 'X';
|
||
} else if ($len > 250) {
|
||
return 'X';
|
||
}
|
||
return 'C';
|
||
|
||
case 'I':
|
||
if (!empty($fieldobj->primary_key)) return 'R';
|
||
return 'I';
|
||
|
||
case false:
|
||
return 'N';
|
||
|
||
case 'B':
|
||
if (isset($fieldobj->binary))
|
||
return ($fieldobj->binary) ? 'B' : 'X';
|
||
return 'B';
|
||
|
||
case 'D':
|
||
if (!empty($this->dateHasTime)) return 'T';
|
||
return 'D';
|
||
|
||
default:
|
||
if ($t == 'LONG' && $this->dataProvider == 'oci8') return 'B';
|
||
return $tmap;
|
||
}
|
||
}
|
||
|
||
function _close() {}
|
||
|
||
/**
|
||
* set/returns the current recordset page when paginating
|
||
*/
|
||
function AbsolutePage($page=-1)
|
||
{
|
||
if ($page != -1) $this->_currentPage = $page;
|
||
return $this->_currentPage;
|
||
}
|
||
|
||
/**
|
||
* set/returns the status of the atFirstPage flag when paginating
|
||
*/
|
||
function AtFirstPage($status=false)
|
||
{
|
||
if ($status != false) $this->_atFirstPage = $status;
|
||
return $this->_atFirstPage;
|
||
}
|
||
|
||
function LastPageNo($page = false)
|
||
{
|
||
if ($page != false) $this->_lastPageNo = $page;
|
||
return $this->_lastPageNo;
|
||
}
|
||
|
||
/**
|
||
* set/returns the status of the atLastPage flag when paginating
|
||
*/
|
||
function AtLastPage($status=false)
|
||
{
|
||
if ($status != false) $this->_atLastPage = $status;
|
||
return $this->_atLastPage;
|
||
}
|
||
} /* end class ADORecordSet */
|
||
|
||
/* ============================================================================================== */
|
||
/* CLASS ADORecordSet_array */
|
||
/* ============================================================================================== */
|
||
|
||
/**
|
||
* This class encapsulates the concept of a recordset created in memory
|
||
* as an array. This is useful for the creation of cached recordsets.
|
||
*
|
||
* Note that the constructor is different from the standard ADORecordSet
|
||
*/
|
||
|
||
class ADORecordSet_array extends ADORecordSet
|
||
{
|
||
var $databaseType = 'array';
|
||
|
||
var $_array; /* holds the 2-dimensional data array */
|
||
var $_types; /* the array of types of each column (C B I L M) */
|
||
var $_colnames; /* names of each column in array */
|
||
var $_skiprow1; /* skip 1st row because it holds column names */
|
||
var $_fieldarr; /* holds array of field objects */
|
||
var $canSeek = true;
|
||
var $affectedrows = false;
|
||
var $insertid = false;
|
||
var $sql = '';
|
||
var $compat = false;
|
||
/**
|
||
* Constructor
|
||
*
|
||
*/
|
||
function ADORecordSet_array($fakeid=1)
|
||
{
|
||
global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
|
||
|
||
/* fetch() on EOF does not delete $this->fields */
|
||
$this->compat = !empty($ADODB_COMPAT_FETCH);
|
||
$this->ADORecordSet($fakeid); /* fake queryID */
|
||
$this->fetchMode = $ADODB_FETCH_MODE;
|
||
}
|
||
|
||
|
||
/**
|
||
* Setup the Array. Later we will have XML-Data and CSV handlers
|
||
*
|
||
* @param array is a 2-dimensional array holding the data.
|
||
* The first row should hold the column names
|
||
* unless paramter $colnames is used.
|
||
* @param typearr holds an array of types. These are the same types
|
||
* used in MetaTypes (C,B,L,I,N).
|
||
* @param [colnames] array of column names. If set, then the first row of
|
||
* $array should not hold the column names.
|
||
*/
|
||
function InitArray($array,$typearr,$colnames=false)
|
||
{
|
||
$this->_array = $array;
|
||
$this->_types = $typearr;
|
||
if ($colnames) {
|
||
$this->_skiprow1 = false;
|
||
$this->_colnames = $colnames;
|
||
} else $this->_colnames = $array[0];
|
||
|
||
$this->Init();
|
||
}
|
||
/**
|
||
* Setup the Array and datatype file objects
|
||
*
|
||
* @param array is a 2-dimensional array holding the data.
|
||
* The first row should hold the column names
|
||
* unless paramter $colnames is used.
|
||
* @param fieldarr holds an array of ADOFieldObject's.
|
||
*/
|
||
function InitArrayFields($array,$fieldarr)
|
||
{
|
||
$this->_array = $array;
|
||
$this->_skiprow1= false;
|
||
if ($fieldarr) {
|
||
$this->_fieldobjects = $fieldarr;
|
||
}
|
||
$this->Init();
|
||
}
|
||
|
||
function &GetArray($nRows=-1)
|
||
{
|
||
if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) {
|
||
return $this->_array;
|
||
} else {
|
||
return ADORecordSet::GetArray($nRows);
|
||
}
|
||
}
|
||
|
||
function _initrs()
|
||
{
|
||
$this->_numOfRows = sizeof($this->_array);
|
||
if ($this->_skiprow1) $this->_numOfRows -= 1;
|
||
|
||
$this->_numOfFields =(isset($this->_fieldobjects)) ?
|
||
sizeof($this->_fieldobjects):sizeof($this->_types);
|
||
}
|
||
|
||
/* Use associative array to get fields array */
|
||
function Fields($colname)
|
||
{
|
||
if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
|
||
|
||
if (!$this->bind) {
|
||
$this->bind = array();
|
||
for ($i=0; $i < $this->_numOfFields; $i++) {
|
||
$o = $this->FetchField($i);
|
||
$this->bind[strtoupper($o->name)] = $i;
|
||
}
|
||
}
|
||
return $this->fields[$this->bind[strtoupper($colname)]];
|
||
}
|
||
|
||
function &FetchField($fieldOffset = -1)
|
||
{
|
||
if (isset($this->_fieldobjects)) {
|
||
return $this->_fieldobjects[$fieldOffset];
|
||
}
|
||
$o = new ADOFieldObject();
|
||
$o->name = $this->_colnames[$fieldOffset];
|
||
$o->type = $this->_types[$fieldOffset];
|
||
$o->max_length = -1; /* length not known */
|
||
|
||
return $o;
|
||
}
|
||
|
||
function _seek($row)
|
||
{
|
||
if (sizeof($this->_array) && $row < $this->_numOfRows) {
|
||
$this->fields = $this->_array[$row];
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function MoveNext()
|
||
{
|
||
if (!$this->EOF) {
|
||
$this->_currentRow++;
|
||
|
||
$pos = $this->_currentRow;
|
||
if ($this->_skiprow1) $pos += 1;
|
||
|
||
if ($this->_numOfRows <= $pos) {
|
||
if (!$this->compat) $this->fields = false;
|
||
} else {
|
||
$this->fields = $this->_array[$pos];
|
||
return true;
|
||
}
|
||
$this->EOF = true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
function _fetch()
|
||
{
|
||
$pos = $this->_currentRow;
|
||
if ($this->_skiprow1) $pos += 1;
|
||
|
||
if ($this->_numOfRows <= $pos) {
|
||
if (!$this->compat) $this->fields = false;
|
||
return false;
|
||
}
|
||
|
||
$this->fields = $this->_array[$pos];
|
||
return true;
|
||
}
|
||
|
||
function _close()
|
||
{
|
||
return true;
|
||
}
|
||
|
||
} /* ADORecordSet_array */
|
||
|
||
/* ============================================================================================== */
|
||
/* HELPER FUNCTIONS */
|
||
/* ============================================================================================== */
|
||
|
||
/**
|
||
* Synonym for ADOLoadCode.
|
||
*
|
||
* @deprecated
|
||
*/
|
||
function ADOLoadDB($dbType)
|
||
{
|
||
return ADOLoadCode($dbType);
|
||
}
|
||
|
||
/**
|
||
* Load the code for a specific database driver
|
||
*/
|
||
function ADOLoadCode($dbType)
|
||
{
|
||
GLOBAL $ADODB_Database;
|
||
|
||
if (!$dbType) return false;
|
||
$ADODB_Database = strtolower($dbType);
|
||
switch ($ADODB_Database) {
|
||
case 'maxsql': $ADODB_Database = 'mysqlt'; break;
|
||
case 'postgres':
|
||
case 'pgsql': $ADODB_Database = 'postgres7'; break;
|
||
}
|
||
/* Karsten Kraus <Karsten.Kraus@web.de> */
|
||
return @include_once(ADODB_DIR."/drivers/adodb-".$ADODB_Database.".inc.php");
|
||
}
|
||
|
||
/**
|
||
* synonym for ADONewConnection for people like me who cannot remember the correct name
|
||
*/
|
||
function &NewADOConnection($db='')
|
||
{
|
||
return ADONewConnection($db);
|
||
}
|
||
|
||
/**
|
||
* Instantiate a new Connection class for a specific database driver.
|
||
*
|
||
* @param [db] is the database Connection object to create. If undefined,
|
||
* use the last database driver that was loaded by ADOLoadCode().
|
||
*
|
||
* @return the freshly created instance of the Connection class.
|
||
*/
|
||
function &ADONewConnection($db='')
|
||
{
|
||
GLOBAL $ADODB_Database;
|
||
|
||
$rez = true;
|
||
if ($db) {
|
||
if ($ADODB_Database != $db) ADOLoadCode($db);
|
||
} else {
|
||
if (!empty($ADODB_Database)) {
|
||
ADOLoadCode($ADODB_Database);
|
||
} else {
|
||
$rez = false;
|
||
}
|
||
}
|
||
|
||
$errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false;
|
||
if (!$rez) {
|
||
if ($errorfn) {
|
||
/* raise an error */
|
||
$errorfn('ADONewConnection', 'ADONewConnection', -998,
|
||
"could not load the database driver for '$db",
|
||
$dbtype);
|
||
} else
|
||
ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",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<br>\n");
|
||
$ok = false;
|
||
}
|
||
|
||
return $ok;
|
||
}
|
||
|
||
|
||
function adodb_backtrace($print=true)
|
||
{
|
||
$s = '';
|
||
if (PHPVERSION() >= 4.3) {
|
||
|
||
$MAXSTRLEN = 64;
|
||
|
||
$s = '<pre align=left>';
|
||
$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 .= '<font face="Courier New,Courier">';
|
||
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("</font><font color=#808080 size=-1> # line %4d, file: <a href=\"file:/%s\">%s</a></font>",
|
||
$arr['line'],$arr['file'],$arr['file']);
|
||
$s .= "\n";
|
||
}
|
||
$s .= '</pre>';
|
||
if ($print) print $s;
|
||
}
|
||
return $s;
|
||
}
|
||
|
||
} /* defined */
|
||
?>
|