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