mirror of
https://github.com/moodle/moodle.git
synced 2025-01-22 08:11:26 +01:00
a6d71a2199
Works OK for me in tests under MySQL on my server
627 lines
20 KiB
PHP
627 lines
20 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.40 7 April 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__));
|
|
|
|
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.40 7 April 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.
|
|
}
|
|
|
|
|
|
//==============================================================================================
|
|
// CLASS ADOConnection
|
|
//==============================================================================================
|
|
|
|
include_once(ADODB_DIR.'/adodb-connection.inc.php');
|
|
|
|
|
|
|
|
//==============================================================================================
|
|
// 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
|
|
//==============================================================================================
|
|
include_once(ADODB_DIR.'/adodb-recordset.inc.php');
|
|
|
|
//==============================================================================================
|
|
// 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;
|
|
if ($provider !== 'native' && $provider != 'odbc' && $provider != 'ado')
|
|
$drivername = $conn->dataProvider;
|
|
else {
|
|
$drivername = $conn->databaseType;
|
|
if (substr($drivername,0,5) == 'odbc_') $drivername = substr($drivername,5);
|
|
else if (substr($drivername,0,4) == 'ado_') $drivername = substr($drivername,4);
|
|
else if ($drivername == 'oracle') $drivername = 'oci8';
|
|
}
|
|
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->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) {
|
|
for ($i=0; $i < $tabs; $i++) $s .= ' ';
|
|
$tabs -= 1;
|
|
$s .= '<font face="Courier New,Courier">';
|
|
if (isset($arr['class'])) $s .= $arr['class'].'.';
|
|
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
|
|
?>
|