3 * @version V1.71 18 Jan 2001 (c) 2000, 2001 John Lim (jlim@natsoft.com.my). All rights reserved.
4 * Released under both BSD license and Lesser GPL library license.
5 * Whenever there is any discrepancy between the two licenses,
6 * the BSD license will take precedence.
8 * Set tabs to 4 for best viewing.
10 * Latest version is available at http://php.weblogs.com
12 * This is the main include file for ADODB.
13 * It has all the generic functionality of ADODB.
14 * Database specific drivers are stored in the adodb-*.inc.php files.
16 * Requires PHP4.01pl2 or later because it uses include_once
19 * Included with PhpWiki, which uses for now only the mysql-specific backend,
20 * instead of going through this main adodb.inc.php library.
21 * Initial port by Lawrence Akka. See lib/WikiDB/ADODB.php
24 if (!defined('_ADODB_LAYER')) {
25 define('_ADODB_LAYER',1);
26 rcs_id('$Id: adodb.inc.php,v 1.3 2004-04-06 20:00:10 rurban Exp $');
28 //==============================================================================
29 // CONSTANT DEFINITIONS
30 //==============================================================================
32 define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
34 define('ADODB_FETCH_DEFAULT',0);
35 define('ADODB_FETCH_NUM',1);
36 define('ADODB_FETCH_ASSOC',2);
37 define('ADODB_FETCH_BOTH',3);
39 // allow [ ] @ and . in table names
40 define('ADODB_TABLE_REGEX','([]0-9a-z_\.\@\[-]*)');
42 if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
45 * Set ADODB_DIR to the directory where this file resides...
46 * This constant was formerly called $ADODB_RootPath
48 if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
50 //==============================================================================
52 //==============================================================================
55 $ADODB_vers, // database version
56 $ADODB_Database, // last database driver used
57 $ADODB_COUNTRECS, // count number of records returned - slows down query
58 $ADODB_CACHE_DIR, // directory to cache recordsets
59 $ADODB_FETCH_MODE; // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
61 //==============================================================================
63 //==============================================================================
65 $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
67 if (!isset($ADODB_CACHE_DIR)) {
68 $ADODB_CACHE_DIR = '/tmp';
70 // do not accept url based paths, eg. http:/ or ftp:/
71 if (strpos($ADODB_CACHE_DIR,':/') !== false)
72 die("Illegal \$ADODB_CACHE_DIR");
75 //==============================================================================
76 // CHANGE NOTHING BELOW UNLESS YOU ARE CODING
77 //==============================================================================
80 // Initialize random number generator for randomizing cache flushes
81 srand(((double)microtime())*1000000);
84 * Name of last database driver loaded into memory. Set by ADOLoadCode().
89 * ADODB version as a string.
91 $ADODB_vers = 'V1.71 18 Jan 2001 (c) 2000, 2001 John Lim (jlim@natsoft.com.my). All rights reserved. Released BSD & LGPL.';
94 * Determines whether recordset->RecordCount() is used.
95 * Set to false for highest performance -- RecordCount() will always return -1 then
96 * for databases that provide "virtual" recordcounts...
98 $ADODB_COUNTRECS = true;
100 //==============================================================================
101 // CLASS ADOFieldObject
102 //==============================================================================
105 * Helper class for FetchFields -- holds info on a column
107 class ADOFieldObject {
112 // additional fields by dannym... (danny_milo@yahoo.com)
113 var $not_null = false;
114 // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
115 // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
117 var $has_default = false; // this one I have done only in mysql and postgres for now ...
118 // others to come (dannym)
119 var $default_value; // default, if any, and supported. Check has_default first.
123 //==============================================================================
124 // CLASS ADOConnection
125 //==============================================================================
128 * Connection object. For connecting to databases, and executing queries.
130 class ADOConnection {
134 var $dataProvider = 'native';
135 var $databaseType = ''; // RDBMS currently in use, eg. odbc, mysql, mssql
136 var $database = ''; // Name of database to be used.
137 var $host = ''; // The hostname of the database server
138 var $user = ''; // The username which is used to connect to the database server.
139 var $password = ''; // Password for the username
140 var $debug = false; // if set to true will output sql statements
141 var $maxblobsize = 8000; // maximum size of blobs or large text fields -- some databases die otherwise like foxpro
142 var $concat_operator = '+'; // default concat operator -- change to || for Oracle/Interbase
143 var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database
144 var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; // used by DBTimeStamp as the default timestamp fmt.
145 var $true = '1'; // string that represents TRUE for a database
146 var $false = '0'; // string that represents FALSE for a database
147 var $replaceQuote = "\\'"; // string to use to replace quotes
148 var $hasInsertID = false; // supports autoincrement ID?
149 var $hasAffectedRows = false; // supports affected rows for update/delete?
150 var $autoCommit = true;
151 var $charSet=false; // character set to use - only for interbase
152 var $metaTablesSQL = '';
153 var $hasTop = false; // support mssql/access SELECT TOP 10 * FROM TABLE
154 var $hasLimit = false; // support pgsql/mysql SELECT * FROM TABLE LIMIT 10
155 var $readOnly = false; // this is a readonly database - used by phpLens
156 var $hasMoveFirst = false; // has ability to run MoveFirst(), scrolling backwards
157 var $hasGenID = false; // can generate sequences using GenID();
158 var $genID = 0; // sequence id used by GenID();
159 var $raiseErrorFn = false; // error function to call
160 var $upperCase = false; // uppercase function to call for searching/where
161 var $isoDates = false; // accepts dates in ISO format
166 var $_connectionID = false; // The returned link identifier whenever a successful database connection is made. */
168 var $_errorMsg = ''; // A variable which was used to keep the returned last error message. The value will
169 //then returned by the errorMsg() function
171 var $_queryID = false; // This variable keeps the last created result link identifier. */
173 var $_isPersistentConnection = false; // A boolean variable to state whether its a persistent connection or normal connection. */
175 var $_bindInputArray = false; // set to true if ADOConnection.Execute() permits binding of array parameters.
180 function ADOConnection()
182 die('Virtual Class -- cannot instantiate');
187 * Connect to database
189 * @param [argHostname] Host to connect to
190 * @param [argUsername] Userid to login
191 * @param [argPassword] Associated password
192 * @param [argDatabaseName] database
194 * @return true or false
196 function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
197 if ($argHostname != "") $this->host = $argHostname;
198 if ($argUsername != "") $this->user = $argUsername;
199 if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons
200 if ($argDatabaseName != "") $this->database = $argDatabaseName;
202 $this->_isPersistentConnection = false;
203 if ($fn = $this->raiseErrorFn) {
204 if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
205 $err = $this->ErrorMsg();
206 if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
207 $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database);
209 if ($this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
211 if ($this->debug) print $this->host.': '.$this->ErrorMsg().'<br>';
218 * Establish persistent connect to database
220 * @param [argHostname] Host to connect to
221 * @param [argUsername] Userid to login
222 * @param [argPassword] Associated password
223 * @param [argDatabaseName] database
225 * @return return true or false
227 function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
229 if ($argHostname != "") $this->host = $argHostname;
230 if ($argUsername != "") $this->user = $argUsername;
231 if ($argPassword != "") $this->password = $argPassword;
232 if ($argDatabaseName != "") $this->database = $argDatabaseName;
234 $this->_isPersistentConnection = true;
236 if ($fn = $this->raiseErrorFn) {
237 if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
238 $err = $this->ErrorMsg();
239 if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
240 $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database);
242 if ($this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
244 if ($this->debug) print $this->host.': '.$this->ErrorMsg().'<br>';
251 * Should prepare the sql statement and return the stmt resource.
252 * For databases that do not support this, we return the $sql. To ensure
253 * compatibility with databases that do not support prepare:
255 * $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
256 * $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
257 * $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
259 * @param sql SQL to send to database
261 * @return return TRUE or FALSE, or the $sql.
264 function Prepare($sql)
271 * PEAR DB Compat - do not use internally.
275 return $this->qstr($s);
280 * PEAR DB Compat - do not use internally.
282 function ErrorNative()
284 return $this->ErrorNo();
289 * PEAR DB Compat - do not use internally.
291 function nextId($seq_name)
293 return $this->GenID($seq_name);
298 * PEAR DB Compat - do not use internally.
300 * Appears that the fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB
301 * are the same numeric values!
303 function SetFetchMode($mode)
305 global $ADODB_FETCH_MODE;
306 $ADODB_FETCH_MODE = $mode;
311 * PEAR DB Compat - do not use internally.
313 function &Query($sql, $inputarr=false)
315 $rs = &$this->Execute($sql, $inputarr);
316 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
322 * PEAR DB Compat - do not use internally
324 function &LimitQuery($sql, $offset, $count)
326 $rs = &$this->SelectLimit($sql, $count, $offset); // swap
327 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
333 * PEAR DB Compat - do not use internally
335 function Disconnect()
337 return $this->Close();
344 * @param sql SQL statement to execute
345 * @param [inputarr] holds the input data to bind to. Null elements will be set to null.
346 * @param [arg3] reserved for john lim for future use
347 * @return RecordSet or false
349 function &Execute($sql,$inputarr=false,$arg3=false)
351 if (!$this->_bindInputArray && $inputarr) {
352 $sqlarr = explode('?',$sql);
355 foreach($inputarr as $v) {
358 // from Ron Baldwin <ron.baldwin@sourceprose.com>
359 // Only quote string types
360 if (gettype($v) == 'string')
362 else if ($v === null)
370 if ($i+1 != sizeof($sqlarr))
371 print "Input Array does not match ?: ".htmlspecialchars($sql);
378 foreach ($inputarr as $kk => $vv) {
379 if (is_string($vv) && strlen($vv)>64) $vv = substr($vv,0,64).'...';
380 $ss .= "($kk=>'$vv') ";
384 print "<hr>($this->databaseType): ".htmlspecialchars($sql)." <code>$ss</code><hr>";
385 $this->_queryID = $this->_query($sql,$inputarr,$arg3);
387 if ($this->databaseType == 'mssql') { // ErrorNo is a slow function call in mssql, and not reliable
388 if($this->ErrorMsg()) {
389 $err = $this->ErrorNo();
390 if ($err) print $err.': '.$this->ErrorMsg().'<br>';
393 if (!$this->_queryID) print $this->ErrorNo().': '.$this->ErrorMsg().'<br>';
396 $this->_queryID =@$this->_query($sql,$inputarr,$arg3);
398 if ($this->_queryID === false) {
399 if ($fn = $this->raiseErrorFn) {
400 $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr);
403 } else if ($this->_queryID === true){
404 $rs = new ADORecordSet_empty();
407 $rsclass = "ADORecordSet_".$this->databaseType;
408 $rs = new $rsclass($this->_queryID); // &new not supported by older PHP versions
409 $rs->connection = &$this; // Pablo suggestion
411 //$this->_insertQuery(&$rs); PHP4 handles closing automatically
413 if (is_string($sql)) $rs->sql = $sql;
419 * Generates a sequence id and stores it in $this->genID;
420 * GenID is only available if $this->hasGenID = true;
422 * @seqname name of sequence to use
423 * @startID if sequence does not exist, start at this ID
424 * @return 0 if not supported, otherwise a sequence id
427 function GenID($seqname='adodbseq',$startID=1)
429 if (!$this->hasGenID) {
430 return 0; // formerly returns false pre 1.60
433 $getnext = sprintf($this->_genIDSQL,$seqname);
434 $rs = @$this->Execute($getnext);
436 $u = strtoupper($seqname);
437 $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
438 $rs = $this->Execute($getnext);
440 if ($rs && !$rs->EOF) $this->genID = (integer) reset($rs->fields);
441 else $this->genID = 0; // false
443 if ($rs) $rs->Close();
450 * @return the last inserted ID. Not all databases support this.
454 if ($this->hasInsertID) return $this->_insertid();
455 if ($this->debug) print '<p>Insert_ID error</p>';
461 * Portable Insert ID. Pablo Roca <pabloroca@mvps.org>
463 * @return the last inserted ID. All databases support this. But aware possible
464 * problems in multiuser environments. Heavy test this before deploying.
466 function PO_Insert_ID($table="", $id="")
468 if ($this->hasInsertID){
469 return $this->Insert_ID();
471 return $this->GetOne("SELECT MAX($id) FROM $table");
477 * @return # rows affected by UPDATE/DELETE
479 function Affected_Rows()
481 if ($this->hasAffectedRows) {
482 $val = $this->_affectedrows();
483 return ($val < 0) ? false : $val;
486 if ($this->debug) print '<p>Affected_Rows error</p>';
492 * @return the last error message
496 return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
501 * @return the last error number. Normally 0 means no error.
505 return ($this->_errorMsg) ? -1 : 0;
510 * @returns an array with the primary key columns in it.
512 function MetaPrimaryKeys($table)
519 * Choose a database to connect to. Many databases do not support this.
521 * @param dbName is the name of the database to select
522 * @return true or false
524 function SelectDB($dbName)
529 * Will select, getting rows from $offset (1-based), for $nrows.
530 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
531 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
532 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
534 * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
535 * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
537 * Uses SELECT TOP for Microsoft databases, and FIRST_ROWS CBO hint for Oracle 8+
538 * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
541 * @param [offset] is the row to start calculations from (1-based)
542 * @param [rows] is the number of rows to get
543 * @param [inputarr] array of bind variables
544 * @param [arg3] is a private parameter only used by jlim
545 * @param [secs2cache] is a private parameter only used by jlim
546 * @return the recordset ($rs->databaseType == 'array')
548 function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$arg3=false,$secs2cache=0)
550 if ($this->hasTop && $nrows > 0) {
551 // suggested by Reinhard Balling. Access requires top after distinct
555 '/(^select[\\t\\n ]*(distinctrow|distinct)?)/i','\\1 top '.$nrows.' ',$sql);
557 if ($secs2cache>0) return $this->CacheExecute($secs2cache, $sql,$inputarr,$arg3);
558 else return $this->Execute($sql,$inputarr,$arg3);
562 '/(^select[\\t\\n ]*(distinctrow|distinct)?)/i','\\1 top '.$nrows.' ',$sql);
567 if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr,$arg3);
568 else $rs = &$this->Execute($sql,$inputarr,$arg3);
569 if ($rs && !$rs->EOF) {
570 return $this->_rs2rs($rs,$nrows,$offset);
578 * Convert recordset to an array recordset
579 * input recordset's cursor should be at beginning, and
580 * old $rs will be closed.
582 * @param rs the recordset to copy
583 * @param [nrows] number of rows to retrieve (optional)
584 * @param [offset] offset by number of rows (optional)
585 * @return the new recordset
587 function &_rs2rs(&$rs,$nrows=-1,$offset=-1)
590 $arr = &$rs->GetArrayLimit($nrows,$offset);
592 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++)
593 $flds[] = &$rs->FetchField($i);
596 $rs2 = new ADORecordSet_array();
597 $rs2->connection = &$this;
598 $rs2->InitArrayFields($arr,$flds);
604 * Return first element of first row of sql statement. Recordset is disposed
607 * @param sql SQL statement
608 * @param [inputarr] input bind array
610 function GetOne($sql,$inputarr=false)
613 $rs = $this->Execute($sql,$inputarr);
615 if (!$rs->EOF) $ret = reset($rs->fields);
624 * Return all rows. Compat with PEAR DB
626 * @param sql SQL statement
627 * @param [inputarr] input bind array
629 function &GetAll($sql,$inputarr=false)
631 $rs = $this->Execute($sql,$inputarr);
633 if (defined('ADODB_PEAR')) return ADODB_PEAR_Error();
635 return $rs->GetArray();
640 * Return one row of sql statement. Recordset is disposed for you.
642 * @param sql SQL statement
643 * @param [inputarr] input bind array
645 function GetRow($sql,$inputarr=false)
647 $rs = $this->Execute($sql,$inputarr);
650 if (!$rs->EOF) $arr = $rs->fields;
660 * Will select, getting rows from $offset (1-based), for $nrows.
661 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
662 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
663 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
665 * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
666 * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
668 * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
670 * @param secs2cache seconds to cache data, set to 0 to force query
672 * @param [offset] is the row to start calculations from (1-based)
673 * @param [nrows] is the number of rows to get
674 * @param [inputarr] array of bind variables
675 * @param [arg3] is a private parameter only used by jlim
676 * @return the recordset ($rs->databaseType == 'array')
678 function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false, $arg3=false)
680 return $this->SelectLimit($sql,$nrows,$offset,$inputarr,$arg3,$secs2cache);
684 function CacheFlush($sql)
686 $f = $this->_gencachename($sql);
687 adodb_write_file($f,''); // is adodb_write_file needed?
692 function _gencachename($sql)
694 global $ADODB_CACHE_DIR;
696 return $ADODB_CACHE_DIR.'/adodb_'.md5($sql.$this->databaseType.$this->database.$this->user).'.cache';
701 * Execute SQL, caching recordsets.
703 * @param secs2cache seconds to cache data, set to 0 to force query
704 * @param sql SQL statement to execute
705 * @param [inputarr] holds the input data to bind to
706 * @param [arg3] reserved for john lim for future use
707 * @return RecordSet or false
709 function &CacheExecute($secs2cache,$sql,$inputarr=false,$arg3=false)
711 include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
712 // cannot cache if $inputarr set
713 if ($inputarr) return $this->Execute($sql, $inputarr, $arg3);
715 $md5file = $this->_gencachename($sql);
718 if ($secs2cache > 0)$rs = &csv2rs($md5file,$err,$secs2cache);
725 // no cached rs found
726 if ($this->debug) print " $md5file cache failure: $err<br>";
727 $rs = &$this->Execute($sql,$inputarr,$arg3);
730 $rs = &$this->_rs2rs($rs);
731 $txt = &rs2csv($rs,false,$sql);
733 if (!adodb_write_file($md5file,$txt,$this->debug) && $this->debug) print ' Cache write error<br>';
734 if ($rs->EOF && !$eof) {
735 $rs = &csv2rs($md5file,$err);
736 $rs->connection = &$this; // Pablo suggestion
742 // ok, set cached object found
743 $rs->connection = &$this; // Pablo suggestion
745 $ttl = $rs->timeCreated + $secs2cache - time();
746 print " $md5file success ttl=$ttl<br>";
754 * Generates an Update Query based on an existing recordset.
755 * $arrFields is an associative array of fields with the value
756 * that should be assigned.
758 * Note: This function should only be used on a recordset
759 * that is run against a single table.
761 * "Jonathan Younger" <jyounger@unilab.com>
763 function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false)
765 include_once(ADODB_DIR.'/adodb-lib.inc.php');
766 return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq);
771 * Generates an Insert Query based on an existing recordset.
772 * $arrFields is an associative array of fields with the value
773 * that should be assigned.
775 * Note: This function should only be used on a recordset
776 * that is run against a single table.
778 function GetInsertSQL(&$rs, $arrFields,$magicq=false)
780 include_once(ADODB_DIR.'/adodb-lib.inc.php');
781 return _adodb_getinsertsql($this,$rs,$arrFields,$magicq);
787 * UpdateBlob('TABLE', 'COLUMN', $var, 'ID=1', 'BLOB');
789 * $blobtype supports 'BLOB' and 'CLOB'
791 * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
792 * $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
794 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
796 $sql = "UPDATE $table SET $column=".$this->qstr($val)." where $where";
797 $rs = $this->Execute($sql);
800 if ($rez) $rs->Close();
807 * UpdateBlob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
809 * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
810 * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
812 function UpdateClob($table,$column,$val,$where)
814 return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
819 * not used - will probably remove in future
821 function BlankRecordSet($id=false)
823 $rsclass = "ADORecordSet_".$this->databaseType;
824 return new $rsclass($id);
829 * @meta contains the desired type, which could be...
830 * C for character. You will have to define the precision yourself.
831 * X for teXt. For unlimited character lengths.
833 * F for floating point, with no need to define scale and precision
834 * N for decimal numbers, you will have to define the (scale, precision) yourself
837 * L for logical/Boolean
839 * R for autoincrement counter/integer
840 * and if you want to use double-byte, add a 2 to the end, like C2 or X2.
843 * @return the actual type of the data or false if no such type available
845 function ActualType($meta)
867 * Maximum size of C field
871 return 255; // make it conservative if not defined
876 * Maximum size of X field
880 return 4000; // make it conservative if not defined
889 return $this->_close();
891 // "Simon Lee" <simon@mediaroad.com> reports that persistent connections need
893 //if ($this->_isPersistentConnection != true) return $this->_close();
899 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
901 * @return true if succeeded or false if database does not support transactions
903 function BeginTrans() {return false;}
907 * If database does not support transactions, always return true as data always commited
909 * @return true/false.
911 function CommitTrans()
916 * If database does not support transactions, rollbacks always fail, so return false
918 * @return true/false.
920 function RollbackTrans()
925 * return the databases that the driver can connect to.
926 * Some databases will return an empty array.
928 * @return an array of database names.
930 function &MetaDatabases()
934 * @return array of tables for current database.
936 function &MetaTables()
938 if ($this->metaTablesSQL) {
939 $rs = $this->Execute($this->metaTablesSQL);
940 if ($rs === false) return false;
941 $arr = $rs->GetArray();
943 for ($i=0; $i < sizeof($arr); $i++) {
944 $arr2[] = $arr[$i][0];
954 * List columns in a database as an array of ADOFieldObjects.
955 * See top of file for definition of object.
957 * @params table table name to query
959 * @return array of ADOFieldObjects for current table.
961 function &MetaColumns($table)
963 global $ADODB_FETCH_MODE;
965 if (!empty($this->metaColumnsSQL)) {
966 $save = $ADODB_FETCH_MODE;
967 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
968 $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
969 $ADODB_FETCH_MODE = $save;
970 if ($rs === false) return false;
973 while (!$rs->EOF) { //print_r($rs->fields);
974 $fld = new ADOFieldObject();
975 $fld->name = $rs->fields[0];
976 $fld->type = $rs->fields[1];
977 $fld->max_length = $rs->fields[2];
978 $retarr[strtoupper($fld->name)] = $fld;
990 * Different SQL databases used different methods to combine strings together.
991 * This function provides a wrapper.
993 * @param s variable number of string parameters
995 * Usage: $db->Concat($str1,$str2);
997 * @return concatenated string
1001 $arr = func_get_args();
1002 return implode($this->concat_operator, $arr);
1007 * Converts a date "d" to a string that the database can understand.
1009 * @param d a date in Unix date time format.
1011 * @return date string in database date format
1015 // note that we are limited to 1970 to 2038
1016 if (empty($d) && $d !== 0) return 'null';
1019 if ($this->isoDates) return "'$d'";
1020 else $d = ADORecordSet::UnixDate($d);
1022 return date($this->fmtDate,$d);
1027 * Converts a timestamp "ts" to a string that the database can understand.
1029 * @param ts a timestamp in Unix date time format.
1031 * @return timestamp string in database timestamp format
1033 function DBTimeStamp($ts)
1035 if (empty($ts) && $ts !== 0) return 'null';
1038 if ($this->isoDates) return "'$ts'";
1039 else $ts = ADORecordSet::UnixTimeStamp($ts);
1040 return date($this->fmtTimeStamp,$ts);
1045 * Converts a timestamp "ts" to a string that the database can understand.
1046 * An example is $db->qstr("Don't bother",magic_quotes_runtime());
1048 * @param s the string to quote
1049 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
1050 * This undoes the stupidity of magic quotes for GPC.
1052 * @return quoted string to be sent back to database
1054 function qstr($s,$magic_quotes=false)
1057 if (!$magic_quotes) {
1059 if ($this->replaceQuote[0] == '\\'){
1060 $s = str_replace('\\','\\\\',$s);
1062 return "'".str_replace("'",$this->replaceQuote,$s)."'";
1065 // undo magic quotes for "
1066 $s = str_replace('\\"','"',$s);
1068 if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything
1070 else {// change \' to '' for sybase/mssql
1071 $s = str_replace('\\\\','\\',$s);
1072 return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
1078 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
1079 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
1080 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
1082 * See readme.htm#ex8 for an example of usage.
1085 * @param nrows is the number of rows per page to get
1086 * @param page is the page number to get (1-based)
1087 * @param [inputarr] array of bind variables
1088 * @param [arg3] is a private parameter only used by jlim
1089 * @param [secs2cache] is a private parameter only used by jlim
1090 * @return the recordset ($rs->databaseType == 'array')
1092 * NOTE: phpLens uses a different algorithm and does not use PageExecute().
1095 function &PageExecute($sql, $nrows, $page, $inputarr=false, $arg3=false, $secs2cache=0)
1097 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1098 return _adodb_pageexecute($this, $sql, $nrows, $page, $inputarr, $arg3, $secs2cache);
1104 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
1105 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
1106 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
1108 * @param secs2cache seconds to cache data, set to 0 to force query
1110 * @param nrows is the number of rows per page to get
1111 * @param page is the page number to get (1-based)
1112 * @param [inputarr] array of bind variables
1113 * @param [arg3] is a private parameter only used by jlim
1114 * @return the recordset ($rs->databaseType == 'array')
1116 function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false, $arg3=false) {
1117 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1118 return _adodb_pageexecute($this, $sql, $nrows, $page, $inputarr, $arg3,$secs2cache);
1121 } // end class ADOConnection
1125 //==============================================================================
1126 // CLASS ADOFetchObj
1127 //==============================================================================
1130 * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
1135 //==============================================================================
1136 // CLASS ADORecordSet_empty
1137 //==============================================================================
1140 * Lightweight recordset when there are no records to be returned
1142 class ADORecordSet_empty
1144 var $dataProvider = 'empty';
1146 var $_numOfRows = 0;
1147 var $fields = false;
1148 var $connection = false;
1149 function RowCount() {return 0;}
1150 function RecordCount() {return 0;}
1151 function Close(){return true;}
1154 //==============================================================================
1155 // CLASS ADORecordSet
1156 //==============================================================================
1159 * RecordSet class that represents the dataset returned by the database.
1160 * To keep memory overhead low, this class holds only the current row in memory.
1161 * No prefetching of data is done, so the RecordCount() can return -1 ( which
1162 * means recordcount not known).
1164 class ADORecordSet {
1168 var $dataProvider = "native";
1169 var $fields = false; // holds the current row data
1170 var $blobSize = 64; // any varchar/char field this size or greater is treated as a blob
1171 // in other words, we use a text area for editting.
1172 var $canSeek = false; // indicates that seek is supported
1173 var $sql; // sql text
1174 var $EOF = false; /* Indicates that the current record position is after the last record in a Recordset object. */
1176 var $emptyTimeStamp = ' '; // what to display when $time==0
1177 var $emptyDate = ' '; // what to display when $time==0
1179 var $timeCreated=0; // datetime in Unix format rs created -- for cached recordsets
1181 var $bind = false; // used by Fields() to hold array - should be private?
1182 var $fetchMode; // default fetch mode
1183 var $connection = false; // the parent connection
1187 var $_numOfRows = -1;
1188 var $_numOfFields = -1;
1189 var $_queryID = -1; /* This variable keeps the result link identifier. */
1190 var $_currentRow = -1; /* This variable keeps the current row in the Recordset. */
1191 var $_closed = false; /* has recordset been closed */
1192 var $_inited = false; /* Init() should only be called once */
1193 var $_obj; /* Used by FetchObj */
1196 var $_currentPage = -1; /* Added by Iván Oliva to implement recordset pagination */
1197 var $_atFirstPage = false; /* Added by Iván Oliva to implement recordset pagination */
1198 var $_atLastPage = false; /* Added by Iván Oliva to implement recordset pagination */
1204 * @param queryID this is the queryID returned by ADOConnection->_query()
1207 function ADORecordSet(&$queryID)
1209 $this->_queryID = $queryID;
1215 if ($this->_inited) return;
1216 $this->_inited = true;
1218 if ($this->_queryID) @$this->_initrs();
1220 $this->_numOfRows = 0;
1221 $this->_numOfFields = 0;
1223 if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
1224 $this->_currentRow = 0;
1225 $this->EOF = ($this->_fetch() === false);
1232 * Generate a <SELECT> string from a recordset, and return the string.
1233 * If the recordset has 2 cols, we treat the 1st col as the containing
1234 * the text to display to the user, and 2nd col as the return value. Default
1235 * strings are compared with the FIRST column.
1237 * @param name name of <SELECT>
1238 * @param [defstr] the value to hilite. Use an array for multiple hilites for listbox.
1239 * @param [blank1stItem] true to leave the 1st item in list empty
1240 * @param [multiple] true for listbox, false for popup
1241 * @param [size] #rows to show for listbox. not used by popup
1242 * @param [selectAttr] additional attributes to defined for <SELECT>.
1243 * useful for holding javascript onChange='...' handlers.
1244 & @param [compareFields0] when we have 2 cols in recordset, we compare the defstr with
1245 * column 0 (1st col) if this is true. This is not documented.
1249 * changes by glen.davies@cce.ac.nz to support multiple hilited items
1252 function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
1253 $size=0, $selectAttr='',$compareFields0=true)
1255 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1256 return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
1257 $size, $selectAttr,$compareFields0);
1261 * Generate a <SELECT> string from a recordset, and return the string.
1262 * If the recordset has 2 cols, we treat the 1st col as the containing
1263 * the text to display to the user, and 2nd col as the return value. Default
1264 * strings are compared with the SECOND column.
1267 function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')
1269 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1270 return _adodb_getmenu($this,$name,$defstr,$blank1stItem,$multiple,
1271 $size, $selectAttr,false);
1276 * return recordset as a 2-dimensional array.
1278 * @param [nRows] is the number of rows to return. -1 means every row.
1280 * @return an array indexed by the rows (0-based) from the recordset
1282 function &GetArray($nRows = -1)
1286 while (!$this->EOF && $nRows != $cnt) {
1287 $results[$cnt++] = $this->fields;
1294 * return recordset as a 2-dimensional array.
1295 * Helper function for ADOConnection->SelectLimit()
1297 * @param offset is the row to start calculations from (1-based)
1298 * @param [nrows] is the number of rows to return
1300 * @return an array indexed by the rows (0-based) from the recordset
1302 function &GetArrayLimit($nrows,$offset=-1)
1304 if ($offset <= 0) return $this->GetArray($nrows);
1305 $this->Move($offset);
1309 while (!$this->EOF && $nrows != $cnt) {
1310 $results[$cnt++] = $this->fields;
1319 * Synonym for GetArray() for compatibility with ADO.
1321 * @param [nRows] is the number of rows to return. -1 means every row.
1323 * @return an array indexed by the rows (0-based) from the recordset
1325 function &GetRows($nRows = -1)
1327 return $this->GetArray($nRows);
1331 * return whole recordset as a 2-dimensional associative array if there are more than 2 columns.
1332 * The first column is treated as the key and is not included in the array.
1333 * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
1334 * $force_array == true.
1336 * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
1337 * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
1340 * @return an associative array indexed by the first column of the array,
1341 * or false if the data has less than 2 cols.
1343 function &GetAssoc($force_array = false) {
1344 $cols = $this->_numOfFields;
1348 $numIndex = isset($this->fields[0]);
1350 if ($cols > 2 || $force_array) {
1352 while (!$this->EOF) {
1353 $results[trim($this->fields[0])] = array_slice($this->fields, 1);
1357 while (!$this->EOF) {
1358 $results[trim(reset($this->fields))] = array_slice($this->fields, 1);
1363 // return scalar values
1365 while (!$this->EOF) {
1366 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
1367 $results[trim(($this->fields[0]))] = $this->fields[1];
1371 while (!$this->EOF) {
1372 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
1373 $v1 = trim(reset($this->fields));
1374 $v2 = ''.next($this->fields);
1375 $results[$v1] = $v2;
1386 * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format
1387 * @param fmt is the format to apply to it, using date()
1389 * @return a timestamp formated as user desires
1391 function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
1393 $tt = $this->UnixTimeStamp($v);
1394 // $tt == -1 if pre 1970
1395 if (($tt === false || $tt == -1) && $v != false) return $v;
1396 if ($tt == 0) return $this->emptyTimeStamp;
1398 return date($fmt,$tt);
1403 * @param v is the character date in YYYY-MM-DD format
1404 * @param fmt is the format to apply to it, using date()
1406 * @return a date formated as user desires
1408 function UserDate($v,$fmt='Y-m-d')
1410 $tt = $this->UnixDate($v);
1411 // $tt == -1 if pre 1970
1412 if (($tt === false || $tt == -1) && $v != false) return $v;
1413 else if ($tt == 0) return $this->emptyDate;
1414 else if ($tt == -1) { // pre-1970
1416 return date($fmt,$tt);
1422 * @param $v is a date string in YYYY-MM-DD format
1424 * @return date in unix timestamp format, or 0 if before 1970, or false if invalid date format
1426 function UnixDate($v)
1428 if (!preg_match( "|([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|",
1429 $v, $rr)) return false;
1431 if ($rr[1] <= 1970) return 0;
1433 return mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
1438 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
1440 * @return date in unix timestamp format, or 0 if before 1970, or false if invalid date format
1442 function UnixTimeStamp($v)
1445 "|([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9]{1,2}))?|",
1446 $v, $rr)) return false;
1447 if ($rr[1] <= 1970 && $rr[2]<= 1) return 0;
1450 return @mktime($rr[4],$rr[5],$rr[6],$rr[2],$rr[3],$rr[1]);
1455 * PEAR DB Compat - do not use internally
1459 return $this->Close();
1464 * PEAR DB compat, number of rows
1468 return $this->_numOfRows;
1473 * PEAR DB compat, number of cols
1477 return $this->_numOfCols;
1481 * Fetch a row, returning false if no more rows.
1482 * This is PEAR DB compat mode.
1484 * @return false or array containing the current record
1486 function &FetchRow()
1488 if ($this->EOF) return false;
1489 $arr = $this->fields;
1496 * Fetch a row, returning PEAR_Error if no more rows.
1497 * This is PEAR DB compat mode.
1499 * @return DB_OK or error object
1501 function FetchInto(&$arr)
1503 if ($this->EOF) return new PEAR_Error('EOF',-1);;
1504 $arr = $this->fields;
1511 * Move to the first row in the recordset. Many databases do NOT support this.
1513 * @return true or false
1515 function MoveFirst()
1517 if ($this->_currentRow == 0) return true;
1518 return $this->Move(0);
1523 * Move to the last row in the recordset.
1525 * @return true or false
1529 if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
1530 while (!$this->EOF) $this->MoveNext();
1536 * Move to next record in the recordset.
1538 * @return true if there still rows available, or false if there are no more rows (EOF).
1543 $this->_currentRow++;
1544 if ($this->_fetch()) return true;
1547 /* -- tested error handling when scrolling cursor -- seems useless.
1548 $conn = $this->connection;
1549 if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
1550 $fn = $conn->raiseErrorFn;
1551 $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
1558 * Random access to a specific row in the recordset. Some databases do not support
1559 * access to previous rows in the databases (no scrolling backwards).
1561 * @param rowNumber is the row to move to (0-based)
1563 * @return true if there still rows available, or false if there are no more rows (EOF).
1565 function Move($rowNumber = 0)
1567 if ($rowNumber == $this->_currentRow) return true;
1568 if ($rowNumber > $this->_numOfRows)
1569 if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-1;
1571 if ($this->canSeek) {
1572 if ($this->_seek($rowNumber)) {
1573 $this->_currentRow = $rowNumber;
1574 if ($this->_fetch()) {
1576 // $this->_currentRow += 1;
1582 if ($rowNumber < $this->_currentRow) return false;
1583 while (! $this->EOF && $this->_currentRow < $rowNumber) {
1584 $this->_currentRow++;
1585 if (!$this->_fetch()) $this->EOF = true;
1587 return !($this->EOF);
1590 $this->fields = null;
1597 * Get the value of a field in the current row by column name.
1598 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
1600 * @param colname is the field to access
1602 * @return the value of $colname column
1604 function Fields($colname)
1606 return $this->fields[$colname];
1611 * Use associative array to get fields array for databases that do not support
1612 * associative arrays. Submitted by Paolo S. Asioli paolo.asioli@libero.it
1614 * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
1615 * before you execute your SQL statement, and access $rs->fields['col'] directly.
1617 function &GetRowAssoc($upper=true)
1621 $this->bind = array();
1622 for ($i=0; $i < $this->_numOfFields; $i++) {
1623 $o = $this->FetchField($i);
1624 $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
1629 foreach($this->bind as $k => $v) {
1630 $record[$k] = $this->fields[$v];
1638 * Clean up recordset
1640 * @return true or false
1644 // free connection object - this seems to globally free the object
1645 // and not merely the reference, so don't do this...
1646 // $this->connection = false;
1647 if (!$this->_closed) {
1648 $this->_closed = true;
1649 return $this->_close();
1655 * synonyms RecordCount and RowCount
1657 * @return the number of rows or -1 if this is not supported
1659 function RecordCount() {return $this->_numOfRows;}
1663 * synonyms RecordCount and RowCount
1665 * @return the number of rows or -1 if this is not supported
1667 function RowCount() {return $this->_numOfRows;}
1671 * Portable RecordCount. Pablo Roca <pabloroca@mvps.org>
1673 * @return the number of records from a previous SELECT. All databases support this.
1675 * But aware possible problems in multiuser environments. For better speed the table
1676 * must be indexed by the condition. Heavy test this before deploying.
1678 function PO_RecordCount($table="", $condition="") {
1680 $lnumrows = $this->_numOfRows;
1681 // the database doesn't support native recordcount, so we do a workaround
1682 if ($lnumrows == -1 && $this->connection) {
1684 if ($condition) $condition = " WHERE " . $condition;
1685 $resultrows = &$this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
1686 if ($resultrows) $lnumrows = reset($resultrows->fields);
1693 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
1695 function CurrentRow() {return $this->_currentRow;}
1698 * synonym for CurrentRow -- for ADO compat
1700 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
1702 function AbsolutePosition() {return $this->_currentRow;}
1705 * @return the number of columns in the recordset. Some databases will set this to 0
1706 * if no records are returned, others will return the number of columns in the query.
1708 function FieldCount() {return $this->_numOfFields;}
1712 * Get the ADOFieldObject of a specific column.
1714 * @param fieldoffset is the column position to access(0-based).
1716 * @return the ADOFieldObject for that column, or false.
1718 function &FetchField($fieldoffset)
1720 // must be defined by child class
1724 * Return the fields array of the current row as an object for convenience.
1726 * @param $isupper to set the object property names to uppercase
1728 * @return the object with the properties set to the fields of the current row
1730 function &FetchObject($isupper=true)
1732 if (empty($this->_obj)) {
1733 $this->_obj = new ADOFetchObj();
1734 $this->_names = array();
1735 for ($i=0; $i <$this->_numOfFields; $i++) {
1736 $f = $this->FetchField($i);
1737 $this->_names[] = $f->name;
1742 for ($i=0; $i <$this->_numOfFields; $i++) {
1743 $name = $this->_names[$i];
1744 if ($isupper) $n = strtoupper($name);
1747 $o->$n = $this->Fields($name);
1752 * Return the fields array of the current row as an object for convenience.
1754 * @param $isupper to set the object property names to uppercase
1756 * @return the object with the properties set to the fields of the current row,
1759 * Fixed bug reported by tim@orotech.net
1761 function &FetchNextObject($isupper=true)
1764 if ($this->_numOfRows != 0 && !$this->EOF) {
1765 $o = $this->FetchObject($isupper);
1766 $this->_currentRow++;
1767 if ($this->_fetch()) return $o;
1774 * Get the metatype of the column. This is used for formatting. This is because
1775 * many databases use different names for the same type, so we transform the original
1776 * type to our standardised version which uses 1 character codes:
1778 * @param t is the type passed in. Normally is ADOFieldObject->type.
1779 * @param len is the maximum length of that field. This is because we treat character
1780 * fields bigger than a certain size as a 'B' (blob).
1781 * @param fieldobj is the field object returned by the database driver. Can hold
1782 * additional info (eg. primary_key for mysql).
1784 * @return the general type of the data:
1785 * C for character < 200 chars
1786 * X for teXt (>= 200 chars)
1788 * N for numeric floating point
1791 * L for logical/Boolean
1793 * R for autoincrement counter/integer
1797 function MetaType($t,$len=-1,$fieldobj=false)
1799 switch (strtoupper($t)) {
1809 if (!empty($this)) if ($len <= $this->blobSize) return 'C';
1810 else if ($len <= 250) return 'C';
1855 if (!empty($fieldobj->primary_key)) return 'R';
1858 default: return 'N';
1862 function _close() {}
1865 * set/returns the current recordset page when paginating
1867 function AbsolutePage($page=-1)
1869 if ($page != -1) $this->_currentPage = $page;
1870 return $this->_currentPage;
1874 * set/returns the status of the atFirstPage flag when paginating
1876 function AtFirstPage($status=false)
1878 if ($status != false) $this->_atFirstPage = $status;
1879 return $this->_atFirstPage;
1883 * set/returns the status of the atLastPage flag when paginating
1885 function AtLastPage($status=false)
1887 if ($status != false) $this->_atLastPage = $status;
1888 return $this->_atLastPage;
1890 } // end class ADORecordSet
1892 //==============================================================================
1893 // CLASS ADORecordSet_array
1894 //==============================================================================
1897 * This class encapsulates the concept of a recordset created in memory
1898 * as an array. This is useful for the creation of cached recordsets.
1900 * Note that the constructor is different from the standard ADORecordSet
1903 class ADORecordSet_array extends ADORecordSet
1905 var $databaseType = "array";
1907 var $_array; // holds the 2-dimensional data array
1908 var $_types; // the array of types of each column (C B I L M)
1909 var $_colnames; // names of each column in array
1910 var $_skiprow1; // skip 1st row because it holds column names
1911 var $_fieldarr; // holds array of field objects
1912 var $canSeek = true;
1913 var $affectedrows = false;
1914 var $insertid = false;
1920 function ADORecordSet_array($fakeid=1)
1922 $this->ADORecordSet($fakeid); // fake queryID
1927 * Setup the Array. Later we will have XML-Data and CSV handlers
1929 * @param array is a 2-dimensional array holding the data.
1930 * The first row should hold the column names
1931 * unless paramter $colnames is used.
1932 * @param typearr holds an array of types. These are the same types
1933 * used in MetaTypes (C,B,L,I,N).
1934 * @param [colnames] array of column names. If set, then the first row of
1935 * $array should not hold the column names.
1937 function InitArray(&$array,$typearr,$colnames=false)
1939 $this->_array = $array;
1940 $this->_types = &$typearr;
1942 $this->_skiprow1 = false;
1943 $this->_colnames = $colnames;
1944 } else $this->_colnames = $array[0];
1949 * Setup the Array and datatype file objects
1951 * @param array is a 2-dimensional array holding the data.
1952 * The first row should hold the column names
1953 * unless paramter $colnames is used.
1954 * @param fieldarr holds an array of ADOFieldObject's.
1956 function InitArrayFields(&$array,&$fieldarr)
1958 $this->_array = &$array;
1959 $this->_skiprow1= false;
1961 $this->_fieldobjects = &$fieldarr;
1969 $this->_numOfRows = sizeof($this->_array);
1970 if ($this->_skiprow1) $this->_numOfRows -= 1;
1972 $this->_numOfFields =(isset($this->_fieldobjects)) ?
1973 sizeof($this->_fieldobjects):sizeof($this->_types);
1976 /* Use associative array to get fields array */
1977 function Fields($colname)
1980 $this->bind = array();
1981 for ($i=0; $i < $this->_numOfFields; $i++) {
1982 $o = $this->FetchField($i);
1983 $this->bind[strtoupper($o->name)] = $i;
1987 return $this->fields[$this->bind[strtoupper($colname)]];
1990 function &FetchField($fieldOffset = -1)
1992 if (isset($this->_fieldobjects)) {
1993 return $this->_fieldobjects[$fieldOffset];
1995 $o = new ADOFieldObject();
1996 $o->name = $this->_colnames[$fieldOffset];
1997 $o->type = $this->_types[$fieldOffset];
1998 $o->max_length = -1; // length not known
2003 function _seek($row)
2010 $pos = $this->_currentRow;
2012 if ($this->_skiprow1) {
2013 if ($this->_numOfRows <= $pos-1) return false;
2016 if ($this->_numOfRows <= $pos) return false;
2019 $this->fields = $this->_array[$pos];
2028 } // ADORecordSet_array
2030 //==============================================================================
2032 //==============================================================================
2035 * Synonym for ADOLoadCode.
2039 function ADOLoadDB($dbType)
2041 return ADOLoadCode($dbType);
2045 * Load the code for a specific database driver
2047 function ADOLoadCode($dbType)
2049 GLOBAL $ADODB_Database;
2051 if (!$dbType) return false;
2052 $ADODB_Database = strtolower($dbType);
2053 switch ($ADODB_Database) {
2054 case 'maxsql': $ADODB_Database = 'mysqlt'; break;
2055 case 'pgsql': $ADODB_Database = 'postgres7'; break;
2057 include_once(ADODB_DIR."/adodb-$ADODB_Database.inc.php");
2062 * synonym for ADONewConnection for people who cannot remember the correct name
2064 function &NewADOConnection($db='')
2066 return ADONewConnection($db);
2070 * Instantiate a new Connection class for a specific database driver.
2072 * @param [db] is the database Connection object to create. If undefined,
2073 * use the last database driver that was loaded by ADOLoadCode().
2075 * @return the freshly created instance of the Connection class.
2077 function &ADONewConnection($db='')
2079 GLOBAL $ADODB_Database;
2082 if ($ADODB_Database != $db) ADOLoadCode($db);
2084 if (!empty($ADODB_Database)) ADOLoadCode($ADODB_Database);
2085 else print "<p>ADONewConnection: No database driver defined</p>";
2088 $cls = 'ADODB_'.$ADODB_Database;
2090 if (defined('ADODB_ERROR_HANDLER')) {
2091 $obj->raiseErrorFn = ADODB_ERROR_HANDLER;
2097 * Save a file $filename and its $contents (normally for caching) with file locking
2099 function adodb_write_file($filename, $contents,$debug=false)
2101 # http://www.php.net/bugs.php?id=9203 Bug that flock fails on Windows
2102 # So to simulate locking, we assume that rename is an atomic operation.
2103 # First we delete $filename, then we create a $tempfile write to it and
2104 # rename to the desired $filename. If the rename works, then we successfully
2105 # modified the file exclusively.
2106 # What a stupid need - having to simulate locking.
2108 # 1. $tempfile name is not unique -- very very low
2109 # 2. unlink($filename) fails -- ok, rename will fail
2110 # 3. adodb reads stale file because unlink fails -- ok, $rs timeout occurs
2111 # 4. another process creates $filename between unlink() and rename() -- ok, rename() fails and cache updated
2112 if (strpos(strtoupper(PHP_OS),'WIN') !== false) {
2113 // skip the decimal place
2114 $mtime = substr(str_replace(' ','_',microtime()),2);
2115 // unlink will let some latencies develop, so uniqid() is more random
2117 // getmypid() actually returns 0 on Win98 - never mind!
2118 $tmpname = $filename.uniqid($mtime).getmypid();
2119 if (!($fd = fopen($tmpname,'a'))) return false;
2120 $ok = ftruncate($fd,0);
2121 if (!fwrite($fd,$contents)) $ok = false;
2123 chmod($tmpname,0644);
2124 if (!@rename($tmpname,$filename)) {
2128 if ($debug && !$ok) print " Rename $tmpname ".($ok? 'ok' : 'failed')." <br>";
2131 if (!($fd = fopen($filename, 'a'))) return false;
2132 if (flock($fd, LOCK_EX) && ftruncate($fd, 0)) {
2133 $ok = fwrite( $fd, $contents );
2135 chmod($filename,0644);
2138 if ($debug)print " Failed acquiring lock for $filename<br>";
2151 // c-basic-offset: 4
2152 // c-hanging-comment-ender-p: nil
2153 // indent-tabs-mode: nil