ChangeLog
[Packages/TYPO3.CMS.git] / typo3 / sysext / adodb / adodb / adodb.inc.php
1 <?php
2 /*
3 * Set tabs to 4 for best viewing.
4 *
5 * Latest version is available at http://adodb.sourceforge.net
6 *
7 * This is the main include file for ADOdb.
8 * Database specific drivers are stored in the adodb/drivers/adodb-*.inc.php
9 *
10 * The ADOdb files are formatted so that doxygen can be used to generate documentation.
11 * Doxygen is a documentation generation tool and can be downloaded from http://doxygen.org/
12 */
13
14 /**
15 \mainpage
16
17 @version V4.90 8 June 2006 (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved.
18
19 Released under both BSD license and Lesser GPL library license. You can choose which license
20 you prefer.
21
22 PHP's database access functions are not standardised. This creates a need for a database
23 class library to hide the differences between the different database API's (encapsulate
24 the differences) so we can easily switch databases.
25
26 We currently support MySQL, Oracle, Microsoft SQL Server, Sybase, Sybase SQL Anywhere, DB2,
27 Informix, PostgreSQL, FrontBase, Interbase (Firebird and Borland variants), Foxpro, Access,
28 ADO, SAP DB, SQLite and ODBC. We have had successful reports of connecting to Progress and
29 other databases via ODBC.
30
31 Latest Download at http://adodb.sourceforge.net/
32
33 */
34
35 if (!defined('_ADODB_LAYER')) {
36 define('_ADODB_LAYER',1);
37
38 //==============================================================================================
39 // CONSTANT DEFINITIONS
40 //==============================================================================================
41
42
43 /**
44 * Set ADODB_DIR to the directory where this file resides...
45 * This constant was formerly called $ADODB_RootPath
46 */
47 if (!defined('ADODB_DIR')) define('ADODB_DIR',dirname(__FILE__));
48
49 //==============================================================================================
50 // GLOBAL VARIABLES
51 //==============================================================================================
52
53 GLOBAL
54 $ADODB_vers, // database version
55 $ADODB_COUNTRECS, // count number of records returned - slows down query
56 $ADODB_CACHE_DIR, // directory to cache recordsets
57 $ADODB_EXTENSION, // ADODB extension installed
58 $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
59 $ADODB_FETCH_MODE; // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
60
61 //==============================================================================================
62 // GLOBAL SETUP
63 //==============================================================================================
64
65 $ADODB_EXTENSION = defined('ADODB_EXTENSION');
66
67 //********************************************************//
68 /*
69 Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
70 Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
71
72 0 = ignore empty fields. All empty fields in array are ignored.
73 1 = force null. All empty, php null and string 'null' fields are changed to sql NULL values.
74 2 = force empty. All empty, php null and string 'null' fields are changed to sql empty '' or 0 values.
75 3 = force value. Value is left as it is. Php null and string 'null' are set to sql NULL values and empty fields '' are set to empty '' sql values.
76 */
77 define('ADODB_FORCE_IGNORE',0);
78 define('ADODB_FORCE_NULL',1);
79 define('ADODB_FORCE_EMPTY',2);
80 define('ADODB_FORCE_VALUE',3);
81 //********************************************************//
82
83
84 if (!$ADODB_EXTENSION || ADODB_EXTENSION < 4.0) {
85
86 define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
87
88 // allow [ ] @ ` " and . in table names
89 define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
90
91 // prefetching used by oracle
92 if (!defined('ADODB_PREFETCH_ROWS')) define('ADODB_PREFETCH_ROWS',10);
93
94
95 /*
96 Controls ADODB_FETCH_ASSOC field-name case. Default is 2, use native case-names.
97 This currently works only with mssql, odbc, oci8po and ibase derived drivers.
98
99 0 = assoc lowercase field names. $rs->fields['orderid']
100 1 = assoc uppercase field names. $rs->fields['ORDERID']
101 2 = use native-case field names. $rs->fields['OrderID']
102 */
103
104 define('ADODB_FETCH_DEFAULT',0);
105 define('ADODB_FETCH_NUM',1);
106 define('ADODB_FETCH_ASSOC',2);
107 define('ADODB_FETCH_BOTH',3);
108
109 if (!defined('TIMESTAMP_FIRST_YEAR')) define('TIMESTAMP_FIRST_YEAR',100);
110
111 // PHP's version scheme makes converting to numbers difficult - workaround
112 $_adodb_ver = (float) PHP_VERSION;
113 if ($_adodb_ver >= 5.0) {
114 define('ADODB_PHPVER',0x5000);
115 } else if ($_adodb_ver > 4.299999) { # 4.3
116 define('ADODB_PHPVER',0x4300);
117 } else if ($_adodb_ver > 4.199999) { # 4.2
118 define('ADODB_PHPVER',0x4200);
119 } else if (strnatcmp(PHP_VERSION,'4.0.5')>=0) {
120 define('ADODB_PHPVER',0x4050);
121 } else {
122 define('ADODB_PHPVER',0x4000);
123 }
124 }
125
126 //if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
127
128
129 /**
130 Accepts $src and $dest arrays, replacing string $data
131 */
132 function ADODB_str_replace($src, $dest, $data)
133 {
134 if (ADODB_PHPVER >= 0x4050) return str_replace($src,$dest,$data);
135
136 $s = reset($src);
137 $d = reset($dest);
138 while ($s !== false) {
139 $data = str_replace($s,$d,$data);
140 $s = next($src);
141 $d = next($dest);
142 }
143 return $data;
144 }
145
146 function ADODB_Setup()
147 {
148 GLOBAL
149 $ADODB_vers, // database version
150 $ADODB_COUNTRECS, // count number of records returned - slows down query
151 $ADODB_CACHE_DIR, // directory to cache recordsets
152 $ADODB_FETCH_MODE,
153 $ADODB_FORCE_TYPE;
154
155 $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
156 $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
157
158
159 if (!isset($ADODB_CACHE_DIR)) {
160 $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
161 } else {
162 // do not accept url based paths, eg. http:/ or ftp:/
163 if (strpos($ADODB_CACHE_DIR,'://') !== false)
164 die("Illegal path http:// or ftp://");
165 }
166
167
168 // Initialize random number generator for randomizing cache flushes
169 srand(((double)microtime())*1000000);
170
171 /**
172 * ADODB version as a string.
173 */
174 $ADODB_vers = 'V4.90 8 June 2006 (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved. Released BSD & LGPL.';
175
176 /**
177 * Determines whether recordset->RecordCount() is used.
178 * Set to false for highest performance -- RecordCount() will always return -1 then
179 * for databases that provide "virtual" recordcounts...
180 */
181 if (!isset($ADODB_COUNTRECS)) $ADODB_COUNTRECS = true;
182 }
183
184
185 //==============================================================================================
186 // CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
187 //==============================================================================================
188
189 ADODB_Setup();
190
191 //==============================================================================================
192 // CLASS ADOFieldObject
193 //==============================================================================================
194 /**
195 * Helper class for FetchFields -- holds info on a column
196 */
197 class ADOFieldObject {
198 var $name = '';
199 var $max_length=0;
200 var $type="";
201 /*
202 // additional fields by dannym... (danny_milo@yahoo.com)
203 var $not_null = false;
204 // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
205 // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
206
207 var $has_default = false; // this one I have done only in mysql and postgres for now ...
208 // others to come (dannym)
209 var $default_value; // default, if any, and supported. Check has_default first.
210 */
211 }
212
213
214
215 function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)
216 {
217 //print "Errorno ($fn errno=$errno m=$errmsg) ";
218 $thisConnection->_transOK = false;
219 if ($thisConnection->_oldRaiseFn) {
220 $fn = $thisConnection->_oldRaiseFn;
221 $fn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
222 }
223 }
224
225 //==============================================================================================
226 // CLASS ADOConnection
227 //==============================================================================================
228
229 /**
230 * Connection object. For connecting to databases, and executing queries.
231 */
232 class ADOConnection {
233 //
234 // PUBLIC VARS
235 //
236 var $dataProvider = 'native';
237 var $databaseType = ''; /// RDBMS currently in use, eg. odbc, mysql, mssql
238 var $database = ''; /// Name of database to be used.
239 var $host = ''; /// The hostname of the database server
240 var $user = ''; /// The username which is used to connect to the database server.
241 var $password = ''; /// Password for the username. For security, we no longer store it.
242 var $debug = false; /// if set to true will output sql statements
243 var $maxblobsize = 262144; /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
244 var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase
245 var $substr = 'substr'; /// substring operator
246 var $length = 'length'; /// string length ofperator
247 var $random = 'rand()'; /// random function
248 var $upperCase = 'upper'; /// uppercase function
249 var $fmtDate = "'Y-m-d'"; /// used by DBDate() as the default date format used by the database
250 var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
251 var $true = '1'; /// string that represents TRUE for a database
252 var $false = '0'; /// string that represents FALSE for a database
253 var $replaceQuote = "\\'"; /// string to use to replace quotes
254 var $nameQuote = '"'; /// string to use to quote identifiers and names
255 var $charSet=false; /// character set to use - only for interbase, postgres and oci8
256 var $metaDatabasesSQL = '';
257 var $metaTablesSQL = '';
258 var $uniqueOrderBy = false; /// All order by columns have to be unique
259 var $emptyDate = '&nbsp;';
260 var $emptyTimeStamp = '&nbsp;';
261 var $lastInsID = false;
262 //--
263 var $hasInsertID = false; /// supports autoincrement ID?
264 var $hasAffectedRows = false; /// supports affected rows for update/delete?
265 var $hasTop = false; /// support mssql/access SELECT TOP 10 * FROM TABLE
266 var $hasLimit = false; /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
267 var $readOnly = false; /// this is a readonly database - used by phpLens
268 var $hasMoveFirst = false; /// has ability to run MoveFirst(), scrolling backwards
269 var $hasGenID = false; /// can generate sequences using GenID();
270 var $hasTransactions = true; /// has transactions
271 //--
272 var $genID = 0; /// sequence id used by GenID();
273 var $raiseErrorFn = false; /// error function to call
274 var $isoDates = false; /// accepts dates in ISO format
275 var $cacheSecs = 3600; /// cache for 1 hour
276 var $sysDate = false; /// name of function that returns the current date
277 var $sysTimeStamp = false; /// name of function that returns the current timestamp
278 var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
279
280 var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
281 var $numCacheHits = 0;
282 var $numCacheMisses = 0;
283 var $pageExecuteCountRows = true;
284 var $uniqueSort = false; /// indicates that all fields in order by must be unique
285 var $leftOuter = false; /// operator to use for left outer join in WHERE clause
286 var $rightOuter = false; /// operator to use for right outer join in WHERE clause
287 var $ansiOuter = false; /// whether ansi outer join syntax supported
288 var $autoRollback = false; // autoRollback on PConnect().
289 var $poorAffectedRows = false; // affectedRows not working or unreliable
290
291 var $fnExecute = false;
292 var $fnCacheExecute = false;
293 var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
294 var $rsPrefix = "ADORecordSet_";
295
296 var $autoCommit = true; /// do not modify this yourself - actually private
297 var $transOff = 0; /// temporarily disable transactions
298 var $transCnt = 0; /// count of nested transactions
299
300 var $fetchMode=false;
301 //
302 // PRIVATE VARS
303 //
304 var $_oldRaiseFn = false;
305 var $_transOK = null;
306 var $_connectionID = false; /// The returned link identifier whenever a successful database connection is made.
307 var $_errorMsg = false; /// A variable which was used to keep the returned last error message. The value will
308 /// then returned by the errorMsg() function
309 var $_errorCode = false; /// Last error code, not guaranteed to be used - only by oci8
310 var $_queryID = false; /// This variable keeps the last created result link identifier
311
312 var $_isPersistentConnection = false; /// A boolean variable to state whether its a persistent connection or normal connection. */
313 var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
314 var $_evalAll = false;
315 var $_affected = false;
316 var $_logsql = false;
317 var $_transmode = ''; // transaction mode
318
319 /**
320 * Constructor
321 */
322 function ADOConnection()
323 {
324 die('Virtual Class -- cannot instantiate');
325 }
326
327 function Version()
328 {
329 global $ADODB_vers;
330
331 return (float) substr($ADODB_vers,1);
332 }
333
334 /**
335 Get server version info...
336
337 @returns An array with 2 elements: $arr['string'] is the description string,
338 and $arr[version] is the version (also a string).
339 */
340 function ServerInfo()
341 {
342 return array('description' => '', 'version' => '');
343 }
344
345 function IsConnected()
346 {
347 return !empty($this->_connectionID);
348 }
349
350 function _findvers($str)
351 {
352 if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) return $arr[1];
353 else return '';
354 }
355
356 /**
357 * All error messages go through this bottleneck function.
358 * You can define your own handler by defining the function name in ADODB_OUTP.
359 */
360 function outp($msg,$newline=true)
361 {
362 global $ADODB_FLUSH,$ADODB_OUTP;
363
364 if (defined('ADODB_OUTP')) {
365 $fn = ADODB_OUTP;
366 $fn($msg,$newline);
367 return;
368 } else if (isset($ADODB_OUTP)) {
369 $fn = $ADODB_OUTP;
370 $fn($msg,$newline);
371 return;
372 }
373
374 if ($newline) $msg .= "<br>\n";
375
376 if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) echo $msg;
377 else echo strip_tags($msg);
378
379
380 if (!empty($ADODB_FLUSH) && ob_get_length() !== false) flush(); // do not flush if output buffering enabled - useless - thx to Jesse Mullan
381
382 }
383
384 function Time()
385 {
386 $rs =& $this->_Execute("select $this->sysTimeStamp");
387 if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
388
389 return false;
390 }
391
392 /**
393 * Connect to database
394 *
395 * @param [argHostname] Host to connect to
396 * @param [argUsername] Userid to login
397 * @param [argPassword] Associated password
398 * @param [argDatabaseName] database
399 * @param [forceNew] force new connection
400 *
401 * @return true or false
402 */
403 function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false)
404 {
405 if ($argHostname != "") $this->host = $argHostname;
406 if ($argUsername != "") $this->user = $argUsername;
407 if ($argPassword != "") $this->password = $argPassword; // not stored for security reasons
408 if ($argDatabaseName != "") $this->database = $argDatabaseName;
409
410 $this->_isPersistentConnection = false;
411 if ($forceNew) {
412 if ($rez=$this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
413 } else {
414 if ($rez=$this->_connect($this->host, $this->user, $this->password, $this->database)) return true;
415 }
416 if (isset($rez)) {
417 $err = $this->ErrorMsg();
418 if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
419 $ret = false;
420 } else {
421 $err = "Missing extension for ".$this->dataProvider;
422 $ret = 0;
423 }
424 if ($fn = $this->raiseErrorFn)
425 $fn($this->databaseType,'CONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
426
427
428 $this->_connectionID = false;
429 if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
430 return $ret;
431 }
432
433 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
434 {
435 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
436 }
437
438
439 /**
440 * Always force a new connection to database - currently only works with oracle
441 *
442 * @param [argHostname] Host to connect to
443 * @param [argUsername] Userid to login
444 * @param [argPassword] Associated password
445 * @param [argDatabaseName] database
446 *
447 * @return true or false
448 */
449 function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
450 {
451 return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
452 }
453
454 /**
455 * Establish persistent connect to database
456 *
457 * @param [argHostname] Host to connect to
458 * @param [argUsername] Userid to login
459 * @param [argPassword] Associated password
460 * @param [argDatabaseName] database
461 *
462 * @return return true or false
463 */
464 function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
465 {
466 if (defined('ADODB_NEVER_PERSIST'))
467 return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
468
469 if ($argHostname != "") $this->host = $argHostname;
470 if ($argUsername != "") $this->user = $argUsername;
471 if ($argPassword != "") $this->password = $argPassword;
472 if ($argDatabaseName != "") $this->database = $argDatabaseName;
473
474 $this->_isPersistentConnection = true;
475 if ($rez = $this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
476 if (isset($rez)) {
477 $err = $this->ErrorMsg();
478 if (empty($err)) $err = "Connection error to server '$argHostname' with user '$argUsername'";
479 $ret = false;
480 } else {
481 $err = "Missing extension for ".$this->dataProvider;
482 $ret = 0;
483 }
484 if ($fn = $this->raiseErrorFn) {
485 $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
486 }
487
488 $this->_connectionID = false;
489 if ($this->debug) ADOConnection::outp( $this->host.': '.$err);
490 return $ret;
491 }
492
493 // Format date column in sql string given an input format that understands Y M D
494 function SQLDate($fmt, $col=false)
495 {
496 if (!$col) $col = $this->sysDate;
497 return $col; // child class implement
498 }
499
500 /**
501 * Should prepare the sql statement and return the stmt resource.
502 * For databases that do not support this, we return the $sql. To ensure
503 * compatibility with databases that do not support prepare:
504 *
505 * $stmt = $db->Prepare("insert into table (id, name) values (?,?)");
506 * $db->Execute($stmt,array(1,'Jill')) or die('insert failed');
507 * $db->Execute($stmt,array(2,'Joe')) or die('insert failed');
508 *
509 * @param sql SQL to send to database
510 *
511 * @return return FALSE, or the prepared statement, or the original sql if
512 * if the database does not support prepare.
513 *
514 */
515 function Prepare($sql)
516 {
517 return $sql;
518 }
519
520 /**
521 * Some databases, eg. mssql require a different function for preparing
522 * stored procedures. So we cannot use Prepare().
523 *
524 * Should prepare the stored procedure and return the stmt resource.
525 * For databases that do not support this, we return the $sql. To ensure
526 * compatibility with databases that do not support prepare:
527 *
528 * @param sql SQL to send to database
529 *
530 * @return return FALSE, or the prepared statement, or the original sql if
531 * if the database does not support prepare.
532 *
533 */
534 function PrepareSP($sql,$param=true)
535 {
536 return $this->Prepare($sql,$param);
537 }
538
539 /**
540 * PEAR DB Compat
541 */
542 function Quote($s)
543 {
544 return $this->qstr($s,false);
545 }
546
547 /**
548 Requested by "Karsten Dambekalns" <k.dambekalns@fishfarm.de>
549 */
550 function QMagic($s)
551 {
552 return $this->qstr($s,get_magic_quotes_gpc());
553 }
554
555 function q(&$s)
556 {
557 #if (!empty($this->qNull)) if ($s == 'null') return $s;
558 $s = $this->qstr($s,false);
559 }
560
561 /**
562 * PEAR DB Compat - do not use internally.
563 */
564 function ErrorNative()
565 {
566 return $this->ErrorNo();
567 }
568
569
570 /**
571 * PEAR DB Compat - do not use internally.
572 */
573 function nextId($seq_name)
574 {
575 return $this->GenID($seq_name);
576 }
577
578 /**
579 * Lock a row, will escalate and lock the table if row locking not supported
580 * will normally free the lock at the end of the transaction
581 *
582 * @param $table name of table to lock
583 * @param $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
584 */
585 function RowLock($table,$where)
586 {
587 return false;
588 }
589
590 function CommitLock($table)
591 {
592 return $this->CommitTrans();
593 }
594
595 function RollbackLock($table)
596 {
597 return $this->RollbackTrans();
598 }
599
600 /**
601 * PEAR DB Compat - do not use internally.
602 *
603 * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
604 * for easy porting :-)
605 *
606 * @param mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
607 * @returns The previous fetch mode
608 */
609 function SetFetchMode($mode)
610 {
611 $old = $this->fetchMode;
612 $this->fetchMode = $mode;
613
614 if ($old === false) {
615 global $ADODB_FETCH_MODE;
616 return $ADODB_FETCH_MODE;
617 }
618 return $old;
619 }
620
621
622 /**
623 * PEAR DB Compat - do not use internally.
624 */
625 function &Query($sql, $inputarr=false)
626 {
627 $rs = &$this->Execute($sql, $inputarr);
628 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
629 return $rs;
630 }
631
632
633 /**
634 * PEAR DB Compat - do not use internally
635 */
636 function &LimitQuery($sql, $offset, $count, $params=false)
637 {
638 $rs = &$this->SelectLimit($sql, $count, $offset, $params);
639 if (!$rs && defined('ADODB_PEAR')) return ADODB_PEAR_Error();
640 return $rs;
641 }
642
643
644 /**
645 * PEAR DB Compat - do not use internally
646 */
647 function Disconnect()
648 {
649 return $this->Close();
650 }
651
652 /*
653 Returns placeholder for parameter, eg.
654 $DB->Param('a')
655
656 will return ':a' for Oracle, and '?' for most other databases...
657
658 For databases that require positioned params, eg $1, $2, $3 for postgresql,
659 pass in Param(false) before setting the first parameter.
660 */
661 function Param($name,$type='C')
662 {
663 return '?';
664 }
665
666 /*
667 InParameter and OutParameter are self-documenting versions of Parameter().
668 */
669 function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
670 {
671 return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
672 }
673
674 /*
675 */
676 function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
677 {
678 return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
679
680 }
681
682 /*
683 Usage in oracle
684 $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
685 $db->Parameter($stmt,$id,'myid');
686 $db->Parameter($stmt,$group,'group',64);
687 $db->Execute();
688
689 @param $stmt Statement returned by Prepare() or PrepareSP().
690 @param $var PHP variable to bind to
691 @param $name Name of stored procedure variable name to bind to.
692 @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
693 @param [$maxLen] Holds an maximum length of the variable.
694 @param [$type] The data type of $var. Legal values depend on driver.
695
696 */
697 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
698 {
699 return false;
700 }
701
702 /**
703 Improved method of initiating a transaction. Used together with CompleteTrans().
704 Advantages include:
705
706 a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
707 Only the outermost block is treated as a transaction.<br>
708 b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
709 c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
710 are disabled, making it backward compatible.
711 */
712 function StartTrans($errfn = 'ADODB_TransMonitor')
713 {
714 if ($this->transOff > 0) {
715 $this->transOff += 1;
716 return;
717 }
718
719 $this->_oldRaiseFn = $this->raiseErrorFn;
720 $this->raiseErrorFn = $errfn;
721 $this->_transOK = true;
722
723 if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
724 $this->BeginTrans();
725 $this->transOff = 1;
726 }
727
728
729 /**
730 Used together with StartTrans() to end a transaction. Monitors connection
731 for sql errors, and will commit or rollback as appropriate.
732
733 @autoComplete if true, monitor sql errors and commit and rollback as appropriate,
734 and if set to false force rollback even if no SQL error detected.
735 @returns true on commit, false on rollback.
736 */
737 function CompleteTrans($autoComplete = true)
738 {
739 if ($this->transOff > 1) {
740 $this->transOff -= 1;
741 return true;
742 }
743 $this->raiseErrorFn = $this->_oldRaiseFn;
744
745 $this->transOff = 0;
746 if ($this->_transOK && $autoComplete) {
747 if (!$this->CommitTrans()) {
748 $this->_transOK = false;
749 if ($this->debug) ADOConnection::outp("Smart Commit failed");
750 } else
751 if ($this->debug) ADOConnection::outp("Smart Commit occurred");
752 } else {
753 $this->_transOK = false;
754 $this->RollbackTrans();
755 if ($this->debug) ADOCOnnection::outp("Smart Rollback occurred");
756 }
757
758 return $this->_transOK;
759 }
760
761 /*
762 At the end of a StartTrans/CompleteTrans block, perform a rollback.
763 */
764 function FailTrans()
765 {
766 if ($this->debug)
767 if ($this->transOff == 0) {
768 ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
769 } else {
770 ADOConnection::outp("FailTrans was called");
771 adodb_backtrace();
772 }
773 $this->_transOK = false;
774 }
775
776 /**
777 Check if transaction has failed, only for Smart Transactions.
778 */
779 function HasFailedTrans()
780 {
781 if ($this->transOff > 0) return $this->_transOK == false;
782 return false;
783 }
784
785 /**
786 * Execute SQL
787 *
788 * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
789 * @param [inputarr] holds the input data to bind to. Null elements will be set to null.
790 * @return RecordSet or false
791 */
792 function &Execute($sql,$inputarr=false)
793 {
794 if ($this->fnExecute) {
795 $fn = $this->fnExecute;
796 $ret =& $fn($this,$sql,$inputarr);
797 if (isset($ret)) return $ret;
798 }
799 if ($inputarr) {
800 if (!is_array($inputarr)) $inputarr = array($inputarr);
801
802 $element0 = reset($inputarr);
803 # is_object check because oci8 descriptors can be passed in
804 $array_2d = is_array($element0) && !is_object(reset($element0));
805 //remove extra memory copy of input -mikefedyk
806 unset($element0);
807
808 if (!is_array($sql) && !$this->_bindInputArray) {
809 $sqlarr = explode('?',$sql);
810
811 if (!$array_2d) $inputarr = array($inputarr);
812 foreach($inputarr as $arr) {
813 $sql = ''; $i = 0;
814 //Use each() instead of foreach to reduce memory usage -mikefedyk
815 while(list(, $v) = each($arr)) {
816 $sql .= $sqlarr[$i];
817 // from Ron Baldwin <ron.baldwin#sourceprose.com>
818 // Only quote string types
819 $typ = gettype($v);
820 if ($typ == 'string')
821 //New memory copy of input created here -mikefedyk
822 $sql .= $this->qstr($v);
823 else if ($typ == 'double')
824 $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
825 else if ($typ == 'boolean')
826 $sql .= $v ? $this->true : $this->false;
827 else if ($typ == 'object') {
828 if (method_exists($v, '__toString')) $sql .= $this->qstr($v->__toString());
829 else $sql .= $this->qstr((string) $v);
830 } else if ($v === null)
831 $sql .= 'NULL';
832 else
833 $sql .= $v;
834 $i += 1;
835 }
836 if (isset($sqlarr[$i])) {
837 $sql .= $sqlarr[$i];
838 if ($i+1 != sizeof($sqlarr)) ADOConnection::outp( "Input Array does not match ?: ".htmlspecialchars($sql));
839 } else if ($i != sizeof($sqlarr))
840 ADOConnection::outp( "Input array does not match ?: ".htmlspecialchars($sql));
841
842 $ret =& $this->_Execute($sql);
843 if (!$ret) return $ret;
844 }
845 } else {
846 if ($array_2d) {
847 if (is_string($sql))
848 $stmt = $this->Prepare($sql);
849 else
850 $stmt = $sql;
851
852 foreach($inputarr as $arr) {
853 $ret =& $this->_Execute($stmt,$arr);
854 if (!$ret) return $ret;
855 }
856 } else {
857 $ret =& $this->_Execute($sql,$inputarr);
858 }
859 }
860 } else {
861 $ret =& $this->_Execute($sql,false);
862 }
863
864 return $ret;
865 }
866
867
868 function &_Execute($sql,$inputarr=false)
869 {
870 if ($this->debug) {
871 global $ADODB_INCLUDED_LIB;
872 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
873 $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
874 } else {
875 $this->_queryID = @$this->_query($sql,$inputarr);
876 }
877
878 /************************
879 // OK, query executed
880 *************************/
881
882 if ($this->_queryID === false) { // error handling if query fails
883 if ($this->debug == 99) adodb_backtrace(true,5);
884 $fn = $this->raiseErrorFn;
885 if ($fn) {
886 $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
887 }
888 $false = false;
889 return $false;
890 }
891
892 if ($this->_queryID === true) { // return simplified recordset for inserts/updates/deletes with lower overhead
893 $rs =& new ADORecordSet_empty();
894 return $rs;
895 }
896
897 // return real recordset from select statement
898 $rsclass = $this->rsPrefix.$this->databaseType;
899 $rs = new $rsclass($this->_queryID,$this->fetchMode);
900 $rs->connection = &$this; // Pablo suggestion
901 $rs->Init();
902 if (is_array($sql)) $rs->sql = $sql[0];
903 else $rs->sql = $sql;
904 if ($rs->_numOfRows <= 0) {
905 global $ADODB_COUNTRECS;
906 if ($ADODB_COUNTRECS) {
907 if (!$rs->EOF) {
908 $rs = &$this->_rs2rs($rs,-1,-1,!is_array($sql));
909 $rs->_queryID = $this->_queryID;
910 } else
911 $rs->_numOfRows = 0;
912 }
913 }
914 return $rs;
915 }
916
917 function CreateSequence($seqname='adodbseq',$startID=1)
918 {
919 if (empty($this->_genSeqSQL)) return false;
920 return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
921 }
922
923 function DropSequence($seqname='adodbseq')
924 {
925 if (empty($this->_dropSeqSQL)) return false;
926 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
927 }
928
929 /**
930 * Generates a sequence id and stores it in $this->genID;
931 * GenID is only available if $this->hasGenID = true;
932 *
933 * @param seqname name of sequence to use
934 * @param startID if sequence does not exist, start at this ID
935 * @return 0 if not supported, otherwise a sequence id
936 */
937 function GenID($seqname='adodbseq',$startID=1)
938 {
939 if (!$this->hasGenID) {
940 return 0; // formerly returns false pre 1.60
941 }
942
943 $getnext = sprintf($this->_genIDSQL,$seqname);
944
945 $holdtransOK = $this->_transOK;
946
947 $save_handler = $this->raiseErrorFn;
948 $this->raiseErrorFn = '';
949 @($rs = $this->Execute($getnext));
950 $this->raiseErrorFn = $save_handler;
951
952 if (!$rs) {
953 $this->_transOK = $holdtransOK; //if the status was ok before reset
954 $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
955 $rs = $this->Execute($getnext);
956 }
957 if ($rs && !$rs->EOF) $this->genID = reset($rs->fields);
958 else $this->genID = 0; // false
959
960 if ($rs) $rs->Close();
961
962 return $this->genID;
963 }
964
965 /**
966 * @param $table string name of the table, not needed by all databases (eg. mysql), default ''
967 * @param $column string name of the column, not needed by all databases (eg. mysql), default ''
968 * @return the last inserted ID. Not all databases support this.
969 */
970 function Insert_ID($table='',$column='')
971 {
972 if ($this->_logsql && $this->lastInsID) return $this->lastInsID;
973 if ($this->hasInsertID) return $this->_insertid($table,$column);
974 if ($this->debug) {
975 ADOConnection::outp( '<p>Insert_ID error</p>');
976 adodb_backtrace();
977 }
978 return false;
979 }
980
981
982 /**
983 * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
984 *
985 * @return the last inserted ID. All databases support this. But aware possible
986 * problems in multiuser environments. Heavy test this before deploying.
987 */
988 function PO_Insert_ID($table="", $id="")
989 {
990 if ($this->hasInsertID){
991 return $this->Insert_ID($table,$id);
992 } else {
993 return $this->GetOne("SELECT MAX($id) FROM $table");
994 }
995 }
996
997 /**
998 * @return # rows affected by UPDATE/DELETE
999 */
1000 function Affected_Rows()
1001 {
1002 if ($this->hasAffectedRows) {
1003 if ($this->fnExecute === 'adodb_log_sql') {
1004 if ($this->_logsql && $this->_affected !== false) return $this->_affected;
1005 }
1006 $val = $this->_affectedrows();
1007 return ($val < 0) ? false : $val;
1008 }
1009
1010 if ($this->debug) ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1011 return false;
1012 }
1013
1014
1015 /**
1016 * @return the last error message
1017 */
1018 function ErrorMsg()
1019 {
1020 if ($this->_errorMsg) return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1021 else return '';
1022 }
1023
1024
1025 /**
1026 * @return the last error number. Normally 0 means no error.
1027 */
1028 function ErrorNo()
1029 {
1030 return ($this->_errorMsg) ? -1 : 0;
1031 }
1032
1033 function MetaError($err=false)
1034 {
1035 include_once(ADODB_DIR."/adodb-error.inc.php");
1036 if ($err === false) $err = $this->ErrorNo();
1037 return adodb_error($this->dataProvider,$this->databaseType,$err);
1038 }
1039
1040 function MetaErrorMsg($errno)
1041 {
1042 include_once(ADODB_DIR."/adodb-error.inc.php");
1043 return adodb_errormsg($errno);
1044 }
1045
1046 /**
1047 * @returns an array with the primary key columns in it.
1048 */
1049 function MetaPrimaryKeys($table, $owner=false)
1050 {
1051 // owner not used in base class - see oci8
1052 $p = array();
1053 $objs =& $this->MetaColumns($table);
1054 if ($objs) {
1055 foreach($objs as $v) {
1056 if (!empty($v->primary_key))
1057 $p[] = $v->name;
1058 }
1059 }
1060 if (sizeof($p)) return $p;
1061 if (function_exists('ADODB_VIEW_PRIMARYKEYS'))
1062 return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1063 return false;
1064 }
1065
1066 /**
1067 * @returns assoc array where keys are tables, and values are foreign keys
1068 */
1069 function MetaForeignKeys($table, $owner=false, $upper=false)
1070 {
1071 return false;
1072 }
1073 /**
1074 * Choose a database to connect to. Many databases do not support this.
1075 *
1076 * @param dbName is the name of the database to select
1077 * @return true or false
1078 */
1079 function SelectDB($dbName)
1080 {return false;}
1081
1082
1083 /**
1084 * Will select, getting rows from $offset (1-based), for $nrows.
1085 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1086 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1087 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1088 * eg.
1089 * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1090 * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1091 *
1092 * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1093 * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1094 *
1095 * @param sql
1096 * @param [offset] is the row to start calculations from (1-based)
1097 * @param [nrows] is the number of rows to get
1098 * @param [inputarr] array of bind variables
1099 * @param [secs2cache] is a private parameter only used by jlim
1100 * @return the recordset ($rs->databaseType == 'array')
1101 */
1102 function &SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
1103 {
1104 if ($this->hasTop && $nrows > 0) {
1105 // suggested by Reinhard Balling. Access requires top after distinct
1106 // Informix requires first before distinct - F Riosa
1107 $ismssql = (strpos($this->databaseType,'mssql') !== false);
1108 if ($ismssql) $isaccess = false;
1109 else $isaccess = (strpos($this->databaseType,'access') !== false);
1110
1111 if ($offset <= 0) {
1112
1113 // access includes ties in result
1114 if ($isaccess) {
1115 $sql = preg_replace(
1116 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1117
1118 if ($secs2cache>0) {
1119 $ret =& $this->CacheExecute($secs2cache, $sql,$inputarr);
1120 } else {
1121 $ret =& $this->Execute($sql,$inputarr);
1122 }
1123 return $ret; // PHP5 fix
1124 } else if ($ismssql){
1125 $sql = preg_replace(
1126 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1127 } else {
1128 $sql = preg_replace(
1129 '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.((integer)$nrows).' ',$sql);
1130 }
1131 } else {
1132 $nn = $nrows + $offset;
1133 if ($isaccess || $ismssql) {
1134 $sql = preg_replace(
1135 '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1136 } else {
1137 $sql = preg_replace(
1138 '/(^\s*select\s)/i','\\1 '.$this->hasTop.' '.$nn.' ',$sql);
1139 }
1140 }
1141 }
1142
1143 // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer rows
1144 // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1145 global $ADODB_COUNTRECS;
1146
1147 $savec = $ADODB_COUNTRECS;
1148 $ADODB_COUNTRECS = false;
1149
1150 if ($offset>0){
1151 if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1152 else $rs = &$this->Execute($sql,$inputarr);
1153 } else {
1154 if ($secs2cache>0) $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1155 else $rs = &$this->Execute($sql,$inputarr);
1156 }
1157 $ADODB_COUNTRECS = $savec;
1158 if ($rs && !$rs->EOF) {
1159 $rs =& $this->_rs2rs($rs,$nrows,$offset);
1160 }
1161 //print_r($rs);
1162 return $rs;
1163 }
1164
1165 /**
1166 * Create serializable recordset. Breaks rs link to connection.
1167 *
1168 * @param rs the recordset to serialize
1169 */
1170 function &SerializableRS(&$rs)
1171 {
1172 $rs2 =& $this->_rs2rs($rs);
1173 $ignore = false;
1174 $rs2->connection =& $ignore;
1175
1176 return $rs2;
1177 }
1178
1179 /**
1180 * Convert database recordset to an array recordset
1181 * input recordset's cursor should be at beginning, and
1182 * old $rs will be closed.
1183 *
1184 * @param rs the recordset to copy
1185 * @param [nrows] number of rows to retrieve (optional)
1186 * @param [offset] offset by number of rows (optional)
1187 * @return the new recordset
1188 */
1189 function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true)
1190 {
1191 if (! $rs) {
1192 $false = false;
1193 return $false;
1194 }
1195 $dbtype = $rs->databaseType;
1196 if (!$dbtype) {
1197 $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1198 return $rs;
1199 }
1200 if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1201 $rs->MoveFirst();
1202 $rs = &$rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1203 return $rs;
1204 }
1205 $flds = array();
1206 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1207 $flds[] = $rs->FetchField($i);
1208 }
1209
1210 $arr =& $rs->GetArrayLimit($nrows,$offset);
1211 //print_r($arr);
1212 if ($close) $rs->Close();
1213
1214 $arrayClass = $this->arrayClass;
1215
1216 $rs2 = new $arrayClass();
1217 $rs2->connection = &$this;
1218 $rs2->sql = $rs->sql;
1219 $rs2->dataProvider = $this->dataProvider;
1220 $rs2->InitArrayFields($arr,$flds);
1221 $rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
1222 return $rs2;
1223 }
1224
1225 /*
1226 * Return all rows. Compat with PEAR DB
1227 */
1228 function &GetAll($sql, $inputarr=false)
1229 {
1230 $arr =& $this->GetArray($sql,$inputarr);
1231 return $arr;
1232 }
1233
1234 function &GetAssoc($sql, $inputarr=false,$force_array = false, $first2cols = false)
1235 {
1236 $rs =& $this->Execute($sql, $inputarr);
1237 if (!$rs) {
1238 $false = false;
1239 return $false;
1240 }
1241 $arr =& $rs->GetAssoc($force_array,$first2cols);
1242 return $arr;
1243 }
1244
1245 function &CacheGetAssoc($secs2cache, $sql=false, $inputarr=false,$force_array = false, $first2cols = false)
1246 {
1247 if (!is_numeric($secs2cache)) {
1248 $first2cols = $force_array;
1249 $force_array = $inputarr;
1250 }
1251 $rs =& $this->CacheExecute($secs2cache, $sql, $inputarr);
1252 if (!$rs) {
1253 $false = false;
1254 return $false;
1255 }
1256 $arr =& $rs->GetAssoc($force_array,$first2cols);
1257 return $arr;
1258 }
1259
1260 /**
1261 * Return first element of first row of sql statement. Recordset is disposed
1262 * for you.
1263 *
1264 * @param sql SQL statement
1265 * @param [inputarr] input bind array
1266 */
1267 function GetOne($sql,$inputarr=false)
1268 {
1269 global $ADODB_COUNTRECS;
1270 $crecs = $ADODB_COUNTRECS;
1271 $ADODB_COUNTRECS = false;
1272
1273 $ret = false;
1274 $rs = &$this->Execute($sql,$inputarr);
1275 if ($rs) {
1276 if (!$rs->EOF) $ret = reset($rs->fields);
1277 $rs->Close();
1278 }
1279 $ADODB_COUNTRECS = $crecs;
1280 return $ret;
1281 }
1282
1283 function CacheGetOne($secs2cache,$sql=false,$inputarr=false)
1284 {
1285 $ret = false;
1286 $rs = &$this->CacheExecute($secs2cache,$sql,$inputarr);
1287 if ($rs) {
1288 if (!$rs->EOF) $ret = reset($rs->fields);
1289 $rs->Close();
1290 }
1291
1292 return $ret;
1293 }
1294
1295 function GetCol($sql, $inputarr = false, $trim = false)
1296 {
1297 $rv = false;
1298 $rs = &$this->Execute($sql, $inputarr);
1299 if ($rs) {
1300 $rv = array();
1301 if ($trim) {
1302 while (!$rs->EOF) {
1303 $rv[] = trim(reset($rs->fields));
1304 $rs->MoveNext();
1305 }
1306 } else {
1307 while (!$rs->EOF) {
1308 $rv[] = reset($rs->fields);
1309 $rs->MoveNext();
1310 }
1311 }
1312 $rs->Close();
1313 }
1314 return $rv;
1315 }
1316
1317 function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false)
1318 {
1319 $rv = false;
1320 $rs = &$this->CacheExecute($secs, $sql, $inputarr);
1321 if ($rs) {
1322 if ($trim) {
1323 while (!$rs->EOF) {
1324 $rv[] = trim(reset($rs->fields));
1325 $rs->MoveNext();
1326 }
1327 } else {
1328 while (!$rs->EOF) {
1329 $rv[] = reset($rs->fields);
1330 $rs->MoveNext();
1331 }
1332 }
1333 $rs->Close();
1334 }
1335 return $rv;
1336 }
1337
1338 /*
1339 Calculate the offset of a date for a particular database and generate
1340 appropriate SQL. Useful for calculating future/past dates and storing
1341 in a database.
1342
1343 If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
1344 */
1345 function OffsetDate($dayFraction,$date=false)
1346 {
1347 if (!$date) $date = $this->sysDate;
1348 return '('.$date.'+'.$dayFraction.')';
1349 }
1350
1351
1352 /**
1353 *
1354 * @param sql SQL statement
1355 * @param [inputarr] input bind array
1356 */
1357 function &GetArray($sql,$inputarr=false)
1358 {
1359 global $ADODB_COUNTRECS;
1360
1361 $savec = $ADODB_COUNTRECS;
1362 $ADODB_COUNTRECS = false;
1363 $rs =& $this->Execute($sql,$inputarr);
1364 $ADODB_COUNTRECS = $savec;
1365 if (!$rs)
1366 if (defined('ADODB_PEAR')) {
1367 $cls = ADODB_PEAR_Error();
1368 return $cls;
1369 } else {
1370 $false = false;
1371 return $false;
1372 }
1373 $arr =& $rs->GetArray();
1374 $rs->Close();
1375 return $arr;
1376 }
1377
1378 function &CacheGetAll($secs2cache,$sql=false,$inputarr=false)
1379 {
1380 return $this->CacheGetArray($secs2cache,$sql,$inputarr);
1381 }
1382
1383 function &CacheGetArray($secs2cache,$sql=false,$inputarr=false)
1384 {
1385 global $ADODB_COUNTRECS;
1386
1387 $savec = $ADODB_COUNTRECS;
1388 $ADODB_COUNTRECS = false;
1389 $rs =& $this->CacheExecute($secs2cache,$sql,$inputarr);
1390 $ADODB_COUNTRECS = $savec;
1391
1392 if (!$rs)
1393 if (defined('ADODB_PEAR')) {
1394 $cls = ADODB_PEAR_Error();
1395 return $cls;
1396 } else {
1397 $false = false;
1398 return $false;
1399 }
1400 $arr =& $rs->GetArray();
1401 $rs->Close();
1402 return $arr;
1403 }
1404
1405
1406
1407 /**
1408 * Return one row of sql statement. Recordset is disposed for you.
1409 *
1410 * @param sql SQL statement
1411 * @param [inputarr] input bind array
1412 */
1413 function &GetRow($sql,$inputarr=false)
1414 {
1415 global $ADODB_COUNTRECS;
1416 $crecs = $ADODB_COUNTRECS;
1417 $ADODB_COUNTRECS = false;
1418
1419 $rs =& $this->Execute($sql,$inputarr);
1420
1421 $ADODB_COUNTRECS = $crecs;
1422 if ($rs) {
1423 if (!$rs->EOF) $arr = $rs->fields;
1424 else $arr = array();
1425 $rs->Close();
1426 return $arr;
1427 }
1428
1429 $false = false;
1430 return $false;
1431 }
1432
1433 function &CacheGetRow($secs2cache,$sql=false,$inputarr=false)
1434 {
1435 $rs =& $this->CacheExecute($secs2cache,$sql,$inputarr);
1436 if ($rs) {
1437 $arr = false;
1438 if (!$rs->EOF) $arr = $rs->fields;
1439 $rs->Close();
1440 return $arr;
1441 }
1442 $false = false;
1443 return $false;
1444 }
1445
1446 /**
1447 * Insert or replace a single record. Note: this is not the same as MySQL's replace.
1448 * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
1449 * Also note that no table locking is done currently, so it is possible that the
1450 * record be inserted twice by two programs...
1451 *
1452 * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
1453 *
1454 * $table table name
1455 * $fieldArray associative array of data (you must quote strings yourself).
1456 * $keyCol the primary key field name or if compound key, array of field names
1457 * autoQuote set to true to use a hueristic to quote strings. Works with nulls and numbers
1458 * but does not work with dates nor SQL functions.
1459 * has_autoinc the primary key is an auto-inc field, so skip in insert.
1460 *
1461 * Currently blob replace not supported
1462 *
1463 * returns 0 = fail, 1 = update, 2 = insert
1464 */
1465
1466 function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false)
1467 {
1468 global $ADODB_INCLUDED_LIB;
1469 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
1470
1471 return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
1472 }
1473
1474
1475 /**
1476 * Will select, getting rows from $offset (1-based), for $nrows.
1477 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1478 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1479 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1480 * eg.
1481 * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
1482 * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
1483 *
1484 * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
1485 *
1486 * @param [secs2cache] seconds to cache data, set to 0 to force query. This is optional
1487 * @param sql
1488 * @param [offset] is the row to start calculations from (1-based)
1489 * @param [nrows] is the number of rows to get
1490 * @param [inputarr] array of bind variables
1491 * @return the recordset ($rs->databaseType == 'array')
1492 */
1493 function &CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false)
1494 {
1495 if (!is_numeric($secs2cache)) {
1496 if ($sql === false) $sql = -1;
1497 if ($offset == -1) $offset = false;
1498 // sql, nrows, offset,inputarr
1499 $rs =& $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
1500 } else {
1501 if ($sql === false) ADOConnection::outp( "Warning: \$sql missing from CacheSelectLimit()");
1502 $rs =& $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
1503 }
1504 return $rs;
1505 }
1506
1507
1508 /**
1509 * Flush cached recordsets that match a particular $sql statement.
1510 * If $sql == false, then we purge all files in the cache.
1511 */
1512
1513 /**
1514 * Flush cached recordsets that match a particular $sql statement.
1515 * If $sql == false, then we purge all files in the cache.
1516 */
1517 function CacheFlush($sql=false,$inputarr=false)
1518 {
1519 global $ADODB_CACHE_DIR;
1520
1521 if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
1522 /*if (strncmp(PHP_OS,'WIN',3) === 0)
1523 $dir = str_replace('/', '\\', $ADODB_CACHE_DIR);
1524 else */
1525 $dir = $ADODB_CACHE_DIR;
1526
1527 if ($this->debug) {
1528 ADOConnection::outp( "CacheFlush: $dir<br><pre>\n", $this->_dirFlush($dir),"</pre>");
1529 } else {
1530 $this->_dirFlush($dir);
1531 }
1532 return;
1533 }
1534
1535 global $ADODB_INCLUDED_CSV;
1536 if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
1537
1538 $f = $this->_gencachename($sql.serialize($inputarr),false);
1539 adodb_write_file($f,''); // is adodb_write_file needed?
1540 if (!@unlink($f)) {
1541 if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
1542 }
1543 }
1544
1545 /**
1546 * Private function to erase all of the files and subdirectories in a directory.
1547 *
1548 * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
1549 * Note: $kill_top_level is used internally in the function to flush subdirectories.
1550 */
1551 function _dirFlush($dir, $kill_top_level = false) {
1552 if(!$dh = @opendir($dir)) return;
1553
1554 while (($obj = readdir($dh))) {
1555 if($obj=='.' || $obj=='..')
1556 continue;
1557
1558 if (!@unlink($dir.'/'.$obj))
1559 $this->_dirFlush($dir.'/'.$obj, true);
1560 }
1561 if ($kill_top_level === true)
1562 @rmdir($dir);
1563 return true;
1564 }
1565
1566
1567 function xCacheFlush($sql=false,$inputarr=false)
1568 {
1569 global $ADODB_CACHE_DIR;
1570
1571 if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
1572 if (strncmp(PHP_OS,'WIN',3) === 0) {
1573 $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
1574 } else {
1575 //$cmd = 'find "'.$ADODB_CACHE_DIR.'" -type f -maxdepth 1 -print0 | xargs -0 rm -f';
1576 $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/[0-9a-f][0-9a-f]/';
1577 // old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`';
1578 }
1579 if ($this->debug) {
1580 ADOConnection::outp( "CacheFlush: $cmd<br><pre>\n", system($cmd),"</pre>");
1581 } else {
1582 exec($cmd);
1583 }
1584 return;
1585 }
1586
1587 global $ADODB_INCLUDED_CSV;
1588 if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
1589
1590 $f = $this->_gencachename($sql.serialize($inputarr),false);
1591 adodb_write_file($f,''); // is adodb_write_file needed?
1592 if (!@unlink($f)) {
1593 if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
1594 }
1595 }
1596
1597 /**
1598 * Private function to generate filename for caching.
1599 * Filename is generated based on:
1600 *
1601 * - sql statement
1602 * - database type (oci8, ibase, ifx, etc)
1603 * - database name
1604 * - userid
1605 * - setFetchMode (adodb 4.23)
1606 *
1607 * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
1608 * Assuming that we can have 50,000 files per directory with good performance,
1609 * then we can scale to 12.8 million unique cached recordsets. Wow!
1610 */
1611 function _gencachename($sql,$createdir)
1612 {
1613 global $ADODB_CACHE_DIR;
1614 static $notSafeMode;
1615
1616 if ($this->fetchMode === false) {
1617 global $ADODB_FETCH_MODE;
1618 $mode = $ADODB_FETCH_MODE;
1619 } else {
1620 $mode = $this->fetchMode;
1621 }
1622 $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
1623
1624 if (!isset($notSafeMode)) $notSafeMode = !ini_get('safe_mode');
1625 $dir = ($notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($m,0,2) : $ADODB_CACHE_DIR;
1626
1627 if ($createdir && $notSafeMode && !file_exists($dir)) {
1628 $oldu = umask(0);
1629 if (!mkdir($dir,0771))
1630 if ($this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql");
1631 umask($oldu);
1632 }
1633 return $dir.'/adodb_'.$m.'.cache';
1634 }
1635
1636
1637 /**
1638 * Execute SQL, caching recordsets.
1639 *
1640 * @param [secs2cache] seconds to cache data, set to 0 to force query.
1641 * This is an optional parameter.
1642 * @param sql SQL statement to execute
1643 * @param [inputarr] holds the input data to bind to
1644 * @return RecordSet or false
1645 */
1646 function &CacheExecute($secs2cache,$sql=false,$inputarr=false)
1647 {
1648
1649
1650 if (!is_numeric($secs2cache)) {
1651 $inputarr = $sql;
1652 $sql = $secs2cache;
1653 $secs2cache = $this->cacheSecs;
1654 }
1655
1656 if (is_array($sql)) {
1657 $sqlparam = $sql;
1658 $sql = $sql[0];
1659 } else
1660 $sqlparam = $sql;
1661
1662 global $ADODB_INCLUDED_CSV;
1663 if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
1664
1665 $md5file = $this->_gencachename($sql.serialize($inputarr),true);
1666 $err = '';
1667
1668 if ($secs2cache > 0){
1669 $rs = &csv2rs($md5file,$err,$secs2cache,$this->arrayClass);
1670 $this->numCacheHits += 1;
1671 } else {
1672 $err='Timeout 1';
1673 $rs = false;
1674 $this->numCacheMisses += 1;
1675 }
1676 if (!$rs) {
1677 // no cached rs found
1678 if ($this->debug) {
1679 if (get_magic_quotes_runtime()) {
1680 ADOConnection::outp("Please disable magic_quotes_runtime - it corrupts cache files :(");
1681 }
1682 if ($this->debug !== -1) ADOConnection::outp( " $md5file cache failure: $err (see sql below)");
1683 }
1684
1685 $rs = &$this->Execute($sqlparam,$inputarr);
1686
1687 if ($rs) {
1688 $eof = $rs->EOF;
1689 $rs = &$this->_rs2rs($rs); // read entire recordset into memory immediately
1690 $txt = _rs2serialize($rs,false,$sql); // serialize
1691
1692 if (!adodb_write_file($md5file,$txt,$this->debug)) {
1693 if ($fn = $this->raiseErrorFn) {
1694 $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this);
1695 }
1696 if ($this->debug) ADOConnection::outp( " Cache write error");
1697 }
1698 if ($rs->EOF && !$eof) {
1699 $rs->MoveFirst();
1700 //$rs = &csv2rs($md5file,$err);
1701 $rs->connection = &$this; // Pablo suggestion
1702 }
1703
1704 } else
1705 @unlink($md5file);
1706 } else {
1707 $this->_errorMsg = '';
1708 $this->_errorCode = 0;
1709
1710 if ($this->fnCacheExecute) {
1711 $fn = $this->fnCacheExecute;
1712 $fn($this, $secs2cache, $sql, $inputarr);
1713 }
1714 // ok, set cached object found
1715 $rs->connection = &$this; // Pablo suggestion
1716 if ($this->debug){
1717
1718 $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
1719 $ttl = $rs->timeCreated + $secs2cache - time();
1720 $s = is_array($sql) ? $sql[0] : $sql;
1721 if ($inBrowser) $s = '<i>'.htmlspecialchars($s).'</i>';
1722
1723 ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
1724 }
1725 }
1726 return $rs;
1727 }
1728
1729
1730 /*
1731 Similar to PEAR DB's autoExecute(), except that
1732 $mode can be 'INSERT' or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
1733 If $mode == 'UPDATE', then $where is compulsory as a safety measure.
1734
1735 $forceUpdate means that even if the data has not changed, perform update.
1736 */
1737 function& AutoExecute($table, $fields_values, $mode = 'INSERT', $where = FALSE, $forceUpdate=true, $magicq=false)
1738 {
1739 $false = false;
1740 $sql = 'SELECT * FROM '.$table;
1741 if ($where!==FALSE) $sql .= ' WHERE '.$where;
1742 else if ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) {
1743 ADOConnection::outp('AutoExecute: Illegal mode=UPDATE with empty WHERE clause');
1744 return $false;
1745 }
1746
1747 $rs =& $this->SelectLimit($sql,1);
1748 if (!$rs) return $false; // table does not exist
1749 $rs->tableName = $table;
1750
1751 switch((string) $mode) {
1752 case 'UPDATE':
1753 case '2':
1754 $sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate, $magicq);
1755 break;
1756 case 'INSERT':
1757 case '1':
1758 $sql = $this->GetInsertSQL($rs, $fields_values, $magicq);
1759 break;
1760 default:
1761 ADOConnection::outp("AutoExecute: Unknown mode=$mode");
1762 return $false;
1763 }
1764 $ret = false;
1765 if ($sql) $ret = $this->Execute($sql);
1766 if ($ret) $ret = true;
1767 return $ret;
1768 }
1769
1770
1771 /**
1772 * Generates an Update Query based on an existing recordset.
1773 * $arrFields is an associative array of fields with the value
1774 * that should be assigned.
1775 *
1776 * Note: This function should only be used on a recordset
1777 * that is run against a single table and sql should only
1778 * be a simple select stmt with no groupby/orderby/limit
1779 *
1780 * "Jonathan Younger" <jyounger@unilab.com>
1781 */
1782 function GetUpdateSQL(&$rs, $arrFields,$forceUpdate=false,$magicq=false,$force=null)
1783 {
1784 global $ADODB_INCLUDED_LIB;
1785
1786 //********************************************************//
1787 //This is here to maintain compatibility
1788 //with older adodb versions. Sets force type to force nulls if $forcenulls is set.
1789 if (!isset($force)) {
1790 global $ADODB_FORCE_TYPE;
1791 $force = $ADODB_FORCE_TYPE;
1792 }
1793 //********************************************************//
1794
1795 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
1796 return _adodb_getupdatesql($this,$rs,$arrFields,$forceUpdate,$magicq,$force);
1797 }
1798
1799 /**
1800 * Generates an Insert Query based on an existing recordset.
1801 * $arrFields is an associative array of fields with the value
1802 * that should be assigned.
1803 *
1804 * Note: This function should only be used on a recordset
1805 * that is run against a single table.
1806 */
1807 function GetInsertSQL(&$rs, $arrFields,$magicq=false,$force=null)
1808 {
1809 global $ADODB_INCLUDED_LIB;
1810 if (!isset($force)) {
1811 global $ADODB_FORCE_TYPE;
1812 $force = $ADODB_FORCE_TYPE;
1813
1814 }
1815 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
1816 return _adodb_getinsertsql($this,$rs,$arrFields,$magicq,$force);
1817 }
1818
1819
1820 /**
1821 * Update a blob column, given a where clause. There are more sophisticated
1822 * blob handling functions that we could have implemented, but all require
1823 * a very complex API. Instead we have chosen something that is extremely
1824 * simple to understand and use.
1825 *
1826 * Note: $blobtype supports 'BLOB' and 'CLOB', default is BLOB of course.
1827 *
1828 * Usage to update a $blobvalue which has a primary key blob_id=1 into a
1829 * field blobtable.blobcolumn:
1830 *
1831 * UpdateBlob('blobtable', 'blobcolumn', $blobvalue, 'blob_id=1');
1832 *
1833 * Insert example:
1834 *
1835 * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1836 * $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
1837 */
1838
1839 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
1840 {
1841 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
1842 }
1843
1844 /**
1845 * Usage:
1846 * UpdateBlob('TABLE', 'COLUMN', '/path/to/file', 'ID=1');
1847 *
1848 * $blobtype supports 'BLOB' and 'CLOB'
1849 *
1850 * $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
1851 * $conn->UpdateBlob('blobtable','blobcol',$blobpath,'id=1');
1852 */
1853 function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
1854 {
1855 $fd = fopen($path,'rb');
1856 if ($fd === false) return false;
1857 $val = fread($fd,filesize($path));
1858 fclose($fd);
1859 return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
1860 }
1861
1862 function BlobDecode($blob)
1863 {
1864 return $blob;
1865 }
1866
1867 function BlobEncode($blob)
1868 {
1869 return $blob;
1870 }
1871
1872 function SetCharSet($charset)
1873 {
1874 return false;
1875 }
1876
1877 function IfNull( $field, $ifNull )
1878 {
1879 return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
1880 }
1881
1882 function LogSQL($enable=true)
1883 {
1884 include_once(ADODB_DIR.'/adodb-perf.inc.php');
1885
1886 if ($enable) $this->fnExecute = 'adodb_log_sql';
1887 else $this->fnExecute = false;
1888
1889 $old = $this->_logsql;
1890 $this->_logsql = $enable;
1891 if ($enable && !$old) $this->_affected = false;
1892 return $old;
1893 }
1894
1895 function GetCharSet()
1896 {
1897 return false;
1898 }
1899
1900 /**
1901 * Usage:
1902 * UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
1903 *
1904 * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
1905 * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
1906 */
1907 function UpdateClob($table,$column,$val,$where)
1908 {
1909 return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
1910 }
1911
1912 // not the fastest implementation - quick and dirty - jlim
1913 // for best performance, use the actual $rs->MetaType().
1914 function MetaType($t,$len=-1,$fieldobj=false)
1915 {
1916
1917 if (empty($this->_metars)) {
1918 $rsclass = $this->rsPrefix.$this->databaseType;
1919 $this->_metars =& new $rsclass(false,$this->fetchMode);
1920 $this->_metars->connection =& $this;
1921 }
1922 return $this->_metars->MetaType($t,$len,$fieldobj);
1923 }
1924
1925
1926 /**
1927 * Change the SQL connection locale to a specified locale.
1928 * This is used to get the date formats written depending on the client locale.
1929 */
1930 function SetDateLocale($locale = 'En')
1931 {
1932 $this->locale = $locale;
1933 switch (strtoupper($locale))
1934 {
1935 case 'EN':
1936 $this->fmtDate="'Y-m-d'";
1937 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
1938 break;
1939
1940 case 'US':
1941 $this->fmtDate = "'m-d-Y'";
1942 $this->fmtTimeStamp = "'m-d-Y H:i:s'";
1943 break;
1944
1945 case 'NL':
1946 case 'FR':
1947 case 'RO':
1948 case 'IT':
1949 $this->fmtDate="'d-m-Y'";
1950 $this->fmtTimeStamp = "'d-m-Y H:i:s'";
1951 break;
1952
1953 case 'GE':
1954 $this->fmtDate="'d.m.Y'";
1955 $this->fmtTimeStamp = "'d.m.Y H:i:s'";
1956 break;
1957
1958 default:
1959 $this->fmtDate="'Y-m-d'";
1960 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
1961 break;
1962 }
1963 }
1964
1965 function &GetActiveRecordsClass($class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false)
1966 {
1967 global $_ADODB_ACTIVE_DBS;
1968
1969 $save = $this->SetFetchMode(ADODB_FETCH_NUM);
1970 if (empty($whereOrderBy)) $whereOrderBy = '1=1';
1971 $rows = $this->GetAll("select * from ".$table.' WHERE '.$whereOrderBy,$bindarr);
1972 $this->SetFetchMode($save);
1973
1974 $false = false;
1975
1976 if ($rows === false) {
1977 return $false;
1978 }
1979
1980
1981 if (!isset($_ADODB_ACTIVE_DBS)) {
1982 include(ADODB_DIR.'/adodb-active-record.inc.php');
1983 }
1984 if (!class_exists($class)) {
1985 ADOConnection::outp("Unknown class $class in GetActiveRcordsClass()");
1986 return $false;
1987 }
1988 $arr = array();
1989 foreach($rows as $row) {
1990
1991 $obj =& new $class($table,$primkeyArr,$this);
1992 if ($obj->ErrorMsg()){
1993 $this->_errorMsg = $obj->ErrorMsg();
1994 return $false;
1995 }
1996 $obj->Set($row);
1997 $arr[] =& $obj;
1998 }
1999 return $arr;
2000 }
2001
2002 function &GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false)
2003 {
2004 $arr =& $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
2005 return $arr;
2006 }
2007
2008 /**
2009 * Close Connection
2010 */
2011 function Close()
2012 {
2013 $rez = $this->_close();
2014 $this->_connectionID = false;
2015 return $rez;
2016 }
2017
2018 /**
2019 * Begin a Transaction. Must be followed by CommitTrans() or RollbackTrans().
2020 *
2021 * @return true if succeeded or false if database does not support transactions
2022 */
2023 function BeginTrans() {return false;}
2024
2025 /* set transaction mode */
2026 function SetTransactionMode( $transaction_mode )
2027 {
2028 $transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider);
2029 $this->_transmode = $transaction_mode;
2030 }
2031 /*
2032 http://msdn2.microsoft.com/en-US/ms173763.aspx
2033 http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
2034 http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
2035 http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm
2036 */
2037 function MetaTransaction($mode,$db)
2038 {
2039 $mode = strtoupper($mode);
2040 $mode = str_replace('ISOLATION LEVEL ','',$mode);
2041
2042 switch($mode) {
2043
2044 case 'READ UNCOMMITTED':
2045 switch($db) {
2046 case 'oci8':
2047 case 'oracle':
2048 return 'ISOLATION LEVEL READ COMMITTED';
2049 default:
2050 return 'ISOLATION LEVEL READ UNCOMMITTED';
2051 }
2052 break;
2053
2054 case 'READ COMMITTED':
2055 return 'ISOLATION LEVEL READ COMMITTED';
2056 break;
2057
2058 case 'REPEATABLE READ':
2059 switch($db) {
2060 case 'oci8':
2061 case 'oracle':
2062 return 'ISOLATION LEVEL SERIALIZABLE';
2063 default:
2064 return 'ISOLATION LEVEL REPEATABLE READ';
2065 }
2066 break;
2067
2068 case 'SERIALIZABLE':
2069 return 'ISOLATION LEVEL SERIALIZABLE';
2070 break;
2071
2072 default:
2073 return $mode;
2074 }
2075 }
2076
2077 /**
2078 * If database does not support transactions, always return true as data always commited
2079 *
2080 * @param $ok set to false to rollback transaction, true to commit
2081 *
2082 * @return true/false.
2083 */
2084 function CommitTrans($ok=true)
2085 { return true;}
2086
2087
2088 /**
2089 * If database does not support transactions, rollbacks always fail, so return false
2090 *
2091 * @return true/false.
2092 */
2093 function RollbackTrans()
2094 { return false;}
2095
2096
2097 /**
2098 * return the databases that the driver can connect to.
2099 * Some databases will return an empty array.
2100 *
2101 * @return an array of database names.
2102 */
2103 function MetaDatabases()
2104 {
2105 global $ADODB_FETCH_MODE;
2106
2107 if ($this->metaDatabasesSQL) {
2108 $save = $ADODB_FETCH_MODE;
2109 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2110
2111 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2112
2113 $arr = $this->GetCol($this->metaDatabasesSQL);
2114 if (isset($savem)) $this->SetFetchMode($savem);
2115 $ADODB_FETCH_MODE = $save;
2116
2117 return $arr;
2118 }
2119
2120 return false;
2121 }
2122
2123
2124 /**
2125 * @param ttype can either be 'VIEW' or 'TABLE' or false.
2126 * If false, both views and tables are returned.
2127 * "VIEW" returns only views
2128 * "TABLE" returns only tables
2129 * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
2130 * @param mask is the input mask - only supported by oci8 and postgresql
2131 *
2132 * @return array of tables for current database.
2133 */
2134 function &MetaTables($ttype=false,$showSchema=false,$mask=false)
2135 {
2136 global $ADODB_FETCH_MODE;
2137
2138
2139 $false = false;
2140 if ($mask) {
2141 return $false;
2142 }
2143 if ($this->metaTablesSQL) {
2144 $save = $ADODB_FETCH_MODE;
2145 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2146
2147 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2148
2149 $rs = $this->Execute($this->metaTablesSQL);
2150 if (isset($savem)) $this->SetFetchMode($savem);
2151 $ADODB_FETCH_MODE = $save;
2152
2153 if ($rs === false) return $false;
2154 $arr =& $rs->GetArray();
2155 $arr2 = array();
2156
2157 if ($hast = ($ttype && isset($arr[0][1]))) {
2158 $showt = strncmp($ttype,'T',1);
2159 }
2160
2161 for ($i=0; $i < sizeof($arr); $i++) {
2162 if ($hast) {
2163 if ($showt == 0) {
2164 if (strncmp($arr[$i][1],'T',1) == 0) $arr2[] = trim($arr[$i][0]);
2165 } else {
2166 if (strncmp($arr[$i][1],'V',1) == 0) $arr2[] = trim($arr[$i][0]);
2167 }
2168 } else
2169 $arr2[] = trim($arr[$i][0]);
2170 }
2171 $rs->Close();
2172 return $arr2;
2173 }
2174 return $false;
2175 }
2176
2177
2178 function _findschema(&$table,&$schema)
2179 {
2180 if (!$schema && ($at = strpos($table,'.')) !== false) {
2181 $schema = substr($table,0,$at);
2182 $table = substr($table,$at+1);
2183 }
2184 }
2185
2186 /**
2187 * List columns in a database as an array of ADOFieldObjects.
2188 * See top of file for definition of object.
2189 *
2190 * @param $table table name to query
2191 * @param $normalize makes table name case-insensitive (required by some databases)
2192 * @schema is optional database schema to use - not supported by all databases.
2193 *
2194 * @return array of ADOFieldObjects for current table.
2195 */
2196 function &MetaColumns($table,$normalize=true)
2197 {
2198 global $ADODB_FETCH_MODE;
2199
2200 $false = false;
2201
2202 if (!empty($this->metaColumnsSQL)) {
2203
2204 $schema = false;
2205 $this->_findschema($table,$schema);
2206
2207 $save = $ADODB_FETCH_MODE;
2208 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
2209 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
2210 $rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
2211 if (isset($savem)) $this->SetFetchMode($savem);
2212 $ADODB_FETCH_MODE = $save;
2213 if ($rs === false || $rs->EOF) return $false;
2214
2215 $retarr = array();
2216 while (!$rs->EOF) { //print_r($rs->fields);
2217 $fld = new ADOFieldObject();
2218 $fld->name = $rs->fields[0];
2219 $fld->type = $rs->fields[1];
2220 if (isset($rs->fields[3]) && $rs->fields[3]) {
2221 if ($rs->fields[3]>0) $fld->max_length = $rs->fields[3];
2222 $fld->scale = $rs->fields[4];
2223 if ($fld->scale>0) $fld->max_length += 1;
2224 } else
2225 $fld->max_length = $rs->fields[2];
2226
2227 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
2228 else $retarr[strtoupper($fld->name)] = $fld;
2229 $rs->MoveNext();
2230 }
2231 $rs->Close();
2232 return $retarr;
2233 }
2234 return $false;
2235 }
2236
2237 /**
2238 * List indexes on a table as an array.
2239 * @param table table name to query
2240 * @param primary true to only show primary keys. Not actually used for most databases
2241 *
2242 * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
2243
2244 Array (
2245 [name_of_index] => Array
2246 (
2247 [unique] => true or false
2248 [columns] => Array
2249 (
2250 [0] => firstname
2251 [1] => lastname
2252 )
2253 )
2254 */
2255 function &MetaIndexes($table, $primary = false, $owner = false)
2256 {
2257 $false = false;
2258 return $false;
2259 }
2260
2261 /**
2262 * List columns names in a table as an array.
2263 * @param table table name to query
2264 *
2265 * @return array of column names for current table.
2266 */
2267 function &MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */)
2268 {
2269 $objarr =& $this->MetaColumns($table);
2270 if (!is_array($objarr)) {
2271 $false = false;
2272 return $false;
2273 }
2274 $arr = array();
2275 if ($numIndexes) {
2276 $i = 0;
2277 if ($useattnum) {
2278 foreach($objarr as $v)
2279 $arr[$v->attnum] = $v->name;
2280
2281 } else
2282 foreach($objarr as $v) $arr[$i++] = $v->name;
2283 } else
2284 foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
2285
2286 return $arr;
2287 }
2288
2289 /**
2290 * Different SQL databases used different methods to combine strings together.
2291 * This function provides a wrapper.
2292 *
2293 * param s variable number of string parameters
2294 *
2295 * Usage: $db->Concat($str1,$str2);
2296 *
2297 * @return concatenated string
2298 */
2299 function Concat()
2300 {
2301 $arr = func_get_args();
2302 return implode($this->concat_operator, $arr);
2303 }
2304
2305
2306 /**
2307 * Converts a date "d" to a string that the database can understand.
2308 *
2309 * @param d a date in Unix date time format.
2310 *
2311 * @return date string in database date format
2312 */
2313 function DBDate($d)
2314 {
2315 if (empty($d) && $d !== 0) return 'null';
2316
2317 if (is_string($d) && !is_numeric($d)) {
2318 if ($d === 'null' || strncmp($d,"'",1) === 0) return $d;
2319 if ($this->isoDates) return "'$d'";
2320 $d = ADOConnection::UnixDate($d);
2321 }
2322
2323 return adodb_date($this->fmtDate,$d);
2324 }
2325
2326 function BindDate($d)
2327 {
2328 $d = $this->DBDate($d);
2329 if (strncmp($d,"'",1)) return $d;
2330
2331 return substr($d,1,strlen($d)-2);
2332 }
2333
2334 function BindTimeStamp($d)
2335 {
2336 $d = $this->DBTimeStamp($d);
2337 if (strncmp($d,"'",1)) return $d;
2338
2339 return substr($d,1,strlen($d)-2);
2340 }
2341
2342
2343 /**
2344 * Converts a timestamp "ts" to a string that the database can understand.
2345 *
2346 * @param ts a timestamp in Unix date time format.
2347 *
2348 * @return timestamp string in database timestamp format
2349 */
2350 function DBTimeStamp($ts)
2351 {
2352 if (empty($ts) && $ts !== 0) return 'null';
2353
2354 # strlen(14) allows YYYYMMDDHHMMSS format
2355 if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14))
2356 return adodb_date($this->fmtTimeStamp,$ts);
2357
2358 if ($ts === 'null') return $ts;
2359 if ($this->isoDates && strlen($ts) !== 14) return "'$ts'";
2360
2361 $ts = ADOConnection::UnixTimeStamp($ts);
2362 return adodb_date($this->fmtTimeStamp,$ts);
2363 }
2364
2365 /**
2366 * Also in ADORecordSet.
2367 * @param $v is a date string in YYYY-MM-DD format
2368 *
2369 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2370 */
2371 function UnixDate($v)
2372 {
2373 if (is_object($v)) {
2374 // odbtp support
2375 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2376 return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2377 }
2378
2379 if (is_numeric($v) && strlen($v) !== 8) return $v;
2380 if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|",
2381 ($v), $rr)) return false;
2382
2383 if ($rr[1] <= TIMESTAMP_FIRST_YEAR) return 0;
2384 // h-m-s-MM-DD-YY
2385 return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2386 }
2387
2388
2389 /**
2390 * Also in ADORecordSet.
2391 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2392 *
2393 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2394 */
2395 function UnixTimeStamp($v)
2396 {
2397 if (is_object($v)) {
2398 // odbtp support
2399 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
2400 return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
2401 }
2402
2403 if (!preg_match(
2404 "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
2405 ($v), $rr)) return false;
2406
2407 if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) return 0;
2408
2409 // h-m-s-MM-DD-YY
2410 if (!isset($rr[5])) return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
2411 return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
2412 }
2413
2414 /**
2415 * Also in ADORecordSet.
2416 *
2417 * Format database date based on user defined format.
2418 *
2419 * @param v is the character date in YYYY-MM-DD format, returned by database
2420 * @param fmt is the format to apply to it, using date()
2421 *
2422 * @return a date formated as user desires
2423 */
2424
2425 function UserDate($v,$fmt='Y-m-d',$gmt=false)
2426 {
2427 $tt = $this->UnixDate($v);
2428
2429 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2430 if (($tt === false || $tt == -1) && $v != false) return $v;
2431 else if ($tt == 0) return $this->emptyDate;
2432 else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
2433 }
2434
2435 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2436
2437 }
2438
2439 /**
2440 *
2441 * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format
2442 * @param fmt is the format to apply to it, using date()
2443 *
2444 * @return a timestamp formated as user desires
2445 */
2446 function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false)
2447 {
2448 if (!isset($v)) return $this->emptyTimeStamp;
2449 # strlen(14) allows YYYYMMDDHHMMSS format
2450 if (is_numeric($v) && strlen($v)<14) return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
2451 $tt = $this->UnixTimeStamp($v);
2452 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2453 if (($tt === false || $tt == -1) && $v != false) return $v;
2454 if ($tt == 0) return $this->emptyTimeStamp;
2455 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
2456 }
2457
2458 function escape($s,$magic_quotes=false)
2459 {
2460 return $this->addq($s,$magic_quotes);
2461 }
2462
2463 /**
2464 * Quotes a string, without prefixing nor appending quotes.
2465 */
2466 function addq($s,$magic_quotes=false)
2467 {
2468 if (!$magic_quotes) {
2469
2470 if ($this->replaceQuote[0] == '\\'){
2471 // only since php 4.0.5
2472 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2473 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2474 }
2475 return str_replace("'",$this->replaceQuote,$s);
2476 }
2477
2478 // undo magic quotes for "
2479 $s = str_replace('\\"','"',$s);
2480
2481 if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything
2482 return $s;
2483 else {// change \' to '' for sybase/mssql
2484 $s = str_replace('\\\\','\\',$s);
2485 return str_replace("\\'",$this->replaceQuote,$s);
2486 }
2487 }
2488
2489 /**
2490 * Correctly quotes a string so that all strings are escaped. We prefix and append
2491 * to the string single-quotes.
2492 * An example is $db->qstr("Don't bother",magic_quotes_runtime());
2493 *
2494 * @param s the string to quote
2495 * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
2496 * This undoes the stupidity of magic quotes for GPC.
2497 *
2498 * @return quoted string to be sent back to database
2499 */
2500 function qstr($s,$magic_quotes=false)
2501 {
2502 if (!$magic_quotes) {
2503
2504 if ($this->replaceQuote[0] == '\\'){
2505 // only since php 4.0.5
2506 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
2507 //$s = str_replace("\0","\\\0", str_replace('\\','\\\\',$s));
2508 }
2509 return "'".str_replace("'",$this->replaceQuote,$s)."'";
2510 }
2511
2512 // undo magic quotes for "
2513 $s = str_replace('\\"','"',$s);
2514
2515 if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything
2516 return "'$s'";
2517 else {// change \' to '' for sybase/mssql
2518 $s = str_replace('\\\\','\\',$s);
2519 return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
2520 }
2521 }
2522
2523
2524 /**
2525 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
2526 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
2527 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2528 *
2529 * See readme.htm#ex8 for an example of usage.
2530 *
2531 * @param sql
2532 * @param nrows is the number of rows per page to get
2533 * @param page is the page number to get (1-based)
2534 * @param [inputarr] array of bind variables
2535 * @param [secs2cache] is a private parameter only used by jlim
2536 * @return the recordset ($rs->databaseType == 'array')
2537 *
2538 * NOTE: phpLens uses a different algorithm and does not use PageExecute().
2539 *
2540 */
2541 function &PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0)
2542 {
2543 global $ADODB_INCLUDED_LIB;
2544 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
2545 if ($this->pageExecuteCountRows) $rs =& _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2546 else $rs =& _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
2547 return $rs;
2548 }
2549
2550
2551 /**
2552 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
2553 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
2554 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
2555 *
2556 * @param secs2cache seconds to cache data, set to 0 to force query
2557 * @param sql
2558 * @param nrows is the number of rows per page to get
2559 * @param page is the page number to get (1-based)
2560 * @param [inputarr] array of bind variables
2561 * @return the recordset ($rs->databaseType == 'array')
2562 */
2563 function &CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false)
2564 {
2565 /*switch($this->dataProvider) {
2566 case 'postgres':
2567 case 'mysql':
2568 break;
2569 default: $secs2cache = 0; break;
2570 }*/
2571 $rs =& $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
2572 return $rs;
2573 }
2574
2575 } // end class ADOConnection
2576
2577
2578
2579 //==============================================================================================
2580 // CLASS ADOFetchObj
2581 //==============================================================================================
2582
2583 /**
2584 * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
2585 */
2586 class ADOFetchObj {
2587 };
2588
2589 //==============================================================================================
2590 // CLASS ADORecordSet_empty
2591 //==============================================================================================
2592
2593 /**
2594 * Lightweight recordset when there are no records to be returned
2595 */
2596 class ADORecordSet_empty
2597 {
2598 var $dataProvider = 'empty';
2599 var $databaseType = false;
2600 var $EOF = true;
2601 var $_numOfRows = 0;
2602 var $fields = false;
2603 var $connection = false;
2604 function RowCount() {return 0;}
2605 function RecordCount() {return 0;}
2606 function PO_RecordCount(){return 0;}
2607 function Close(){return true;}
2608 function FetchRow() {return false;}
2609 function FieldCount(){ return 0;}
2610 function Init() {}
2611 }
2612
2613 //==============================================================================================
2614 // DATE AND TIME FUNCTIONS
2615 //==============================================================================================
2616 if (!defined('ADODB_DATE_VERSION')) include(ADODB_DIR.'/adodb-time.inc.php');
2617
2618 //==============================================================================================
2619 // CLASS ADORecordSet
2620 //==============================================================================================
2621
2622 if (PHP_VERSION < 5) include_once(ADODB_DIR.'/adodb-php4.inc.php');
2623 else include_once(ADODB_DIR.'/adodb-iterator.inc.php');
2624 /**
2625 * RecordSet class that represents the dataset returned by the database.
2626 * To keep memory overhead low, this class holds only the current row in memory.
2627 * No prefetching of data is done, so the RecordCount() can return -1 ( which
2628 * means recordcount not known).
2629 */
2630 class ADORecordSet extends ADODB_BASE_RS {
2631 /*
2632 * public variables
2633 */
2634 var $dataProvider = "native";
2635 var $fields = false; /// holds the current row data
2636 var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob
2637 /// in other words, we use a text area for editing.
2638 var $canSeek = false; /// indicates that seek is supported
2639 var $sql; /// sql text
2640 var $EOF = false; /// Indicates that the current record position is after the last record in a Recordset object.
2641
2642 var $emptyTimeStamp = '&nbsp;'; /// what to display when $time==0
2643 var $emptyDate = '&nbsp;'; /// what to display when $time==0
2644 var $debug = false;
2645 var $timeCreated=0; /// datetime in Unix format rs created -- for cached recordsets
2646
2647 var $bind = false; /// used by Fields() to hold array - should be private?
2648 var $fetchMode; /// default fetch mode
2649 var $connection = false; /// the parent connection
2650 /*
2651 * private variables
2652 */
2653 var $_numOfRows = -1; /** number of rows, or -1 */
2654 var $_numOfFields = -1; /** number of fields in recordset */
2655 var $_queryID = -1; /** This variable keeps the result link identifier. */
2656 var $_currentRow = -1; /** This variable keeps the current row in the Recordset. */
2657 var $_closed = false; /** has recordset been closed */
2658 var $_inited = false; /** Init() should only be called once */
2659 var $_obj; /** Used by FetchObj */
2660 var $_names; /** Used by FetchObj */
2661
2662 var $_currentPage = -1; /** Added by Iván Oliva to implement recordset pagination */
2663 var $_atFirstPage = false; /** Added by Iván Oliva to implement recordset pagination */
2664 var $_atLastPage = false; /** Added by Iván Oliva to implement recordset pagination */
2665 var $_lastPageNo = -1;
2666 var $_maxRecordCount = 0;
2667 var $datetime = false;
2668
2669 /**
2670 * Constructor
2671 *
2672 * @param queryID this is the queryID returned by ADOConnection->_query()
2673 *
2674 */
2675 function ADORecordSet($queryID)
2676 {
2677 $this->_queryID = $queryID;
2678 }
2679
2680
2681
2682 function Init()
2683 {
2684 if ($this->_inited) return;
2685 $this->_inited = true;
2686 if ($this->_queryID) @$this->_initrs();
2687 else {
2688 $this->_numOfRows = 0;
2689 $this->_numOfFields = 0;
2690 }
2691 if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
2692
2693 $this->_currentRow = 0;
2694 if ($this->EOF = ($this->_fetch() === false)) {
2695 $this->_numOfRows = 0; // _numOfRows could be -1
2696 }
2697 } else {
2698 $this->EOF = true;
2699 }
2700 }
2701
2702
2703 /**
2704 * Generate a SELECT tag string from a recordset, and return the string.
2705 * If the recordset has 2 cols, we treat the 1st col as the containing
2706 * the text to display to the user, and 2nd col as the return value. Default
2707 * strings are compared with the FIRST column.
2708 *
2709 * @param name name of SELECT tag
2710 * @param [defstr] the value to hilite. Use an array for multiple hilites for listbox.
2711 * @param [blank1stItem] true to leave the 1st item in list empty
2712 * @param [multiple] true for listbox, false for popup
2713 * @param [size] #rows to show for listbox. not used by popup
2714 * @param [selectAttr] additional attributes to defined for SELECT tag.
2715 * useful for holding javascript onChange='...' handlers.
2716 & @param [compareFields0] when we have 2 cols in recordset, we compare the defstr with
2717 * column 0 (1st col) if this is true. This is not documented.
2718 *
2719 * @return HTML
2720 *
2721 * changes by glen.davies@cce.ac.nz to support multiple hilited items
2722 */
2723 function GetMenu($name,$defstr='',$blank1stItem=true,$multiple=false,
2724 $size=0, $selectAttr='',$compareFields0=true)
2725 {
2726 global $ADODB_INCLUDED_LIB;
2727 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
2728 return _adodb_getmenu($this, $name,$defstr,$blank1stItem,$multiple,
2729 $size, $selectAttr,$compareFields0);
2730 }
2731
2732
2733
2734 /**
2735 * Generate a SELECT tag string from a recordset, and return the string.
2736 * If the recordset has 2 cols, we treat the 1st col as the containing
2737 * the text to display to the user, and 2nd col as the return value. Default
2738 * strings are compared with the SECOND column.
2739 *
2740 */
2741 function GetMenu2($name,$defstr='',$blank1stItem=true,$multiple=false,$size=0, $selectAttr='')
2742 {
2743 return $this->GetMenu($name,$defstr,$blank1stItem,$multiple,
2744 $size, $selectAttr,false);
2745 }
2746
2747 /*
2748 Grouped Menu
2749 */
2750 function GetMenu3($name,$defstr='',$blank1stItem=true,$multiple=false,
2751 $size=0, $selectAttr='')
2752 {
2753 global $ADODB_INCLUDED_LIB;
2754 if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
2755 return _adodb_getmenu_gp($this, $name,$defstr,$blank1stItem,$multiple,
2756 $size, $selectAttr,false);
2757 }
2758
2759 /**
2760 * return recordset as a 2-dimensional array.
2761 *
2762 * @param [nRows] is the number of rows to return. -1 means every row.
2763 *
2764 * @return an array indexed by the rows (0-based) from the recordset
2765 */
2766 function &GetArray($nRows = -1)
2767 {
2768 global $ADODB_EXTENSION; if ($ADODB_EXTENSION) {
2769 $results = adodb_getall($this,$nRows);
2770 return $results;
2771 }
2772 $results = array();
2773 $cnt = 0;
2774 while (!$this->EOF && $nRows != $cnt) {
2775 $results[] = $this->fields;
2776 $this->MoveNext();
2777 $cnt++;
2778 }
2779 return $results;
2780 }
2781
2782 function &GetAll($nRows = -1)
2783 {
2784 $arr =& $this->GetArray($nRows);
2785 return $arr;
2786 }
2787
2788 /*
2789 * Some databases allow multiple recordsets to be returned. This function
2790 * will return true if there is a next recordset, or false if no more.
2791 */
2792 function NextRecordSet()
2793 {
2794 return false;
2795 }
2796
2797 /**
2798 * return recordset as a 2-dimensional array.
2799 * Helper function for ADOConnection->SelectLimit()
2800 *
2801 * @param offset is the row to start calculations from (1-based)
2802 * @param [nrows] is the number of rows to return
2803 *
2804 * @return an array indexed by the rows (0-based) from the recordset
2805 */
2806 function &GetArrayLimit($nrows,$offset=-1)
2807 {
2808 if ($offset <= 0) {
2809 $arr =& $this->GetArray($nrows);
2810 return $arr;
2811 }
2812
2813 $this->Move($offset);
2814
2815 $results = array();
2816 $cnt = 0;
2817 while (!$this->EOF && $nrows != $cnt) {
2818 $results[$cnt++] = $this->fields;
2819 $this->MoveNext();
2820 }
2821
2822 return $results;
2823 }
2824
2825
2826 /**
2827 * Synonym for GetArray() for compatibility with ADO.
2828 *
2829 * @param [nRows] is the number of rows to return. -1 means every row.
2830 *
2831 * @return an array indexed by the rows (0-based) from the recordset
2832 */
2833 function &GetRows($nRows = -1)
2834 {
2835 $arr =& $this->GetArray($nRows);
2836 return $arr;
2837 }
2838
2839 /**
2840 * return whole recordset as a 2-dimensional associative array if there are more than 2 columns.
2841 * The first column is treated as the key and is not included in the array.
2842 * If there is only 2 columns, it will return a 1 dimensional array of key-value pairs unless
2843 * $force_array == true.
2844 *
2845 * @param [force_array] has only meaning if we have 2 data columns. If false, a 1 dimensional
2846 * array is returned, otherwise a 2 dimensional array is returned. If this sounds confusing,
2847 * read the source.
2848 *
2849 * @param [first2cols] means if there are more than 2 cols, ignore the remaining cols and
2850 * instead of returning array[col0] => array(remaining cols), return array[col0] => col1
2851 *
2852 * @return an associative array indexed by the first column of the array,
2853 * or false if the data has less than 2 cols.
2854 */
2855 function &GetAssoc($force_array = false, $first2cols = false)
2856 {
2857 global $ADODB_EXTENSION;
2858
2859 $cols = $this->_numOfFields;
2860 if ($cols < 2) {
2861 $false = false;
2862 return $false;
2863 }
2864 $numIndex = isset($this->fields[0]);
2865 $results = array();
2866
2867 if (!$first2cols && ($cols > 2 || $force_array)) {
2868 if ($ADODB_EXTENSION) {
2869 if ($numIndex) {
2870 while (!$this->EOF) {
2871 $results[trim($this->fields[0])] = array_slice($this->fields, 1);
2872 adodb_movenext($this);
2873 }
2874 } else {
2875 while (!$this->EOF) {
2876 $results[trim(reset($this->fields))] = array_slice($this->fields, 1);
2877 adodb_movenext($this);
2878 }
2879 }
2880 } else {
2881 if ($numIndex) {
2882 while (!$this->EOF) {
2883 $results[trim($this->fields[0])] = array_slice($this->fields, 1);
2884 $this->MoveNext();
2885 }
2886 } else {
2887 while (!$this->EOF) {
2888 $results[trim(reset($this->fields))] = array_slice($this->fields, 1);
2889 $this->MoveNext();
2890 }
2891 }
2892 }
2893 } else {
2894 if ($ADODB_EXTENSION) {
2895 // return scalar values
2896 if ($numIndex) {
2897 while (!$this->EOF) {
2898 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2899 $results[trim(($this->fields[0]))] = $this->fields[1];
2900 adodb_movenext($this);
2901 }
2902 } else {
2903 while (!$this->EOF) {
2904 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2905 $v1 = trim(reset($this->fields));
2906 $v2 = ''.next($this->fields);
2907 $results[$v1] = $v2;
2908 adodb_movenext($this);
2909 }
2910 }
2911 } else {
2912 if ($numIndex) {
2913 while (!$this->EOF) {
2914 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2915 $results[trim(($this->fields[0]))] = $this->fields[1];
2916 $this->MoveNext();
2917 }
2918 } else {
2919 while (!$this->EOF) {
2920 // some bug in mssql PHP 4.02 -- doesn't handle references properly so we FORCE creating a new string
2921 $v1 = trim(reset($this->fields));
2922 $v2 = ''.next($this->fields);
2923 $results[$v1] = $v2;
2924 $this->MoveNext();
2925 }
2926 }
2927 }
2928 }
2929
2930 $ref =& $results; # workaround accelerator incompat with PHP 4.4 :(
2931 return $ref;
2932 }
2933
2934
2935 /**
2936 *
2937 * @param v is the character timestamp in YYYY-MM-DD hh:mm:ss format
2938 * @param fmt is the format to apply to it, using date()
2939 *
2940 * @return a timestamp formated as user desires
2941 */
2942 function UserTimeStamp($v,$fmt='Y-m-d H:i:s')
2943 {
2944 if (is_numeric($v) && strlen($v)<14) return adodb_date($fmt,$v);
2945 $tt = $this->UnixTimeStamp($v);
2946 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2947 if (($tt === false || $tt == -1) && $v != false) return $v;
2948 if ($tt === 0) return $this->emptyTimeStamp;
2949 return adodb_date($fmt,$tt);
2950 }
2951
2952
2953 /**
2954 * @param v is the character date in YYYY-MM-DD format, returned by database
2955 * @param fmt is the format to apply to it, using date()
2956 *
2957 * @return a date formated as user desires
2958 */
2959 function UserDate($v,$fmt='Y-m-d')
2960 {
2961 $tt = $this->UnixDate($v);
2962 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
2963 if (($tt === false || $tt == -1) && $v != false) return $v;
2964 else if ($tt == 0) return $this->emptyDate;
2965 else if ($tt == -1) { // pre-TIMESTAMP_FIRST_YEAR
2966 }
2967 return adodb_date($fmt,$tt);
2968 }
2969
2970
2971 /**
2972 * @param $v is a date string in YYYY-MM-DD format
2973 *
2974 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2975 */
2976 function UnixDate($v)
2977 {
2978 return ADOConnection::UnixDate($v);
2979 }
2980
2981
2982 /**
2983 * @param $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
2984 *
2985 * @return date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
2986 */
2987 function UnixTimeStamp($v)
2988 {
2989 return ADOConnection::UnixTimeStamp($v);
2990 }
2991
2992
2993 /**
2994 * PEAR DB Compat - do not use internally
2995 */
2996 function Free()
2997 {
2998 return $this->Close();
2999 }
3000
3001
3002 /**
3003 * PEAR DB compat, number of rows
3004 */
3005 function NumRows()
3006 {
3007 return $this->_numOfRows;
3008 }
3009
3010
3011 /**
3012 * PEAR DB compat, number of cols
3013 */
3014 function NumCols()
3015 {
3016 return $this->_numOfFields;
3017 }
3018
3019 /**
3020 * Fetch a row, returning false if no more rows.
3021 * This is PEAR DB compat mode.
3022 *
3023 * @return false or array containing the current record
3024 */
3025 function &FetchRow()
3026 {
3027 if ($this->EOF) {
3028 $false = false;
3029 return $false;
3030 }
3031 $arr = $this->fields;
3032 $this->_currentRow++;
3033 if (!$this->_fetch()) $this->EOF = true;
3034 return $arr;
3035 }
3036
3037
3038 /**
3039 * Fetch a row, returning PEAR_Error if no more rows.
3040 * This is PEAR DB compat mode.
3041 *
3042 * @return DB_OK or error object
3043 */
3044 function FetchInto(&$arr)
3045 {
3046 if ($this->EOF) return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
3047 $arr = $this->fields;
3048 $this->MoveNext();
3049 return 1; // DB_OK
3050 }
3051
3052
3053 /**
3054 * Move to the first row in the recordset. Many databases do NOT support this.
3055 *
3056 * @return true or false
3057 */
3058 function MoveFirst()
3059 {
3060 if ($this->_currentRow == 0) return true;
3061 return $this->Move(0);
3062 }
3063
3064
3065 /**
3066 * Move to the last row in the recordset.
3067 *
3068 * @return true or false
3069 */
3070 function MoveLast()
3071 {
3072 if ($this->_numOfRows >= 0) return $this->Move($this->_numOfRows-1);
3073 if ($this->EOF) return false;
3074 while (!$this->EOF) {
3075 $f = $this->fields;
3076 $this->MoveNext();
3077 }
3078 $this->fields = $f;
3079 $this->EOF = false;
3080 return true;
3081 }
3082
3083
3084 /**
3085 * Move to next record in the recordset.
3086 *
3087 * @return true if there still rows available, or false if there are no more rows (EOF).
3088 */
3089 function MoveNext()
3090 {
3091 if (!$this->EOF) {
3092 $this->_currentRow++;
3093 if ($this->_fetch()) return true;
3094 }
3095 $this->EOF = true;
3096 /* -- tested error handling when scrolling cursor -- seems useless.
3097 $conn = $this->connection;
3098 if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
3099 $fn = $conn->raiseErrorFn;
3100 $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
3101 }
3102 */
3103 return false;
3104 }
3105
3106
3107 /**
3108 * Random access to a specific row in the recordset. Some databases do not support
3109 * access to previous rows in the databases (no scrolling backwards).
3110 *
3111 * @param rowNumber is the row to move to (0-based)
3112 *
3113 * @return true if there still rows available, or false if there are no more rows (EOF).
3114 */
3115 function Move($rowNumber = 0)
3116 {
3117 $this->EOF = false;
3118 if ($rowNumber == $this->_currentRow) return true;
3119 if ($rowNumber >= $this->_numOfRows)
3120 if ($this->_numOfRows != -1) $rowNumber = $this->_numOfRows-2;
3121
3122 if ($this->canSeek) {
3123
3124 if ($this->_seek($rowNumber)) {
3125 $this->_currentRow = $rowNumber;
3126 if ($this->_fetch()) {
3127 return true;
3128 }
3129 } else {
3130 $this->EOF = true;
3131 return false;
3132 }
3133 } else {
3134 if ($rowNumber < $this->_currentRow) return false;
3135 global $ADODB_EXTENSION;
3136 if ($ADODB_EXTENSION) {
3137 while (!$this->EOF && $this->_currentRow < $rowNumber) {
3138 adodb_movenext($this);
3139 }
3140 } else {
3141
3142 while (! $this->EOF && $this->_currentRow < $rowNumber) {
3143 $this->_currentRow++;
3144
3145 if (!$this->_fetch()) $this->EOF = true;
3146 }
3147 }
3148 return !($this->EOF);
3149 }
3150
3151 $this->fields = false;
3152 $this->EOF = true;
3153 return false;
3154 }
3155
3156
3157 /**
3158 * Get the value of a field in the current row by column name.
3159 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
3160 *
3161 * @param colname is the field to access
3162 *
3163 * @return the value of $colname column
3164 */
3165 function Fields($colname)
3166 {
3167 return $this->fields[$colname];
3168 }
3169
3170 function GetAssocKeys($upper=true)
3171 {
3172 $this->bind = array();
3173 for ($i=0; $i < $this->_numOfFields; $i++) {
3174 $o = $this->FetchField($i);
3175 if ($upper === 2) $this->bind[$o->name] = $i;
3176 else $this->bind[($upper) ? strtoupper($o->name) : strtolower($o->name)] = $i;
3177 }
3178 }
3179
3180 /**
3181 * Use associative array to get fields array for databases that do not support
3182 * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
3183 *
3184 * If you don't want uppercase cols, set $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC
3185 * before you execute your SQL statement, and access $rs->fields['col'] directly.
3186 *
3187 * $upper 0 = lowercase, 1 = uppercase, 2 = whatever is returned by FetchField
3188 */
3189 function &GetRowAssoc($upper=1)
3190 {
3191 $record = array();
3192 // if (!$this->fields) return $record;
3193
3194 if (!$this->bind) {
3195 $this->GetAssocKeys($upper);
3196 }
3197
3198 foreach($this->bind as $k => $v) {
3199 $record[$k] = $this->fields[$v];
3200 }
3201
3202 return $record;
3203 }
3204
3205
3206 /**
3207 * Clean up recordset
3208 *
3209 * @return true or false
3210 */
3211 function Close()
3212 {
3213 // free connection object - this seems to globally free the object
3214 // and not merely the reference, so don't do this...
3215 // $this->connection = false;
3216 if (!$this->_closed) {
3217 $this->_closed = true;
3218 return $this->_close();
3219 } else
3220 return true;
3221 }
3222
3223 /**
3224 * synonyms RecordCount and RowCount
3225 *
3226 * @return the number of rows or -1 if this is not supported
3227 */
3228 function RecordCount() {return $this->_numOfRows;}
3229
3230
3231 /*
3232 * If we are using PageExecute(), this will return the maximum possible rows
3233 * that can be returned when paging a recordset.
3234 */
3235 function MaxRecordCount()
3236 {
3237 return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->RecordCount();
3238 }
3239
3240 /**
3241 * synonyms RecordCount and RowCount
3242 *
3243 * @return the number of r