MDL-76897 quiz: quiz_set_grade -> update_quiz_maximum_grade
[moodle.git] / lib / adodb / adodb.inc.php
blob7ed8242b0db1fa6e30d4a3e7612f22ac2272651b
1 <?php
2 /**
3 * ADOdb Library main include file.
5 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
7 * @package ADOdb
8 * @link https://adodb.org Project's web site and documentation
9 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
11 * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
12 * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
13 * any later version. This means you can use it in proprietary products.
14 * See the LICENSE.md file distributed with this source code for details.
15 * @license BSD-3-Clause
16 * @license LGPL-2.1-or-later
18 * @copyright 2000-2013 John Lim
19 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
22 if (!defined('_ADODB_LAYER')) {
23 define('_ADODB_LAYER',1);
25 // The ADOdb extension is no longer maintained and effectively unsupported
26 // since v5.04. The library will not function properly if it is present.
27 if(defined('ADODB_EXTENSION')) {
28 $msg = "Unsupported ADOdb Extension (v" . ADODB_EXTENSION . ") detected! "
29 . "Disable it to use ADOdb";
31 $errorfn = defined('ADODB_ERROR_HANDLER') ? ADODB_ERROR_HANDLER : false;
32 if ($errorfn) {
33 $conn = false;
34 $errorfn('ADOdb', basename(__FILE__), -9999, $msg, null, null, $conn);
35 } else {
36 die($msg . PHP_EOL);
40 //==============================================================================================
41 // CONSTANT DEFINITIONS
42 //==============================================================================================
45 /**
46 * Set ADODB_DIR to the directory where this file resides...
47 * This constant was formerly called $ADODB_RootPath
49 if (!defined('ADODB_DIR')) {
50 define('ADODB_DIR',dirname(__FILE__));
53 //==============================================================================================
54 // GLOBAL VARIABLES
55 //==============================================================================================
57 GLOBAL
58 $ADODB_vers, // database version
59 $ADODB_COUNTRECS, // count number of records returned - slows down query
60 $ADODB_CACHE_DIR, // directory to cache recordsets
61 $ADODB_CACHE,
62 $ADODB_CACHE_CLASS,
63 $ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
64 $ADODB_FETCH_MODE, // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
65 $ADODB_GETONE_EOF,
66 $ADODB_QUOTE_FIELDNAMES; // Allows you to force quotes (backticks) around field names in queries generated by getinsertsql and getupdatesql.
68 //==============================================================================================
69 // GLOBAL SETUP
70 //==============================================================================================
72 /*********************************************************
73 * Controls $ADODB_FORCE_TYPE mode. Default is ADODB_FORCE_VALUE (3).
74 * Used in GetUpdateSql and GetInsertSql functions. Thx to Niko, nuko#mbnet.fi
75 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:adodb_force_type
77 * 0 = ignore empty fields. All empty fields in array are ignored.
78 * 1 = force null. All empty, php null and string 'null' fields are
79 * changed to sql NULL values.
80 * 2 = force empty. All empty, php null and string 'null' fields are
81 * changed to sql empty '' or 0 values.
82 * 3 = force value. Value is left as it is. Php null and string 'null'
83 * are set to sql NULL values and empty fields '' are set to empty '' sql values.
84 * 4 = force value. Like 1 but numeric empty fields are set to zero.
86 define('ADODB_FORCE_IGNORE',0);
87 define('ADODB_FORCE_NULL',1);
88 define('ADODB_FORCE_EMPTY',2);
89 define('ADODB_FORCE_VALUE',3);
90 define('ADODB_FORCE_NULL_AND_ZERO',4);
91 // ********************************************************
94 /**
95 * Constants for returned values from the charMax and textMax methods.
96 * If not specifically defined in the driver, methods return the NOTSET value.
98 define ('ADODB_STRINGMAX_NOTSET', -1);
99 define ('ADODB_STRINGMAX_NOLIMIT',-2);
102 * Defines the the default meta type returned
103 * when ADOdb encounters a type that it is not
104 * defined in the metaTypes.
106 if (!defined('ADODB_DEFAULT_METATYPE'))
107 define ('ADODB_DEFAULT_METATYPE','N');
109 define('ADODB_BAD_RS','<p>Bad $rs in %s. Connection or SQL invalid. Try using $connection->debug=true;</p>');
111 // allow [ ] @ ` " and . in table names
112 define('ADODB_TABLE_REGEX','([]0-9a-z_\:\"\`\.\@\[-]*)');
114 // prefetching used by oracle
115 if (!defined('ADODB_PREFETCH_ROWS')) {
116 define('ADODB_PREFETCH_ROWS',10);
121 * Fetch mode
123 * Set global variable $ADODB_FETCH_MODE to one of these constants or use
124 * the SetFetchMode() method to control how recordset fields are returned
125 * when fetching data.
127 * - NUM: array()
128 * - ASSOC: array('id' => 456, 'name' => 'john')
129 * - BOTH: array(0 => 456, 'id' => 456, 1 => 'john', 'name' => 'john')
130 * - DEFAULT: driver-dependent
132 define('ADODB_FETCH_DEFAULT', 0);
133 define('ADODB_FETCH_NUM', 1);
134 define('ADODB_FETCH_ASSOC', 2);
135 define('ADODB_FETCH_BOTH', 3);
138 * Associative array case constants
140 * By defining the ADODB_ASSOC_CASE constant to one of these values, it is
141 * possible to control the case of field names (associative array's keys)
142 * when operating in ADODB_FETCH_ASSOC fetch mode.
143 * - LOWER: $rs->fields['orderid']
144 * - UPPER: $rs->fields['ORDERID']
145 * - NATIVE: $rs->fields['OrderID'] (or whatever the RDBMS will return)
147 * The default is to use native case-names.
149 * NOTE: This functionality is not implemented everywhere, it currently
150 * works only with: mssql, odbc, oci8 and ibase derived drivers
152 define('ADODB_ASSOC_CASE_LOWER', 0);
153 define('ADODB_ASSOC_CASE_UPPER', 1);
154 define('ADODB_ASSOC_CASE_NATIVE', 2);
157 if (!defined('TIMESTAMP_FIRST_YEAR')) {
158 define('TIMESTAMP_FIRST_YEAR',100);
162 * AutoExecute constants
163 * (moved from adodb-pear.inc.php since they are only used in here)
165 define('DB_AUTOQUERY_INSERT', 1);
166 define('DB_AUTOQUERY_UPDATE', 2);
170 function ADODB_Setup() {
171 GLOBAL
172 $ADODB_vers, // database version
173 $ADODB_COUNTRECS, // count number of records returned - slows down query
174 $ADODB_CACHE_DIR, // directory to cache recordsets
175 $ADODB_FETCH_MODE,
176 $ADODB_CACHE,
177 $ADODB_CACHE_CLASS,
178 $ADODB_FORCE_TYPE,
179 $ADODB_GETONE_EOF,
180 $ADODB_QUOTE_FIELDNAMES;
182 if (empty($ADODB_CACHE_CLASS)) {
183 $ADODB_CACHE_CLASS = 'ADODB_Cache_File' ;
185 $ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
186 $ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
187 $ADODB_GETONE_EOF = null;
189 if (!isset($ADODB_CACHE_DIR)) {
190 $ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
191 } else {
192 // do not accept url based paths, eg. http:/ or ftp:/
193 if (strpos($ADODB_CACHE_DIR,'://') !== false) {
194 die("Illegal path http:// or ftp://");
199 * ADODB version as a string.
201 $ADODB_vers = 'v5.22.2 2022-05-08';
204 * Determines whether recordset->RecordCount() is used.
205 * Set to false for highest performance -- RecordCount() will always return -1 then
206 * for databases that provide "virtual" recordcounts...
208 if (!isset($ADODB_COUNTRECS)) {
209 $ADODB_COUNTRECS = true;
214 //==============================================================================================
215 // CHANGE NOTHING BELOW UNLESS YOU ARE DESIGNING ADODB
216 //==============================================================================================
218 ADODB_Setup();
220 //==============================================================================================
221 // CLASS ADOFieldObject
222 //==============================================================================================
224 * Helper class for FetchFields -- holds info on a column
226 class ADOFieldObject {
227 var $name = '';
228 var $max_length=0;
229 var $type="";
231 // additional fields by dannym... (danny_milo@yahoo.com)
232 var $not_null = false;
233 // actually, this has already been built-in in the postgres, fbsql AND mysql module? ^-^
234 // so we can as well make not_null standard (leaving it at "false" does not harm anyways)
236 var $has_default = false; // this one I have done only in mysql and postgres for now ...
237 // others to come (dannym)
238 var $default_value; // default, if any, and supported. Check has_default first.
244 * Parse date string to prevent injection attack.
246 * @param string $s
248 * @return string
250 function _adodb_safedate($s) {
251 return str_replace(array("'", '\\'), '', $s);
255 * Parse date string to prevent injection attack.
256 * Date string will have one quote at beginning e.g. '3434343'
258 * @param string $s
260 * @return string
262 function _adodb_safedateq($s) {
263 $len = strlen($s);
264 if ($s[0] !== "'") {
265 $s2 = "'".$s[0];
266 } else {
267 $s2 = "'";
269 for($i=1; $i<$len; $i++) {
270 $ch = $s[$i];
271 if ($ch === '\\') {
272 $s2 .= "'";
273 break;
274 } elseif ($ch === "'") {
275 $s2 .= $ch;
276 break;
279 $s2 .= $ch;
282 return strlen($s2) == 0 ? 'null' : $s2;
286 * For transaction handling.
288 * @param $dbms
289 * @param $fn
290 * @param $errno
291 * @param $errmsg
292 * @param $p1
293 * @param $p2
294 * @param $thisConnection
296 function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection) {
297 //print "Errorno ($fn errno=$errno m=$errmsg) ";
298 $thisConnection->_transOK = false;
299 if ($thisConnection->_oldRaiseFn) {
300 $errfn = $thisConnection->_oldRaiseFn;
301 $errfn($dbms, $fn, $errno, $errmsg, $p1, $p2,$thisConnection);
306 * Class ADODB_Cache_File
308 class ADODB_Cache_File {
310 var $createdir = true; // requires creation of temp dirs
312 function __construct() {
313 global $ADODB_INCLUDED_CSV;
314 if (empty($ADODB_INCLUDED_CSV)) {
315 include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
320 * Write serialised RecordSet to cache item/file.
322 * @param $filename
323 * @param $contents
324 * @param $debug
325 * @param $secs2cache
327 * @return bool|int
329 function writecache($filename, $contents, $debug, $secs2cache) {
330 return adodb_write_file($filename, $contents,$debug);
334 * load serialised RecordSet and unserialise it
336 * @param $filename
337 * @param $err
338 * @param $secs2cache
339 * @param $rsClass
341 * @return ADORecordSet
343 function &readcache($filename, &$err, $secs2cache, $rsClass) {
344 $rs = csv2rs($filename,$err,$secs2cache,$rsClass);
345 return $rs;
349 * Flush all items in cache.
351 * @param bool $debug
353 * @return bool|void
355 function flushall($debug=false) {
356 global $ADODB_CACHE_DIR;
358 $rez = false;
360 if (strlen($ADODB_CACHE_DIR) > 1) {
361 $rez = $this->_dirFlush($ADODB_CACHE_DIR);
362 if ($debug) {
363 ADOConnection::outp( "flushall: $ADODB_CACHE_DIR<br><pre>\n". $rez."</pre>");
366 return $rez;
370 * Flush one file in cache.
372 * @param string $f
373 * @param bool $debug
375 function flushcache($f, $debug=false) {
376 if (!@unlink($f)) {
377 if ($debug) {
378 ADOConnection::outp( "flushcache: failed for $f");
384 * @param string $hash
386 * @return string
388 function getdirname($hash) {
389 global $ADODB_CACHE_DIR;
390 if (!isset($this->notSafeMode)) {
391 $this->notSafeMode = !ini_get('safe_mode');
393 return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
397 * Create temp directories.
399 * @param string $hash
400 * @param bool $debug
402 * @return string
404 function createdir($hash, $debug) {
405 global $ADODB_CACHE_PERMS;
407 $dir = $this->getdirname($hash);
408 if ($this->notSafeMode && !file_exists($dir)) {
409 $oldu = umask(0);
410 if (!@mkdir($dir, empty($ADODB_CACHE_PERMS) ? 0771 : $ADODB_CACHE_PERMS)) {
411 if(!is_dir($dir) && $debug) {
412 ADOConnection::outp("Cannot create $dir");
415 umask($oldu);
418 return $dir;
422 * Private function to erase all of the files and subdirectories in a directory.
424 * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
425 * Note: $kill_top_level is used internally in the function to flush subdirectories.
427 function _dirFlush($dir, $kill_top_level = false) {
428 if(!$dh = @opendir($dir)) return;
430 while (($obj = readdir($dh))) {
431 if($obj=='.' || $obj=='..') continue;
432 $f = $dir.'/'.$obj;
434 if (strpos($obj,'.cache')) {
435 @unlink($f);
437 if (is_dir($f)) {
438 $this->_dirFlush($f, true);
441 if ($kill_top_level === true) {
442 @rmdir($dir);
444 return true;
448 //==============================================================================================
449 // CLASS ADOConnection
450 //==============================================================================================
453 * Connection object. For connecting to databases, and executing queries.
455 abstract class ADOConnection {
457 // PUBLIC VARS
459 var $dataProvider = 'native';
460 var $databaseType = ''; /// RDBMS currently in use, eg. odbc, mysql, mssql
461 var $database = ''; /// Name of database to be used.
464 * @var string If the driver is PDO, then the dsnType is e.g. sqlsrv, otherwise empty
466 public $dsnType = '';
468 var $host = ''; /// The hostname of the database server
469 var $port = ''; /// The port of the database server
470 var $user = ''; /// The username which is used to connect to the database server.
471 var $password = ''; /// Password for the username. For security, we no longer store it.
472 var $debug = false; /// if set to true will output sql statements
473 var $maxblobsize = 262144; /// maximum size of blobs or large text fields (262144 = 256K)-- some db's die otherwise like foxpro
474 var $concat_operator = '+'; /// default concat operator -- change to || for Oracle/Interbase
475 var $substr = 'substr'; /// substring operator
476 var $length = 'length'; /// string length ofperator
477 var $random = 'rand()'; /// random function
478 var $upperCase = 'upper'; /// uppercase function
479 var $fmtDate = "'Y-m-d'"; /// used by DBDate() as the default date format used by the database
480 var $fmtTimeStamp = "'Y-m-d, h:i:s A'"; /// used by DBTimeStamp as the default timestamp fmt.
481 var $true = '1'; /// string that represents TRUE for a database
482 var $false = '0'; /// string that represents FALSE for a database
483 var $replaceQuote = "\\'"; /// string to use to replace quotes
484 var $nameQuote = '"'; /// string to use to quote identifiers and names
485 var $leftBracket = '['; /// left square bracked for t-sql styled column names
486 var $rightBracket = ']'; /// right square bracked for t-sql styled column names
487 var $charSet=false; /// character set to use - only for interbase, postgres and oci8
488 var $metaDatabasesSQL = '';
489 var $metaTablesSQL = '';
490 var $uniqueOrderBy = false; /// All order by columns have to be unique
491 var $emptyDate = '&nbsp;';
492 var $emptyTimeStamp = '&nbsp;';
493 var $lastInsID = false;
494 //--
495 var $hasInsertID = false; /// supports autoincrement ID?
496 var $hasAffectedRows = false; /// supports affected rows for update/delete?
497 var $hasTop = false; /// support mssql/access SELECT TOP 10 * FROM TABLE
498 var $hasLimit = false; /// support pgsql/mysql SELECT * FROM TABLE LIMIT 10
499 var $readOnly = false; /// this is a readonly database - used by phpLens
500 var $hasMoveFirst = false; /// has ability to run MoveFirst(), scrolling backwards
501 var $hasGenID = false; /// can generate sequences using GenID();
502 var $hasTransactions = true; /// has transactions
503 //--
504 var $genID = 0; /// sequence id used by GenID();
506 /** @var bool|callable Error function to call */
507 var $raiseErrorFn = false;
509 var $isoDates = false; /// accepts dates in ISO format
510 var $cacheSecs = 3600; /// cache for 1 hour
512 /*****************************************
513 * memcached server options
514 ******************************************/
517 * Use memCache library instead of caching in files.
518 * @var bool $memCache
520 public $memCache = false;
523 * The memcache server(s) to connect to. Can be defined as:
524 * - a single host name/ip address
525 * - a list of hosts/ip addresses
526 * - an array of server connection data (weighted server groups).
527 * @link https://adodb.org/dokuwiki/doku.php?id=v5:userguide:memcached
528 * @var string|array $memCacheHost
530 public $memCacheHost;
533 * Default port number.
534 * The default port can be overridden if memcache server connection data
535 * is provided as an array {@see $memCacheHost}.
536 * @var int $memCachePort
538 public $memCachePort = 11211;
541 * Enable compression of stored items.
542 * @var bool $memCacheCompress
544 public $memCacheCompress = false;
547 * An array of memcached options.
548 * Only used with memcached; memcache ignores this setting.
549 * @link https://www.php.net/manual/en/memcached.constants.php
550 * @var array $memCacheOptions
552 public $memCacheOptions = array();
554 var $sysDate = false; /// name of function that returns the current date
555 var $sysTimeStamp = false; /// name of function that returns the current timestamp
556 var $sysUTimeStamp = false; // name of function that returns the current timestamp accurate to the microsecond or nearest fraction
557 var $arrayClass = 'ADORecordSet_array'; /// name of class used to generate array recordsets, which are pre-downloaded recordsets
559 var $noNullStrings = false; /// oracle specific stuff - if true ensures that '' is converted to ' '
560 var $numCacheHits = 0;
561 var $numCacheMisses = 0;
562 var $pageExecuteCountRows = true;
563 var $uniqueSort = false; /// indicates that all fields in order by must be unique
564 var $leftOuter = false; /// operator to use for left outer join in WHERE clause
565 var $rightOuter = false; /// operator to use for right outer join in WHERE clause
566 var $ansiOuter = false; /// whether ansi outer join syntax supported
567 var $autoRollback = false; // autoRollback on PConnect().
568 var $poorAffectedRows = false; // affectedRows not working or unreliable
570 /** @var bool|callable Execute function to call */
571 var $fnExecute = false;
573 /** @var bool|callable Cache execution function to call */
574 var $fnCacheExecute = false;
576 var $blobEncodeType = false; // false=not required, 'I'=encode to integer, 'C'=encode to char
577 var $rsPrefix = "ADORecordSet_";
579 var $autoCommit = true; /// do not modify this yourself - actually private
580 var $transOff = 0; /// temporarily disable transactions
581 var $transCnt = 0; /// count of nested transactions
583 var $fetchMode=false;
585 var $null2null = 'null'; // in autoexecute/getinsertsql/getupdatesql, this value will be converted to a null
586 var $bulkBind = false; // enable 2D Execute array
588 // PRIVATE VARS
590 var $_oldRaiseFn = false;
591 var $_transOK = null;
592 /** @var resource Identifier for the native database connection */
593 var $_connectionID = false;
594 var $_errorMsg = false; /// A variable which was used to keep the returned last error message. The value will
595 /// then returned by the errorMsg() function
596 var $_errorCode = false; /// Last error code, not guaranteed to be used - only by oci8
597 var $_queryID = false; /// This variable keeps the last created result link identifier
599 var $_isPersistentConnection = false; /// A boolean variable to state whether its a persistent connection or normal connection. */
600 var $_bindInputArray = false; /// set to true if ADOConnection.Execute() permits binding of array parameters.
601 var $_evalAll = false;
602 var $_affected = false;
603 var $_logsql = false;
604 var $_transmode = ''; // transaction mode
607 * Additional parameters that may be passed to drivers in the connect string.
609 * Data is stored as an array of arrays and not a simple associative array,
610 * because some drivers (e.g. mysql) allow multiple parameters with the same
611 * key to be set.
612 * @link https://github.com/ADOdb/ADOdb/issues/187
614 * @see setConnectionParameter()
616 * @var array $connectionParameters Set of ParameterName => Value pairs
618 protected $connectionParameters = array();
621 * A simple associative array of user-defined custom actual/meta types
623 public $customActualTypes = array();
626 * An array of user-defined custom meta/actual types.
627 * $this->customMetaTypes[$meta] = array(
628 * 'actual'=>'',
629 * 'dictionary'=>'',
630 * 'handler'=>'',
631 * 'callback'=>''
632 * );
634 public $customMetaTypes = array();
638 * Default Constructor.
639 * We define it even though it does not actually do anything. This avoids
640 * getting a PHP Fatal error: Cannot call constructor if a subclass tries
641 * to call its parent constructor.
643 public function __construct()
648 * Adds a parameter to the connection string.
650 * Parameters must be added before the connection is established;
651 * they are then passed on to the connect statement, which will.
652 * process them if the driver supports this feature.
654 * Example usage:
655 * - mssqlnative: setConnectionParameter('CharacterSet','UTF-8');
656 * - mysqli: setConnectionParameter(MYSQLI_SET_CHARSET_NAME,'utf8mb4');
658 * If used in a portable environment, parameters set in this manner should
659 * be predicated on the database provider, as unexpected results may occur
660 * if applied to the wrong database.
662 * @param string $parameter The name of the parameter to set
663 * @param string $value The value of the parameter
665 * @return bool True if success, false otherwise (e.g. parameter is not valid)
667 public function setConnectionParameter($parameter, $value) {
668 $this->connectionParameters[] = array($parameter=>$value);
669 return true;
673 * ADOdb version.
675 * @return string
677 static function Version() {
678 global $ADODB_vers;
680 // Semantic Version number matching regex
681 $regex = '^[vV]?(\d+\.\d+\.\d+' // Version number (X.Y.Z) with optional 'V'
682 . '(?:-(?:' // Optional preprod version: a '-'
683 . 'dev|' // followed by 'dev'
684 . '(?:(?:alpha|beta|rc)(?:\.\d+))' // or a preprod suffix and version number
685 . '))?)(?:\s|$)'; // Whitespace or end of string
687 if (!preg_match("/$regex/", $ADODB_vers, $matches)) {
688 // This should normally not happen... Return whatever is between the start
689 // of the string and the first whitespace (or the end of the string).
690 self::outp("Invalid version number: '$ADODB_vers'", 'Version');
691 $regex = '^[vV]?(.*?)(?:\s|$)';
692 preg_match("/$regex/", $ADODB_vers, $matches);
694 return $matches[1];
698 * Set a custom meta type with a corresponding actual
700 * @param string $metaType The Custom ADOdb metatype
701 * @param string $dictionaryType The database dictionary type
702 * @param string $actualType The database actual type
703 * @param bool $handleAsType handle like an existing Metatype
704 * @param mixed $callBack A pre-processing function
706 * @return bool success if the actual exists
708 final public function setCustomMetaType(
709 $metaType,
710 $dictionaryType,
711 $actualType,
712 $handleAsType=false,
713 $callback=false){
715 $this->customMetaTypes[strtoupper($metaType)] = array(
716 'actual'=>$actualType,
717 'dictionary'=>strtoupper($dictionaryType),
718 'handler'=>$handleAsType,
719 'callback'=>$callback
723 * Create a reverse lookup for the actualType
725 $this->customActualTypes[$actualType] = $metaType;
727 return true;
731 * Get a list of custom meta types.
733 * @return string[]
735 final public function getCustomMetaTypes()
737 return $this->customMetaTypes;
742 * Get server version info.
744 * @return string[] Array with 2 string elements: version and description
746 function ServerInfo() {
747 return array('description' => '', 'version' => '');
751 * Return true if connected to the database.
753 * @return bool
755 function IsConnected() {
756 return !empty($this->_connectionID);
760 * Find version string.
762 * @param string $str
764 * @return string
766 function _findvers($str) {
767 if (preg_match('/([0-9]+\.([0-9\.])+)/',$str, $arr)) {
768 return $arr[1];
769 } else {
770 return '';
775 * All error messages go through this bottleneck function.
777 * You can define your own handler by defining the function name in ADODB_OUTP.
779 * @param string $msg Message to print
780 * @param bool $newline True to add a newline after printing $msg
782 static function outp($msg,$newline=true) {
783 global $ADODB_FLUSH,$ADODB_OUTP;
785 if (defined('ADODB_OUTP')) {
786 $fn = ADODB_OUTP;
787 $fn($msg,$newline);
788 return;
789 } else if (isset($ADODB_OUTP)) {
790 call_user_func($ADODB_OUTP,$msg,$newline);
791 return;
794 if ($newline) {
795 $msg .= "<br>\n";
798 if (isset($_SERVER['HTTP_USER_AGENT']) || !$newline) {
799 echo $msg;
800 } else {
801 echo strip_tags($msg);
805 if (!empty($ADODB_FLUSH) && ob_get_length() !== false) {
806 flush(); // do not flush if output buffering enabled - useless - thx to Jesse Mullan
812 * Return the database server's current date and time.
813 * @return int|false
815 function Time() {
816 $rs = $this->_Execute("select $this->sysTimeStamp");
817 if ($rs && !$rs->EOF) {
818 return $this->UnixTimeStamp(reset($rs->fields));
821 return false;
825 * Parses the hostname to extract the port.
826 * Overwrites $this->host and $this->port, only if a port is specified.
827 * The Hostname can be fully or partially qualified,
828 * ie: "db.mydomain.com:5432" or "ldaps://ldap.mydomain.com:636"
829 * Any specified scheme such as ldap:// or ldaps:// is maintained.
831 protected function parseHostNameAndPort() {
832 $parsed_url = parse_url($this->host);
833 if (is_array($parsed_url) && isset($parsed_url['host']) && isset($parsed_url['port'])) {
834 if ( isset($parsed_url['scheme']) ) {
835 // If scheme is specified (ie: ldap:// or ldaps://, make sure we retain that.
836 $this->host = $parsed_url['scheme'] . "://" . $parsed_url['host'];
837 } else {
838 $this->host = $parsed_url['host'];
840 $this->port = $parsed_url['port'];
845 * Connect to database.
847 * @param string $argHostname Host to connect to
848 * @param string $argUsername Userid to login
849 * @param string $argPassword Associated password
850 * @param string $argDatabaseName Database name
851 * @param bool $forceNew Force new connection
853 * @return bool
855 function Connect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "", $forceNew = false) {
856 if ($argHostname != "") {
857 $this->host = $argHostname;
859 // Overwrites $this->host and $this->port if a port is specified.
860 $this->parseHostNameAndPort();
862 if ($argUsername != "") {
863 $this->user = $argUsername;
865 if ($argPassword != "") {
866 $this->password = 'not stored'; // not stored for security reasons
868 if ($argDatabaseName != "") {
869 $this->database = $argDatabaseName;
872 $this->_isPersistentConnection = false;
874 if ($forceNew) {
875 if ($rez=$this->_nconnect($this->host, $this->user, $argPassword, $this->database)) {
876 return true;
878 } else {
879 if ($rez=$this->_connect($this->host, $this->user, $argPassword, $this->database)) {
880 return true;
883 if (isset($rez)) {
884 $err = $this->ErrorMsg();
885 $errno = $this->ErrorNo();
886 if (empty($err)) {
887 $err = "Connection error to server '$argHostname' with user '$argUsername'";
889 } else {
890 $err = "Missing extension for ".$this->dataProvider;
891 $errno = 0;
893 if ($fn = $this->raiseErrorFn) {
894 $fn($this->databaseType, 'CONNECT', $errno, $err, $this->host, $this->database, $this);
897 $this->_connectionID = false;
898 if ($this->debug) {
899 ADOConnection::outp( $this->host.': '.$err);
901 return false;
905 * Always force a new connection to database.
907 * @param string $argHostname Host to connect to
908 * @param string $argUsername Userid to login
909 * @param string $argPassword Associated password
910 * @param string $argDatabaseName Database name
912 * @return bool
914 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) {
915 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName);
919 * Always force a new connection to database.
921 * Currently this only works with Oracle.
923 * @param string $argHostname Host to connect to
924 * @param string $argUsername Userid to login
925 * @param string $argPassword Associated password
926 * @param string $argDatabaseName Database name
928 * @return bool
930 function NConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
931 return $this->Connect($argHostname, $argUsername, $argPassword, $argDatabaseName, true);
935 * Establish persistent connection to database.
937 * @param string $argHostname Host to connect to
938 * @param string $argUsername Userid to login
939 * @param string $argPassword Associated password
940 * @param string $argDatabaseName Database name
942 * @return bool
944 function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "") {
946 if (defined('ADODB_NEVER_PERSIST')) {
947 return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
950 if ($argHostname != "") {
951 $this->host = $argHostname;
953 // Overwrites $this->host and $this->port if a port is specified.
954 $this->parseHostNameAndPort();
956 if ($argUsername != "") {
957 $this->user = $argUsername;
959 if ($argPassword != "") {
960 $this->password = 'not stored';
962 if ($argDatabaseName != "") {
963 $this->database = $argDatabaseName;
966 $this->_isPersistentConnection = true;
968 if ($rez = $this->_pconnect($this->host, $this->user, $argPassword, $this->database)) {
969 return true;
971 if (isset($rez)) {
972 $err = $this->ErrorMsg();
973 if (empty($err)) {
974 $err = "Connection error to server '$argHostname' with user '$argUsername'";
976 $ret = false;
977 } else {
978 $err = "Missing extension for ".$this->dataProvider;
979 $ret = false;
981 if ($fn = $this->raiseErrorFn) {
982 $fn($this->databaseType,'PCONNECT',$this->ErrorNo(),$err,$this->host,$this->database,$this);
985 $this->_connectionID = false;
986 if ($this->debug) {
987 ADOConnection::outp( $this->host.': '.$err);
989 return $ret;
993 * Throw an exception if the handler is defined or prints the message if not.
994 * @param string $msg Message
995 * @param string $src the name of the calling function (in uppercase)
996 * @param string $sql Optional offending SQL statement
998 function outp_throw($msg, $src='WARN', $sql='') {
999 if (defined('ADODB_ERROR_HANDLER') && ADODB_ERROR_HANDLER == 'adodb_throw') {
1000 adodb_throw($this->databaseType,$src,-9999,$msg,$sql,false,$this);
1001 return;
1003 ADOConnection::outp($msg);
1007 * Create cache class.
1009 * Code is backwards-compatible with old memcache implementation.
1011 function _CreateCache() {
1012 global $ADODB_CACHE, $ADODB_CACHE_CLASS;
1014 if ($this->memCache) {
1015 global $ADODB_INCLUDED_MEMCACHE;
1017 if (empty($ADODB_INCLUDED_MEMCACHE)) {
1018 include_once(ADODB_DIR.'/adodb-memcache.lib.inc.php');
1020 $ADODB_CACHE = new ADODB_Cache_MemCache($this);
1021 } else {
1022 $ADODB_CACHE = new $ADODB_CACHE_CLASS($this);
1027 * Format date column in sql string.
1029 * See https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:sqldate
1030 * for documentation on supported formats.
1032 * @param string $fmt Format string
1033 * @param string $col Date column; use system date if not specified.
1035 * @return string
1037 function SQLDate($fmt, $col = '') {
1038 if (!$col) {
1039 $col = $this->sysDate;
1041 return $col; // child class implement
1045 * Prepare an SQL statement and return the statement resource.
1047 * For databases that do not support prepared statements, we return the
1048 * provided SQL statement as-is, to ensure compatibility:
1050 * $stmt = $db->prepare("insert into table (id, name) values (?,?)");
1051 * $db->execute($stmt, array(1,'Jill')) or die('insert failed');
1052 * $db->execute($stmt, array(2,'Joe')) or die('insert failed');
1054 * @param string $sql SQL to send to database
1056 * @return mixed|false The prepared statement, or the original sql if the
1057 * database does not support prepare.
1059 function Prepare($sql) {
1060 return $sql;
1064 * Releases a previously prepared statement.
1066 * @param mixed $stmt Statement resource, as returned by {@see prepare()}
1068 * @return bool
1070 function releaseStatement(&$stmt) {
1071 return true;
1075 * Prepare a Stored Procedure and return the statement resource.
1077 * Some databases, eg. mssql require a different function for preparing
1078 * stored procedures. So we cannot use Prepare().
1080 * For databases that do not support this, we return the $sql.
1082 * @param string $sql SQL to send to database
1083 * @param bool $param
1085 * @return mixed|false The prepared statement, or the original sql if the
1086 * database does not support prepare.
1088 function PrepareSP($sql,$param=true) {
1089 return $this->Prepare($sql,$param);
1093 * PEAR DB Compat - alias for qStr.
1094 * @param $s
1095 * @return string
1097 function Quote($s) {
1098 return $this->qstr($s);
1102 * Quotes a string so that all strings are escaped.
1103 * Wrapper for qstr with magic_quotes = false.
1105 * @param string &$s
1107 function q(&$s) {
1108 //if (!empty($this->qNull && $s == 'null') {
1109 // return $s;
1111 $s = $this->qstr($s);
1115 * PEAR DB Compat - do not use internally.
1116 * @return int
1118 function ErrorNative() {
1119 return $this->ErrorNo();
1124 * PEAR DB Compat - do not use internally.
1125 * @param string $seq_name
1126 * @return int
1128 function nextId($seq_name) {
1129 return $this->GenID($seq_name);
1133 * Lock a row.
1134 * Will escalate and lock the table if row locking is not supported.
1135 * Will normally free the lock at the end of the transaction.
1137 * @param string $table name of table to lock
1138 * @param string $where where clause to use, eg: "WHERE row=12". If left empty, will escalate to table lock
1139 * @param string $col
1141 * @return bool
1143 function RowLock($table,$where,$col='1 as adodbignore') {
1144 return false;
1148 * @param string $table
1149 * @return true
1151 function CommitLock($table) {
1152 return $this->CommitTrans();
1156 * @param string $table
1157 * @return true
1159 function RollbackLock($table) {
1160 return $this->RollbackTrans();
1164 * PEAR DB Compat - do not use internally.
1166 * The fetch modes for NUMERIC and ASSOC for PEAR DB and ADODB are identical
1167 * for easy porting :-)
1169 * @param int $mode The fetchmode ADODB_FETCH_ASSOC or ADODB_FETCH_NUM
1171 * @return int Previous fetch mode
1173 function SetFetchMode($mode) {
1174 $old = $this->fetchMode;
1175 $this->fetchMode = $mode;
1177 if ($old === false) {
1178 global $ADODB_FETCH_MODE;
1179 return $ADODB_FETCH_MODE;
1181 return $old;
1185 * PEAR DB Compat - do not use internally.
1187 * @param string $sql
1188 * @param array|bool $inputarr
1190 * @return ADORecordSet|bool
1192 function Query($sql, $inputarr=false) {
1193 $rs = $this->Execute($sql, $inputarr);
1194 if (!$rs && defined('ADODB_PEAR')) {
1195 return ADODB_PEAR_Error();
1197 return $rs;
1201 * PEAR DB Compat - do not use internally
1203 function LimitQuery($sql, $offset, $count, $params=false) {
1204 $rs = $this->SelectLimit($sql, $count, $offset, $params);
1205 if (!$rs && defined('ADODB_PEAR')) {
1206 return ADODB_PEAR_Error();
1208 return $rs;
1213 * PEAR DB Compat - do not use internally
1215 function Disconnect() {
1216 return $this->Close();
1220 * Returns a placeholder for query parameters.
1222 * e.g. $DB->Param('a') will return
1223 * - '?' for most databases
1224 * - ':a' for Oracle
1225 * - '$1', '$2', etc. for PostgreSQL
1227 * @param mixed $name parameter's name.
1228 * For databases that require positioned params (e.g. PostgreSQL),
1229 * a "falsy" value can be used to force resetting the placeholder
1230 * count; using boolean 'false' will reset it without actually
1231 * returning a placeholder. ADOdb will also automatically reset
1232 * the count when executing a query.
1233 * @param string $type (unused)
1234 * @return string query parameter placeholder
1236 function Param($name,$type='C') {
1237 return '?';
1241 * Self-documenting version of Parameter().
1243 * @param $stmt
1244 * @param &$var
1245 * @param $name
1246 * @param int $maxLen
1247 * @param bool $type
1249 * @return bool
1251 function InParameter(&$stmt, &$var, $name, $maxLen=4000, $type=false) {
1252 return $this->Parameter($stmt,$var,$name,false,$maxLen,$type);
1256 * Self-documenting version of Parameter().
1258 * @param $stmt
1259 * @param $var
1260 * @param $name
1261 * @param int $maxLen
1262 * @param bool $type
1264 * @return bool
1266 function OutParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false) {
1267 return $this->Parameter($stmt,$var,$name,true,$maxLen,$type);
1273 * Usage in oracle
1274 * $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
1275 * $db->Parameter($stmt,$id,'myid');
1276 * $db->Parameter($stmt,$group,'group',64);
1277 * $db->Execute();
1279 * @param mixed &$stmt Statement returned by Prepare() or PrepareSP().
1280 * @param mixed &$var PHP variable to bind to
1281 * @param string $name Name of stored procedure variable name to bind to.
1282 * @param int|bool $isOutput Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
1283 * @param int $maxLen Holds an maximum length of the variable.
1284 * @param mixed $type The data type of $var. Legal values depend on driver.
1286 * @return bool
1288 function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false) {
1289 return false;
1293 function IgnoreErrors($saveErrs=false) {
1294 if (!$saveErrs) {
1295 $saveErrs = array($this->raiseErrorFn,$this->_transOK);
1296 $this->raiseErrorFn = false;
1297 return $saveErrs;
1298 } else {
1299 $this->raiseErrorFn = $saveErrs[0];
1300 $this->_transOK = $saveErrs[1];
1305 * Improved method of initiating a transaction. Used together with CompleteTrans().
1306 * Advantages include:
1308 * a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
1309 * Only the outermost block is treated as a transaction.<br>
1310 * b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
1311 * c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
1312 * are disabled, making it backward compatible.
1314 function StartTrans($errfn = 'ADODB_TransMonitor') {
1315 if ($this->transOff > 0) {
1316 $this->transOff += 1;
1317 return true;
1320 $this->_oldRaiseFn = $this->raiseErrorFn;
1321 $this->raiseErrorFn = $errfn;
1322 $this->_transOK = true;
1324 if ($this->debug && $this->transCnt > 0) {
1325 ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
1327 $ok = $this->BeginTrans();
1328 $this->transOff = 1;
1329 return $ok;
1334 * Complete a transation.
1336 * Used together with StartTrans() to end a transaction. Monitors connection
1337 * for sql errors, and will commit or rollback as appropriate.
1339 * @param bool autoComplete if true, monitor sql errors and commit and
1340 * rollback as appropriate, and if set to false
1341 * force rollback even if no SQL error detected.
1342 * @returns true on commit, false on rollback.
1344 function CompleteTrans($autoComplete = true) {
1345 if ($this->transOff > 1) {
1346 $this->transOff -= 1;
1347 return true;
1349 $this->raiseErrorFn = $this->_oldRaiseFn;
1351 $this->transOff = 0;
1352 if ($this->_transOK && $autoComplete) {
1353 if (!$this->CommitTrans()) {
1354 $this->_transOK = false;
1355 if ($this->debug) {
1356 ADOConnection::outp("Smart Commit failed");
1358 } else {
1359 if ($this->debug) {
1360 ADOConnection::outp("Smart Commit occurred");
1363 } else {
1364 $this->_transOK = false;
1365 $this->RollbackTrans();
1366 if ($this->debug) {
1367 ADOConnection::outp("Smart Rollback occurred");
1371 return $this->_transOK;
1375 * At the end of a StartTrans/CompleteTrans block, perform a rollback.
1377 function FailTrans() {
1378 if ($this->debug)
1379 if ($this->transOff == 0) {
1380 ADOConnection::outp("FailTrans outside StartTrans/CompleteTrans");
1381 } else {
1382 ADOConnection::outp("FailTrans was called");
1383 adodb_backtrace();
1385 $this->_transOK = false;
1389 * Check if transaction has failed, only for Smart Transactions.
1391 function HasFailedTrans() {
1392 if ($this->transOff > 0) {
1393 return $this->_transOK == false;
1395 return false;
1399 * Execute SQL
1401 * @param string $sql SQL statement to execute, or possibly an array
1402 * holding prepared statement ($sql[0] will hold sql text)
1403 * @param array|bool $inputarr holds the input data to bind to.
1404 * Null elements will be set to null.
1406 * @return ADORecordSet|bool
1408 public function Execute($sql, $inputarr = false) {
1409 if ($this->fnExecute) {
1410 $fn = $this->fnExecute;
1411 $ret = $fn($this,$sql,$inputarr);
1412 if (isset($ret)) {
1413 return $ret;
1416 if ($inputarr !== false) {
1417 if (!is_array($inputarr)) {
1418 $inputarr = array($inputarr);
1421 $element0 = reset($inputarr);
1422 # is_object check because oci8 descriptors can be passed in
1423 $array_2d = $this->bulkBind && is_array($element0) && !is_object(reset($element0));
1425 //remove extra memory copy of input -mikefedyk
1426 unset($element0);
1428 if (!is_array($sql) && !$this->_bindInputArray) {
1429 // @TODO this would consider a '?' within a string as a parameter...
1430 $sqlarr = explode('?',$sql);
1431 $nparams = sizeof($sqlarr)-1;
1433 if (!$array_2d) {
1434 // When not Bind Bulk - convert to array of arguments list
1435 $inputarr = array($inputarr);
1436 } else {
1437 // Bulk bind - Make sure all list of params have the same number of elements
1438 $countElements = array_map('count', $inputarr);
1439 if (1 != count(array_unique($countElements))) {
1440 $this->outp_throw(
1441 "[bulk execute] Input array has different number of params [" . print_r($countElements, true) . "].",
1442 'Execute'
1444 return false;
1446 unset($countElements);
1448 // Make sure the number of parameters provided in the input
1449 // array matches what the query expects
1450 $element0 = reset($inputarr);
1451 if ($nparams != count($element0)) {
1452 $this->outp_throw(
1453 "Input array has " . count($element0) .
1454 " params, does not match query: '" . htmlspecialchars($sql) . "'",
1455 'Execute'
1457 return false;
1460 // clean memory
1461 unset($element0);
1463 foreach($inputarr as $arr) {
1464 $sql = ''; $i = 0;
1465 foreach ($arr as $v) {
1466 $sql .= $sqlarr[$i];
1467 // from Ron Baldwin <ron.baldwin#sourceprose.com>
1468 // Only quote string types
1469 $typ = gettype($v);
1470 if ($typ == 'string') {
1471 //New memory copy of input created here -mikefedyk
1472 $sql .= $this->qstr($v);
1473 } else if ($typ == 'double') {
1474 $sql .= str_replace(',','.',$v); // locales fix so 1.1 does not get converted to 1,1
1475 } else if ($typ == 'boolean') {
1476 $sql .= $v ? $this->true : $this->false;
1477 } else if ($typ == 'object') {
1478 if (method_exists($v, '__toString')) {
1479 $sql .= $this->qstr($v->__toString());
1480 } else {
1481 $sql .= $this->qstr((string) $v);
1483 } else if ($v === null) {
1484 $sql .= 'NULL';
1485 } else {
1486 $sql .= $v;
1488 $i += 1;
1490 if ($i == $nparams) {
1491 break;
1493 } // while
1494 if (isset($sqlarr[$i])) {
1495 $sql .= $sqlarr[$i];
1496 if ($i+1 != sizeof($sqlarr)) {
1497 $this->outp_throw( "Input Array does not match ?: ".htmlspecialchars($sql),'Execute');
1499 } else if ($i != sizeof($sqlarr)) {
1500 $this->outp_throw( "Input array does not match ?: ".htmlspecialchars($sql),'Execute');
1503 $ret = $this->_Execute($sql);
1504 if (!$ret) {
1505 return $ret;
1508 } else {
1509 if ($array_2d) {
1510 if (is_string($sql)) {
1511 $stmt = $this->Prepare($sql);
1512 } else {
1513 $stmt = $sql;
1516 foreach($inputarr as $arr) {
1517 $ret = $this->_Execute($stmt,$arr);
1518 if (!$ret) {
1519 return $ret;
1522 } else {
1523 $ret = $this->_Execute($sql,$inputarr);
1526 } else {
1527 $ret = $this->_Execute($sql,false);
1530 return $ret;
1533 function _Execute($sql,$inputarr=false) {
1534 // ExecuteCursor() may send non-string queries (such as arrays),
1535 // so we need to ignore those.
1536 if( is_string($sql) ) {
1537 // Strips keyword used to help generate SELECT COUNT(*) queries
1538 // from SQL if it exists.
1539 // TODO: obsoleted by #715 - kept for backwards-compatibility
1540 $sql = str_replace( '_ADODB_COUNT', '', $sql );
1543 if ($this->debug) {
1544 global $ADODB_INCLUDED_LIB;
1545 if (empty($ADODB_INCLUDED_LIB)) {
1546 include_once(ADODB_DIR.'/adodb-lib.inc.php');
1548 $this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
1549 } else {
1550 $this->_queryID = @$this->_query($sql,$inputarr);
1553 // ************************
1554 // OK, query executed
1555 // ************************
1557 // error handling if query fails
1558 if ($this->_queryID === false) {
1559 if ($this->debug == 99) {
1560 adodb_backtrace(true,5);
1562 $fn = $this->raiseErrorFn;
1563 if ($fn) {
1564 $fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
1566 return false;
1569 // return simplified recordset for inserts/updates/deletes with lower overhead
1570 if ($this->_queryID === true) {
1571 $rsclass = $this->rsPrefix.'empty';
1572 $rs = (class_exists($rsclass)) ? new $rsclass(): new ADORecordSet_empty();
1574 return $rs;
1577 if ($this->dataProvider == 'pdo' && $this->databaseType != 'pdo') {
1578 // PDO uses a slightly different naming convention for the
1579 // recordset class if the database type is changed, so we must
1580 // treat it specifically. The mysql driver leaves the
1581 // databaseType as pdo
1582 $rsclass = $this->rsPrefix . 'pdo_' . $this->databaseType;
1583 } else {
1584 $rsclass = $this->rsPrefix . $this->databaseType;
1587 // return real recordset from select statement
1588 $rs = new $rsclass($this->_queryID,$this->fetchMode);
1589 $rs->connection = $this; // Pablo suggestion
1590 $rs->Init();
1591 if (is_array($sql)) {
1592 $rs->sql = $sql[0];
1593 } else {
1594 $rs->sql = $sql;
1596 if ($rs->_numOfRows <= 0) {
1597 global $ADODB_COUNTRECS;
1598 if ($ADODB_COUNTRECS) {
1599 if (!$rs->EOF) {
1600 $rs = $this->_rs2rs($rs,-1,-1,!is_array($sql));
1601 $rs->_queryID = $this->_queryID;
1602 } else
1603 $rs->_numOfRows = 0;
1606 return $rs;
1609 function CreateSequence($seqname='adodbseq',$startID=1) {
1610 if (empty($this->_genSeqSQL)) {
1611 return false;
1613 return $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1616 function DropSequence($seqname='adodbseq') {
1617 if (empty($this->_dropSeqSQL)) {
1618 return false;
1620 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
1624 * Generates a sequence id and stores it in $this->genID.
1626 * GenID is only available if $this->hasGenID = true;
1628 * @param string $seqname Name of sequence to use
1629 * @param int $startID If sequence does not exist, start at this ID
1631 * @return int Sequence id, 0 if not supported
1633 function GenID($seqname='adodbseq',$startID=1) {
1634 if (!$this->hasGenID) {
1635 return 0; // formerly returns false pre 1.60
1638 $getnext = sprintf($this->_genIDSQL,$seqname);
1640 $holdtransOK = $this->_transOK;
1642 $save_handler = $this->raiseErrorFn;
1643 $this->raiseErrorFn = '';
1644 @($rs = $this->Execute($getnext));
1645 $this->raiseErrorFn = $save_handler;
1647 if (!$rs) {
1648 $this->_transOK = $holdtransOK; //if the status was ok before reset
1649 $createseq = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$startID));
1650 $rs = $this->Execute($getnext);
1652 if ($rs && !$rs->EOF) {
1653 $this->genID = reset($rs->fields);
1654 } else {
1655 $this->genID = 0; // false
1658 if ($rs) {
1659 $rs->Close();
1662 return $this->genID;
1666 * Returns the last inserted ID.
1668 * Not all databases support this feature. Some do not require to specify
1669 * table or column name (e.g. MySQL).
1671 * @param string $table Table name, default ''
1672 * @param string $column Column name, default ''
1674 * @return int The last inserted ID.
1676 function Insert_ID($table='',$column='') {
1677 if ($this->_logsql && $this->lastInsID) {
1678 return $this->lastInsID;
1680 if ($this->hasInsertID) {
1681 return $this->_insertID($table,$column);
1683 if ($this->debug) {
1684 ADOConnection::outp( '<p>Insert_ID error</p>');
1685 adodb_backtrace();
1687 return false;
1691 * Enable or disable the Last Insert Id functionality.
1693 * If the Driver supports it, this function allows setting {@see $hasInsertID}.
1695 * @param bool $enable False to disable
1697 public function enableLastInsertID($enable = true) {}
1700 * Return the id of the last row that has been inserted in a table.
1702 * @param string $table
1703 * @param string $column
1705 * @return int|false
1707 protected function _insertID($table = '', $column = '')
1709 return false;
1713 * Portable Insert ID. Pablo Roca <pabloroca#mvps.org>
1715 * @param string $table
1716 * @param string $id
1718 * @return mixed The last inserted ID. All databases support this, but be
1719 * aware of possible problems in multiuser environments.
1720 * Heavily test this before deploying.
1722 function PO_Insert_ID($table="", $id="") {
1723 if ($this->hasInsertID){
1724 return $this->Insert_ID($table,$id);
1725 } else {
1726 return $this->GetOne("SELECT MAX($id) FROM $table");
1731 * @return int|false Number of rows affected by UPDATE/DELETE
1733 function Affected_Rows() {
1734 if ($this->hasAffectedRows) {
1735 if ($this->fnExecute === 'adodb_log_sql') {
1736 if ($this->_logsql && $this->_affected !== false) {
1737 return $this->_affected;
1740 $val = $this->_affectedrows();
1741 return ($val < 0) ? false : $val;
1744 if ($this->debug) {
1745 ADOConnection::outp( '<p>Affected_Rows error</p>',false);
1747 return false;
1752 * @return string the last error message
1754 function ErrorMsg() {
1755 if ($this->_errorMsg) {
1756 return '!! '.strtoupper($this->dataProvider.' '.$this->databaseType).': '.$this->_errorMsg;
1757 } else {
1758 return '';
1764 * @return int the last error number. Normally 0 means no error.
1766 function ErrorNo() {
1767 return ($this->_errorMsg) ? -1 : 0;
1770 function MetaError($err=false) {
1771 include_once(ADODB_DIR."/adodb-error.inc.php");
1772 if ($err === false) {
1773 $err = $this->ErrorNo();
1775 return adodb_error($this->dataProvider,$this->databaseType,$err);
1778 function MetaErrorMsg($errno) {
1779 include_once(ADODB_DIR."/adodb-error.inc.php");
1780 return adodb_errormsg($errno);
1784 * @returns an array with the primary key columns in it.
1786 function MetaPrimaryKeys($table, $owner=false) {
1787 // owner not used in base class - see oci8
1788 $p = array();
1789 $objs = $this->MetaColumns($table);
1790 if ($objs) {
1791 foreach($objs as $v) {
1792 if (!empty($v->primary_key)) {
1793 $p[] = $v->name;
1797 if (sizeof($p)) {
1798 return $p;
1800 if (function_exists('ADODB_VIEW_PRIMARYKEYS')) {
1801 return ADODB_VIEW_PRIMARYKEYS($this->databaseType, $this->database, $table, $owner);
1803 return false;
1807 * Returns a list of Foreign Keys associated with a specific table.
1809 * If there are no foreign keys then the function returns false.
1811 * @param string $table The name of the table to get the foreign keys for.
1812 * @param string $owner Table owner/schema.
1813 * @param bool $upper If true, only matches the table with the uppercase name.
1814 * @param bool $associative Returns the result in associative mode;
1815 * if ADODB_FETCH_MODE is already associative, then
1816 * this parameter is discarded.
1818 * @return string[]|false An array where keys are tables, and values are foreign keys;
1819 * false if no foreign keys could be found.
1821 function metaForeignKeys($table, $owner = '', $upper = false, $associative = false) {
1822 return false;
1826 * Choose a database to connect to. Many databases do not support this.
1828 * @param string $dbName the name of the database to select
1829 * @return bool
1831 function SelectDB($dbName) {return false;}
1835 * Select a limited number of rows.
1837 * Will select, getting rows from $offset (1-based), for $nrows.
1838 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
1839 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
1840 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
1841 * eg.
1842 * SelectLimit('select * from table',3); will return rows 1 to 3 (1-based)
1843 * SelectLimit('select * from table',3,2); will return rows 3 to 5 (1-based)
1845 * Uses SELECT TOP for Microsoft databases (when $this->hasTop is set)
1846 * BUG: Currently SelectLimit fails with $sql with LIMIT or TOP clause already set
1848 * @param string $sql
1849 * @param int $offset Row to start calculations from (1-based)
1850 * @param int $nrows Number of rows to get
1851 * @param array|bool $inputarr Array of bind variables
1852 * @param int $secs2cache Private parameter only used by jlim
1854 * @return ADORecordSet The recordset ($rs->databaseType == 'array')
1856 function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0) {
1857 $nrows = (int)$nrows;
1858 $offset = (int)$offset;
1860 if ($this->hasTop && $nrows > 0) {
1861 // suggested by Reinhard Balling. Access requires top after distinct
1862 // Informix requires first before distinct - F Riosa
1863 $ismssql = (strpos($this->databaseType,'mssql') !== false);
1864 if ($ismssql) {
1865 $isaccess = false;
1866 } else {
1867 $isaccess = (strpos($this->databaseType,'access') !== false);
1870 if ($offset <= 0) {
1871 // access includes ties in result
1872 if ($isaccess) {
1873 $sql = preg_replace(
1874 '/(^\s*select\s+(distinctrow|distinct)?)/i',
1875 '\\1 '.$this->hasTop.' '.$nrows.' ',
1876 $sql
1879 if ($secs2cache != 0) {
1880 $ret = $this->CacheExecute($secs2cache, $sql,$inputarr);
1881 } else {
1882 $ret = $this->Execute($sql,$inputarr);
1884 return $ret; // PHP5 fix
1885 } else if ($ismssql){
1886 $sql = preg_replace(
1887 '/(^\s*select\s+(distinctrow|distinct)?)/i',
1888 '\\1 '.$this->hasTop.' '.$nrows.' ',
1889 $sql
1891 } else {
1892 $sql = preg_replace(
1893 '/(^\s*select\s)/i',
1894 '\\1 '.$this->hasTop.' '.$nrows.' ',
1895 $sql
1898 } else {
1899 $nn = $nrows + $offset;
1900 if ($isaccess || $ismssql) {
1901 $sql = preg_replace(
1902 '/(^\s*select\s+(distinctrow|distinct)?)/i',
1903 '\\1 '.$this->hasTop.' '.$nn.' ',
1904 $sql
1906 } else {
1907 $sql = preg_replace(
1908 '/(^\s*select\s)/i',
1909 '\\1 '.$this->hasTop.' '.$nn.' ',
1910 $sql
1916 // if $offset>0, we want to skip rows, and $ADODB_COUNTRECS is set, we buffer rows
1917 // 0 to offset-1 which will be discarded anyway. So we disable $ADODB_COUNTRECS.
1918 global $ADODB_COUNTRECS;
1920 try {
1921 $savec = $ADODB_COUNTRECS;
1922 $ADODB_COUNTRECS = false;
1924 if ($secs2cache != 0) {
1925 $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
1926 } else {
1927 $rs = $this->Execute($sql, $inputarr);
1929 } finally {
1930 $ADODB_COUNTRECS = $savec;
1933 if ($rs && !$rs->EOF) {
1934 $rs = $this->_rs2rs($rs,$nrows,$offset);
1936 //print_r($rs);
1937 return $rs;
1941 * Create serializable recordset. Breaks rs link to connection.
1943 * @param ADORecordSet $rs the recordset to serialize
1945 * @return ADORecordSet_array|bool the new recordset
1947 function SerializableRS(&$rs) {
1948 $rs2 = $this->_rs2rs($rs);
1949 $ignore = false;
1950 $rs2->connection = $ignore;
1952 return $rs2;
1956 * Convert a database recordset to an array recordset.
1958 * Input recordset's cursor should be at beginning, and old $rs will be closed.
1960 * @param ADORecordSet $rs the recordset to copy
1961 * @param int $nrows number of rows to retrieve (optional)
1962 * @param int $offset offset by number of rows (optional)
1963 * @param bool $close
1965 * @return ADORecordSet_array|ADORecordSet|bool the new recordset
1967 function &_rs2rs(&$rs,$nrows=-1,$offset=-1,$close=true) {
1968 if (! $rs) {
1969 $ret = false;
1970 return $ret;
1972 $dbtype = $rs->databaseType;
1973 if (!$dbtype) {
1974 $rs = $rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1 -- why ?
1975 return $rs;
1977 if (($dbtype == 'array' || $dbtype == 'csv') && $nrows == -1 && $offset == -1) {
1978 $rs->MoveFirst();
1979 $rs = $rs; // required to prevent crashing in 4.2.1, but does not happen in 4.3.1-- why ?
1980 return $rs;
1982 $flds = array();
1983 for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++) {
1984 $flds[] = $rs->FetchField($i);
1987 $arr = $rs->GetArrayLimit($nrows,$offset);
1988 //print_r($arr);
1989 if ($close) {
1990 $rs->Close();
1993 $arrayClass = $this->arrayClass;
1995 $rs2 = new $arrayClass($fakeQueryId=1);
1996 $rs2->connection = $this;
1997 $rs2->sql = $rs->sql;
1998 $rs2->dataProvider = $this->dataProvider;
1999 $rs2->InitArrayFields($arr,$flds);
2000 $rs2->fetchMode = isset($rs->adodbFetchMode) ? $rs->adodbFetchMode : $rs->fetchMode;
2001 return $rs2;
2005 * Return all rows.
2007 * Compat with PEAR DB.
2009 * @param string $sql SQL statement
2010 * @param array|bool $inputarr Input bind array
2012 * @return array|false
2014 function GetAll($sql, $inputarr=false) {
2015 return $this->GetArray($sql,$inputarr);
2019 * Execute statement and return rows in an array.
2021 * The function executes a statement and returns all of the returned rows in
2022 * an array, or false if the statement execution fails or if only 1 column
2023 * is requested in the SQL statement.
2024 * If no records match the provided SQL statement, an empty array is returned.
2026 * @param string $sql SQL statement
2027 * @param array|bool $inputarr input bind array
2028 * @param bool $force_array
2029 * @param bool $first2cols
2031 * @return array|bool
2033 public function GetAssoc($sql, $inputarr = false, $force_array = false, $first2cols = false) {
2034 $rs = $this->Execute($sql, $inputarr);
2036 if (!$rs) {
2038 * Execution failure
2040 return false;
2042 return $rs->GetAssoc($force_array, $first2cols);
2046 * Search for the results of an executed query in the cache.
2048 * @param int $secs2cache
2049 * @param string|bool $sql SQL statement
2050 * @param array|bool $inputarr input bind array
2051 * @param bool $force_array
2052 * @param bool $first2cols
2054 * @return false|array
2056 public function CacheGetAssoc($secs2cache, $sql = false, $inputarr = false,$force_array = false, $first2cols = false) {
2057 if (!is_numeric($secs2cache)) {
2058 $first2cols = $force_array;
2059 $force_array = $inputarr;
2061 $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
2062 if (!$rs) {
2063 return false;
2065 return $rs->GetAssoc($force_array, $first2cols);
2069 * Return first element of first row of sql statement. Recordset is disposed
2070 * for you.
2072 * @param string $sql SQL statement
2073 * @param array|bool $inputarr input bind array
2074 * @return mixed
2076 public function GetOne($sql, $inputarr=false) {
2077 global $ADODB_COUNTRECS,$ADODB_GETONE_EOF;
2079 try {
2080 $crecs = $ADODB_COUNTRECS;
2081 $ADODB_COUNTRECS = false;
2082 $rs = $this->Execute($sql, $inputarr);
2083 } finally {
2084 $ADODB_COUNTRECS = $crecs;
2087 $ret = false;
2088 if ($rs) {
2089 if ($rs->EOF) {
2090 $ret = $ADODB_GETONE_EOF;
2091 } else {
2092 $ret = reset($rs->fields);
2095 $rs->Close();
2097 return $ret;
2100 // $where should include 'WHERE fld=value'
2101 function GetMedian($table, $field,$where = '') {
2102 $total = $this->GetOne("select count(*) from $table $where");
2103 if (!$total) {
2104 return false;
2107 $midrow = (integer) ($total/2);
2108 $rs = $this->SelectLimit("select $field from $table $where order by 1",1,$midrow);
2109 if ($rs && !$rs->EOF) {
2110 return reset($rs->fields);
2112 return false;
2116 function CacheGetOne($secs2cache,$sql=false,$inputarr=false) {
2117 global $ADODB_GETONE_EOF;
2119 $ret = false;
2120 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
2121 if ($rs) {
2122 if ($rs->EOF) {
2123 $ret = $ADODB_GETONE_EOF;
2124 } else {
2125 $ret = reset($rs->fields);
2127 $rs->Close();
2130 return $ret;
2134 * Executes a statement and returns each row's first column in an array.
2136 * @param string $sql SQL statement
2137 * @param array|bool $inputarr input bind array
2138 * @param bool $trim enables space trimming of the returned value.
2139 * This is only relevant if the returned string
2140 * is coming from a CHAR type field.
2142 * @return array|bool 1D array containning the first row of the query
2144 function GetCol($sql, $inputarr = false, $trim = false) {
2146 $rs = $this->Execute($sql, $inputarr);
2147 if ($rs) {
2148 $rv = array();
2149 if ($trim) {
2150 while (!$rs->EOF) {
2151 $rv[] = trim(reset($rs->fields));
2152 $rs->MoveNext();
2154 } else {
2155 while (!$rs->EOF) {
2156 $rv[] = reset($rs->fields);
2157 $rs->MoveNext();
2160 $rs->Close();
2161 } else {
2162 $rv = false;
2164 return $rv;
2167 function CacheGetCol($secs, $sql = false, $inputarr = false,$trim=false) {
2168 $rs = $this->CacheExecute($secs, $sql, $inputarr);
2169 if ($rs) {
2170 $rv = array();
2171 if ($trim) {
2172 while (!$rs->EOF) {
2173 $rv[] = trim(reset($rs->fields));
2174 $rs->MoveNext();
2176 } else {
2177 while (!$rs->EOF) {
2178 $rv[] = reset($rs->fields);
2179 $rs->MoveNext();
2182 $rs->Close();
2183 } else
2184 $rv = false;
2186 return $rv;
2190 * Calculate the offset of a date for a particular database
2191 * and generate appropriate SQL.
2193 * Useful for calculating future/past dates and storing in a database.
2195 * @param double $dayFraction 1.5 means 1.5 days from now, 1.0/24 for 1 hour
2196 * @param string|false $date Reference date, false for system time
2198 * @return string
2200 function OffsetDate($dayFraction,$date=false) {
2201 if (!$date) {
2202 $date = $this->sysDate;
2204 return '('.$date.'+'.$dayFraction.')';
2209 * Executes a statement and returns a the entire recordset in an array.
2211 * @param string $sql SQL statement
2212 * @param array|bool $inputarr input bind array
2214 * @return array|false
2216 function GetArray($sql,$inputarr=false) {
2217 global $ADODB_COUNTRECS;
2219 try {
2220 $savec = $ADODB_COUNTRECS;
2221 $ADODB_COUNTRECS = false;
2222 $rs = $this->Execute($sql, $inputarr);
2223 } finally {
2224 $ADODB_COUNTRECS = $savec;
2227 if (!$rs)
2228 if (defined('ADODB_PEAR')) {
2229 return ADODB_PEAR_Error();
2230 } else {
2231 return false;
2233 $arr = $rs->GetArray();
2234 $rs->Close();
2235 return $arr;
2238 function CacheGetAll($secs2cache,$sql=false,$inputarr=false) {
2239 return $this->CacheGetArray($secs2cache,$sql,$inputarr);
2242 function CacheGetArray($secs2cache,$sql=false,$inputarr=false) {
2243 global $ADODB_COUNTRECS;
2245 try {
2246 $savec = $ADODB_COUNTRECS;
2247 $ADODB_COUNTRECS = false;
2248 $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
2249 } finally {
2250 $ADODB_COUNTRECS = $savec;
2253 if (!$rs)
2254 if (defined('ADODB_PEAR')) {
2255 return ADODB_PEAR_Error();
2256 } else {
2257 return false;
2259 $arr = $rs->GetArray();
2260 $rs->Close();
2261 return $arr;
2264 function GetRandRow($sql, $arr= false) {
2265 $rezarr = $this->GetAll($sql, $arr);
2266 $sz = sizeof($rezarr);
2267 return $rezarr[abs(rand()) % $sz];
2271 * Return one row of sql statement. Recordset is disposed for you.
2272 * Note that SelectLimit should not be called.
2274 * @param string $sql SQL statement
2275 * @param array|bool $inputarr input bind array
2277 * @return array|false Array containing the first row of the query
2279 function GetRow($sql,$inputarr=false) {
2280 global $ADODB_COUNTRECS;
2282 try {
2283 $crecs = $ADODB_COUNTRECS;
2284 $ADODB_COUNTRECS = false;
2285 $rs = $this->Execute($sql, $inputarr);
2286 } finally {
2287 $ADODB_COUNTRECS = $crecs;
2290 if ($rs) {
2291 if (!$rs->EOF) {
2292 $arr = $rs->fields;
2293 } else {
2294 $arr = array();
2296 $rs->Close();
2297 return $arr;
2300 return false;
2304 * @param int $secs2cache
2305 * @param string|false $sql
2306 * @param mixed[]|bool $inputarr
2307 * @return mixed[]|bool
2309 function CacheGetRow($secs2cache,$sql=false,$inputarr=false) {
2310 $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
2311 if ($rs) {
2312 if (!$rs->EOF) {
2313 $arr = $rs->fields;
2314 } else {
2315 $arr = array();
2318 $rs->Close();
2319 return $arr;
2321 return false;
2325 * Insert or replace a single record. Note: this is not the same as MySQL's replace.
2326 * ADOdb's Replace() uses update-insert semantics, not insert-delete-duplicates of MySQL.
2327 * Also note that no table locking is done currently, so it is possible that the
2328 * record be inserted twice by two programs...
2330 * $this->Replace('products', array('prodname' =>"'Nails'","price" => 3.99), 'prodname');
2332 * $table table name
2333 * $fieldArray associative array of data (you must quote strings yourself).
2334 * $keyCol the primary key field name or if compound key, array of field names
2335 * autoQuote set to true to use a heuristic to quote strings. Works with nulls and numbers
2336 * but does not work with dates nor SQL functions.
2337 * has_autoinc the primary key is an auto-inc field, so skip in insert.
2339 * Currently blob replace not supported
2341 * returns 0 = fail, 1 = update, 2 = insert
2344 function Replace($table, $fieldArray, $keyCol, $autoQuote=false, $has_autoinc=false) {
2345 global $ADODB_INCLUDED_LIB;
2346 if (empty($ADODB_INCLUDED_LIB)) {
2347 include_once(ADODB_DIR.'/adodb-lib.inc.php');
2350 return _adodb_replace($this, $table, $fieldArray, $keyCol, $autoQuote, $has_autoinc);
2355 * Will select, getting rows from $offset (1-based), for $nrows.
2356 * This simulates the MySQL "select * from table limit $offset,$nrows" , and
2357 * the PostgreSQL "select * from table limit $nrows offset $offset". Note that
2358 * MySQL and PostgreSQL parameter ordering is the opposite of the other.
2359 * eg.
2360 * CacheSelectLimit(15,'select * from table',3); will return rows 1 to 3 (1-based)
2361 * CacheSelectLimit(15,'select * from table',3,2); will return rows 3 to 5 (1-based)
2363 * BUG: Currently CacheSelectLimit fails with $sql with LIMIT or TOP clause already set
2365 * @param int $secs2cache Seconds to cache data, set to 0 to force query. This is optional
2366 * @param string $sql
2367 * @param int $offset Row to start calculations from (1-based)
2368 * @param int $nrows Number of rows to get
2369 * @param array $inputarr Array of bind variables
2371 * @return ADORecordSet The recordset ($rs->databaseType == 'array')
2373 function CacheSelectLimit($secs2cache,$sql,$nrows=-1,$offset=-1,$inputarr=false) {
2374 if (!is_numeric($secs2cache)) {
2375 if ($sql === false) {
2376 $sql = -1;
2378 if ($offset == -1) {
2379 $offset = false;
2381 // sql, nrows, offset,inputarr
2382 $rs = $this->SelectLimit($secs2cache,$sql,$nrows,$offset,$this->cacheSecs);
2383 } else {
2384 if ($sql === false) {
2385 $this->outp_throw("Warning: \$sql missing from CacheSelectLimit()",'CacheSelectLimit');
2387 $rs = $this->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
2389 return $rs;
2393 * Flush cached recordsets that match a particular $sql statement.
2394 * If $sql == false, then we purge all files in the cache.
2396 function CacheFlush($sql=false,$inputarr=false) {
2397 global $ADODB_CACHE_DIR, $ADODB_CACHE;
2399 # Create cache if it does not exist
2400 if (empty($ADODB_CACHE)) {
2401 $this->_CreateCache();
2404 if (!$sql) {
2405 $ADODB_CACHE->flushall($this->debug);
2406 return;
2409 $f = $this->_gencachename($sql.serialize($inputarr),false);
2410 return $ADODB_CACHE->flushcache($f, $this->debug);
2415 * Private function to generate filename for caching.
2416 * Filename is generated based on:
2418 * - sql statement
2419 * - database type (oci8, ibase, ifx, etc)
2420 * - database name
2421 * - userid
2422 * - setFetchMode (adodb 4.23)
2424 * When not in safe mode, we create 256 sub-directories in the cache directory ($ADODB_CACHE_DIR).
2425 * Assuming that we can have 50,000 files per directory with good performance,
2426 * then we can scale to 12.8 million unique cached recordsets. Wow!
2428 function _gencachename($sql,$createdir) {
2429 global $ADODB_CACHE, $ADODB_CACHE_DIR;
2431 if ($this->fetchMode === false) {
2432 global $ADODB_FETCH_MODE;
2433 $mode = $ADODB_FETCH_MODE;
2434 } else {
2435 $mode = $this->fetchMode;
2437 $m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
2438 if (!$ADODB_CACHE->createdir) {
2439 return $m;
2441 if (!$createdir) {
2442 $dir = $ADODB_CACHE->getdirname($m);
2443 } else {
2444 $dir = $ADODB_CACHE->createdir($m, $this->debug);
2447 return $dir.'/adodb_'.$m.'.cache';
2452 * Execute SQL, caching recordsets.
2454 * @param int $secs2cache Seconds to cache data, set to 0 to force query.
2455 * This is an optional parameter.
2456 * @param string|bool $sql SQL statement to execute
2457 * @param array|bool $inputarr Holds the input data to bind
2459 * @return ADORecordSet RecordSet or false
2461 function CacheExecute($secs2cache,$sql=false,$inputarr=false) {
2462 global $ADODB_CACHE;
2464 if (empty($ADODB_CACHE)) {
2465 $this->_CreateCache();
2468 if (!is_numeric($secs2cache)) {
2469 $inputarr = $sql;
2470 $sql = $secs2cache;
2471 $secs2cache = $this->cacheSecs;
2474 if (is_array($sql)) {
2475 $sqlparam = $sql;
2476 $sql = $sql[0];
2477 } else
2478 $sqlparam = $sql;
2481 $md5file = $this->_gencachename($sql.serialize($inputarr),true);
2482 $err = '';
2484 if ($secs2cache > 0){
2485 $rs = $ADODB_CACHE->readcache($md5file,$err,$secs2cache,$this->arrayClass);
2486 $this->numCacheHits += 1;
2487 } else {
2488 $err='Timeout 1';
2489 $rs = false;
2490 $this->numCacheMisses += 1;
2493 if (!$rs) {
2494 // no cached rs found
2495 if ($this->debug) {
2496 if ($this->debug !== -1) {
2497 ADOConnection::outp( " $md5file cache failure: $err (this is a notice and not an error)");
2501 $rs = $this->Execute($sqlparam,$inputarr);
2503 if ($rs) {
2504 $eof = $rs->EOF;
2505 $rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
2506 $rs->timeCreated = time(); // used by caching
2507 $txt = _rs2serialize($rs,false,$sql); // serialize
2509 $ok = $ADODB_CACHE->writecache($md5file,$txt,$this->debug, $secs2cache);
2510 if (!$ok) {
2511 if ($ok === false) {
2512 $em = 'Cache write error';
2513 $en = -32000;
2515 if ($fn = $this->raiseErrorFn) {
2516 $fn($this->databaseType,'CacheExecute', $en, $em, $md5file,$sql,$this);
2518 } else {
2519 $em = 'Cache file locked warning';
2520 $en = -32001;
2521 // do not call error handling for just a warning
2524 if ($this->debug) {
2525 ADOConnection::outp( " ".$em);
2528 if ($rs->EOF && !$eof) {
2529 $rs->MoveFirst();
2530 //$rs = csv2rs($md5file,$err);
2531 $rs->connection = $this; // Pablo suggestion
2534 } else if (!$this->memCache) {
2535 $ADODB_CACHE->flushcache($md5file);
2537 } else {
2538 $this->_errorMsg = '';
2539 $this->_errorCode = 0;
2541 if ($this->fnCacheExecute) {
2542 $fn = $this->fnCacheExecute;
2543 $fn($this, $secs2cache, $sql, $inputarr);
2545 // ok, set cached object found
2546 $rs->connection = $this; // Pablo suggestion
2547 if ($this->debug){
2548 if ($this->debug == 99) {
2549 adodb_backtrace();
2551 $inBrowser = isset($_SERVER['HTTP_USER_AGENT']);
2552 $ttl = $rs->timeCreated + $secs2cache - time();
2553 $s = is_array($sql) ? $sql[0] : $sql;
2554 if ($inBrowser) {
2555 $s = '<i>'.htmlspecialchars($s).'</i>';
2558 ADOConnection::outp( " $md5file reloaded, ttl=$ttl [ $s ]");
2561 return $rs;
2568 $forceUpdate .
2571 * Similar to PEAR DB's autoExecute(), except that $mode can be 'INSERT'
2572 * or 'UPDATE' or DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE.
2573 * If $mode == 'UPDATE', then $where is compulsory as a safety measure.
2575 * @param $table
2576 * @param $fields_values
2577 * @param string $mode
2578 * @param false $where
2579 * @param bool $forceUpdate If true, perform update even if the data has not changed.
2580 * @param bool $magic_quotes This param is not used since 5.21.0.
2581 * It remains for backwards compatibility.
2583 * @return bool
2585 * @noinspection PhpUnusedParameterInspection
2587 function AutoExecute($table, $fields_values, $mode = 'INSERT', $where = false, $forceUpdate = true, $magic_quotes = false) {
2588 if (empty($fields_values)) {
2589 $this->outp_throw('AutoExecute: Empty fields array', 'AutoExecute');
2590 return false;
2592 if ($where === false && ($mode == 'UPDATE' || $mode == 2 /* DB_AUTOQUERY_UPDATE */) ) {
2593 $this->outp_throw('AutoExecute: Illegal mode=UPDATE with empty WHERE clause', 'AutoExecute');
2594 return false;
2597 $sql = "SELECT * FROM $table";
2598 $rs = $this->SelectLimit($sql, 1);
2599 if (!$rs) {
2600 return false; // table does not exist
2603 $rs->tableName = $table;
2604 if ($where !== false) {
2605 $sql .= " WHERE $where";
2607 $rs->sql = $sql;
2609 switch($mode) {
2610 case 'UPDATE':
2611 case DB_AUTOQUERY_UPDATE:
2612 $sql = $this->GetUpdateSQL($rs, $fields_values, $forceUpdate);
2613 break;
2614 case 'INSERT':
2615 case DB_AUTOQUERY_INSERT:
2616 $sql = $this->GetInsertSQL($rs, $fields_values);
2617 break;
2618 default:
2619 $this->outp_throw("AutoExecute: Unknown mode=$mode", 'AutoExecute');
2620 return false;
2622 return $sql && $this->Execute($sql);
2627 * Generates an Update Query based on an existing recordset.
2629 * $arrFields is an associative array of fields with the value
2630 * that should be assigned.
2632 * Note: This function should only be used on a recordset
2633 * that is run against a single table and sql should only
2634 * be a simple select stmt with no groupby/orderby/limit
2635 * @author "Jonathan Younger" <jyounger@unilab.com>
2637 * @param $rs
2638 * @param $arrFields
2639 * @param bool $forceUpdate
2640 * @param bool $magic_quotes This param is not used since 5.21.0.
2641 * It remains for backwards compatibility.
2642 * @param null $force
2644 * @return false|string
2646 * @noinspection PhpUnusedParameterInspection
2648 function GetUpdateSQL(&$rs, $arrFields, $forceUpdate=false, $magic_quotes=false, $force=null) {
2649 global $ADODB_INCLUDED_LIB;
2651 // ********************************************************
2652 // This is here to maintain compatibility
2653 // with older adodb versions. Sets force type to force nulls if $forcenulls is set.
2654 if (!isset($force)) {
2655 global $ADODB_FORCE_TYPE;
2656 $force = $ADODB_FORCE_TYPE;
2658 // ********************************************************
2660 if (empty($ADODB_INCLUDED_LIB)) {
2661 include_once(ADODB_DIR.'/adodb-lib.inc.php');
2663 return _adodb_getupdatesql($this, $rs, $arrFields, $forceUpdate, $force);
2667 * Generates an Insert Query based on an existing recordset.
2669 * $arrFields is an associative array of fields with the value
2670 * that should be assigned.
2672 * Note: This function should only be used on a recordset
2673 * that is run against a single table.
2675 * @param $rs
2676 * @param $arrFields
2677 * @param bool $magic_quotes This param is not used since 5.21.0.
2678 * It remains for backwards compatibility.
2679 * @param null $force
2681 * @return false|string
2683 * @noinspection PhpUnusedParameterInspection
2685 function GetInsertSQL(&$rs, $arrFields, $magic_quotes=false, $force=null) {
2686 global $ADODB_INCLUDED_LIB;
2687 if (!isset($force)) {
2688 global $ADODB_FORCE_TYPE;
2689 $force = $ADODB_FORCE_TYPE;
2691 if (empty($ADODB_INCLUDED_LIB)) {
2692 include_once(ADODB_DIR.'/adodb-lib.inc.php');
2694 return _adodb_getinsertsql($this, $rs, $arrFields, $force);
2699 * Update a BLOB column, given a where clause.
2701 * There are more sophisticated blob handling functions that we could have
2702 * implemented, but all require a very complex API. Instead we have chosen
2703 * something that is extremely simple to understand and use.
2705 * Sample usage:
2706 * - update a BLOB in field table.blob_col with value $blobValue, for a
2707 * record having primary key id=1
2708 * $conn->updateBlob('table', 'blob_col', $blobValue, 'id=1');
2709 * - insert example:
2710 * $conn->execute('INSERT INTO table (id, blob_col) VALUES (1, null)');
2711 * $conn->updateBlob('table', 'blob_col', $blobValue, 'id=1');
2713 * @param string $table
2714 * @param string $column
2715 * @param string $val Filename containing blob data
2716 * @param mixed $where {@see updateBlob()}
2717 * @param string $blobtype supports 'BLOB' (default) and 'CLOB'
2719 * @return bool success
2721 function updateBlob($table, $column, $val, $where, $blobtype='BLOB') {
2722 return $this->Execute("UPDATE $table SET $column=? WHERE $where",array($val)) != false;
2726 * Update a BLOB from a file.
2728 * Usage example:
2729 * $conn->updateBlobFile('table', 'blob_col', '/path/to/file', 'id=1');
2731 * @param string $table
2732 * @param string $column
2733 * @param string $path Filename containing blob data
2734 * @param mixed $where {@see updateBlob()}
2735 * @param string $blobtype supports 'BLOB' and 'CLOB'
2737 * @return bool success
2739 function updateBlobFile($table, $column, $path, $where, $blobtype='BLOB') {
2740 $fd = fopen($path,'rb');
2741 if ($fd === false) {
2742 return false;
2744 $val = fread($fd,filesize($path));
2745 fclose($fd);
2746 return $this->UpdateBlob($table,$column,$val,$where,$blobtype);
2749 function BlobDecode($blob) {
2750 return $blob;
2753 function BlobEncode($blob) {
2754 return $blob;
2758 * Retrieve the client connection's current character set.
2760 * @return string|false The character set, or false if it can't be determined.
2762 function getCharSet() {
2763 return $this->charSet;
2767 * Sets the client-side character set.
2769 * This is only supported for some databases.
2770 * @see https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:setcharset
2772 * @param string $charset The character set to switch to.
2774 * @return bool True if the character set was changed successfully, false otherwise.
2776 function setCharSet($charset) {
2777 $this->charSet = $charset;
2778 return true;
2781 function IfNull( $field, $ifNull ) {
2782 return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
2785 function LogSQL($enable=true) {
2786 include_once(ADODB_DIR.'/adodb-perf.inc.php');
2788 if ($enable) {
2789 $this->fnExecute = 'adodb_log_sql';
2790 } else {
2791 $this->fnExecute = false;
2794 $old = $this->_logsql;
2795 $this->_logsql = $enable;
2796 if ($enable && !$old) {
2797 $this->_affected = false;
2799 return $old;
2803 * Usage:
2804 * UpdateClob('TABLE', 'COLUMN', $var, 'ID=1', 'CLOB');
2806 * $conn->Execute('INSERT INTO clobtable (id, clobcol) VALUES (1, null)');
2807 * $conn->UpdateClob('clobtable','clobcol',$clob,'id=1');
2809 function UpdateClob($table,$column,$val,$where) {
2810 return $this->UpdateBlob($table,$column,$val,$where,'CLOB');
2813 // not the fastest implementation - quick and dirty - jlim
2814 // for best performance, use the actual $rs->MetaType().
2815 function MetaType($t,$len=-1,$fieldobj=false) {
2817 if (empty($this->_metars)) {
2818 $rsclass = $this->rsPrefix.$this->databaseType;
2819 $this->_metars = new $rsclass(false,$this->fetchMode);
2820 $this->_metars->connection = $this;
2822 return $this->_metars->MetaType($t,$len,$fieldobj);
2827 * Change the SQL connection locale to a specified locale.
2828 * This is used to get the date formats written depending on the client locale.
2830 function SetDateLocale($locale = 'En') {
2831 $this->locale = $locale;
2832 switch (strtoupper($locale))
2834 case 'EN':
2835 $this->fmtDate="'Y-m-d'";
2836 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2837 break;
2839 case 'US':
2840 $this->fmtDate = "'m-d-Y'";
2841 $this->fmtTimeStamp = "'m-d-Y H:i:s'";
2842 break;
2844 case 'PT_BR':
2845 case 'NL':
2846 case 'FR':
2847 case 'RO':
2848 case 'IT':
2849 $this->fmtDate="'d-m-Y'";
2850 $this->fmtTimeStamp = "'d-m-Y H:i:s'";
2851 break;
2853 case 'GE':
2854 $this->fmtDate="'d.m.Y'";
2855 $this->fmtTimeStamp = "'d.m.Y H:i:s'";
2856 break;
2858 default:
2859 $this->fmtDate="'Y-m-d'";
2860 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
2861 break;
2866 * GetActiveRecordsClass Performs an 'ALL' query
2868 * @param mixed $class This string represents the class of the current active record
2869 * @param mixed $table Table used by the active record object
2870 * @param mixed $whereOrderBy Where, order, by clauses
2871 * @param mixed $bindarr
2872 * @param mixed $primkeyArr
2873 * @param array $extra Query extras: limit, offset...
2874 * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo"
2875 * @access public
2876 * @return void
2878 function GetActiveRecordsClass(
2879 $class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false,
2880 $extra=array(),
2881 $relations=array())
2883 global $_ADODB_ACTIVE_DBS;
2884 ## reduce overhead of adodb.inc.php -- moved to adodb-active-record.inc.php
2885 ## if adodb-active-recordx is loaded -- should be no issue as they will probably use Find()
2886 if (!isset($_ADODB_ACTIVE_DBS)) {
2887 include_once(ADODB_DIR.'/adodb-active-record.inc.php');
2889 return adodb_GetActiveRecordsClass($this, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra, $relations);
2892 function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false) {
2893 $arr = $this->GetActiveRecordsClass('ADODB_Active_Record', $table, $where, $bindarr, $primkeyArr);
2894 return $arr;
2898 * Close Connection
2900 function Close() {
2901 $rez = $this->_close();
2902 $this->_queryID = false;
2903 $this->_connectionID = false;
2904 return $rez;
2908 * Begin a Transaction.
2910 * Must be followed by CommitTrans() or RollbackTrans().
2912 * @return bool true if succeeded or false if database does not support transactions
2914 function BeginTrans() {
2915 if ($this->debug) {
2916 ADOConnection::outp("BeginTrans: Transactions not supported for this driver");
2918 return false;
2921 /* set transaction mode */
2922 function SetTransactionMode( $transaction_mode ) {
2923 $transaction_mode = $this->MetaTransaction($transaction_mode, $this->dataProvider);
2924 $this->_transmode = $transaction_mode;
2927 http://msdn2.microsoft.com/en-US/ms173763.aspx
2928 http://dev.mysql.com/doc/refman/5.0/en/innodb-transaction-isolation.html
2929 http://www.postgresql.org/docs/8.1/interactive/sql-set-transaction.html
2930 http://www.stanford.edu/dept/itss/docs/oracle/10g/server.101/b10759/statements_10005.htm
2932 function MetaTransaction($mode,$db) {
2933 $mode = strtoupper($mode);
2934 $mode = str_replace('ISOLATION LEVEL ','',$mode);
2936 switch($mode) {
2938 case 'READ UNCOMMITTED':
2939 switch($db) {
2940 case 'oci8':
2941 case 'oracle':
2942 return 'ISOLATION LEVEL READ COMMITTED';
2943 default:
2944 return 'ISOLATION LEVEL READ UNCOMMITTED';
2946 break;
2948 case 'READ COMMITTED':
2949 return 'ISOLATION LEVEL READ COMMITTED';
2950 break;
2952 case 'REPEATABLE READ':
2953 switch($db) {
2954 case 'oci8':
2955 case 'oracle':
2956 return 'ISOLATION LEVEL SERIALIZABLE';
2957 default:
2958 return 'ISOLATION LEVEL REPEATABLE READ';
2960 break;
2962 case 'SERIALIZABLE':
2963 return 'ISOLATION LEVEL SERIALIZABLE';
2964 break;
2966 default:
2967 return $mode;
2972 * Commits a transaction.
2974 * If database does not support transactions, return true as data is
2975 * always committed.
2977 * @param bool $ok True to commit, false to rollback the transaction.
2979 * @return bool true if successful
2981 function CommitTrans($ok=true) {
2982 return true;
2987 * Rolls back a transaction.
2989 * If database does not support transactions, return false as rollbacks
2990 * always fail.
2992 * @return bool true if successful
2994 function RollbackTrans() {
2995 return false;
3000 * return the databases that the driver can connect to.
3001 * Some databases will return an empty array.
3003 * @return array|bool an array of database names.
3005 function MetaDatabases() {
3006 global $ADODB_FETCH_MODE;
3008 if ($this->metaDatabasesSQL) {
3009 $save = $ADODB_FETCH_MODE;
3010 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
3012 if ($this->fetchMode !== false) {
3013 $savem = $this->SetFetchMode(false);
3016 $arr = $this->GetCol($this->metaDatabasesSQL);
3017 if (isset($savem)) {
3018 $this->SetFetchMode($savem);
3020 $ADODB_FETCH_MODE = $save;
3022 return $arr;
3025 return false;
3029 * List procedures or functions in an array.
3030 * @param procedureNamePattern a procedure name pattern; must match the procedure name as it is stored in the database
3031 * @param catalog a catalog name; must match the catalog name as it is stored in the database;
3032 * @param schemaPattern a schema name pattern;
3034 * @return array of procedures on current database.
3036 * Array(
3037 * [name_of_procedure] => Array(
3038 * [type] => PROCEDURE or FUNCTION
3039 * [catalog] => Catalog_name
3040 * [schema] => Schema_name
3041 * [remarks] => explanatory comment on the procedure
3045 function MetaProcedures($procedureNamePattern = null, $catalog = null, $schemaPattern = null) {
3046 return false;
3051 * @param ttype can either be 'VIEW' or 'TABLE' or false.
3052 * If false, both views and tables are returned.
3053 * "VIEW" returns only views
3054 * "TABLE" returns only tables
3055 * @param showSchema returns the schema/user with the table name, eg. USER.TABLE
3056 * @param mask is the input mask - only supported by oci8 and postgresql
3058 * @return array of tables for current database.
3060 function MetaTables($ttype=false,$showSchema=false,$mask=false) {
3061 global $ADODB_FETCH_MODE;
3063 if ($mask) {
3064 return false;
3066 if ($this->metaTablesSQL) {
3067 $save = $ADODB_FETCH_MODE;
3068 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
3070 if ($this->fetchMode !== false) {
3071 $savem = $this->SetFetchMode(false);
3074 $rs = $this->Execute($this->metaTablesSQL);
3075 if (isset($savem)) {
3076 $this->SetFetchMode($savem);
3078 $ADODB_FETCH_MODE = $save;
3080 if ($rs === false) {
3081 return false;
3083 $arr = $rs->GetArray();
3084 $arr2 = array();
3086 if ($hast = ($ttype && isset($arr[0][1]))) {
3087 $showt = strncmp($ttype,'T',1);
3090 for ($i=0; $i < sizeof($arr); $i++) {
3091 if ($hast) {
3092 if ($showt == 0) {
3093 if (strncmp($arr[$i][1],'T',1) == 0) {
3094 $arr2[] = trim($arr[$i][0]);
3096 } else {
3097 if (strncmp($arr[$i][1],'V',1) == 0) {
3098 $arr2[] = trim($arr[$i][0]);
3101 } else
3102 $arr2[] = trim($arr[$i][0]);
3104 $rs->Close();
3105 return $arr2;
3107 return false;
3111 function _findschema(&$table,&$schema) {
3112 if (!$schema && ($at = strpos($table,'.')) !== false) {
3113 $schema = substr($table,0,$at);
3114 $table = substr($table,$at+1);
3119 * List columns in a database as an array of ADOFieldObjects.
3120 * See top of file for definition of object.
3122 * @param $table table name to query
3123 * @param $normalize makes table name case-insensitive (required by some databases)
3124 * @schema is optional database schema to use - not supported by all databases.
3126 * @return array of ADOFieldObjects for current table.
3128 function MetaColumns($table,$normalize=true) {
3129 global $ADODB_FETCH_MODE;
3131 if (!empty($this->metaColumnsSQL)) {
3132 $schema = false;
3133 $this->_findschema($table,$schema);
3135 $save = $ADODB_FETCH_MODE;
3136 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
3137 if ($this->fetchMode !== false) {
3138 $savem = $this->SetFetchMode(false);
3140 $rs = $this->Execute(sprintf($this->metaColumnsSQL,($normalize)?strtoupper($table):$table));
3141 if (isset($savem)) {
3142 $this->SetFetchMode($savem);
3144 $ADODB_FETCH_MODE = $save;
3145 if ($rs === false || $rs->EOF) {
3146 return false;
3149 $retarr = array();
3150 while (!$rs->EOF) { //print_r($rs->fields);
3151 $fld = new ADOFieldObject();
3152 $fld->name = $rs->fields[0];
3153 $fld->type = $rs->fields[1];
3154 if (isset($rs->fields[3]) && $rs->fields[3]) {
3155 if ($rs->fields[3]>0) {
3156 $fld->max_length = $rs->fields[3];
3158 $fld->scale = $rs->fields[4];
3159 if ($fld->scale>0) {
3160 $fld->max_length += 1;
3162 } else {
3163 $fld->max_length = $rs->fields[2];
3166 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) {
3167 $retarr[] = $fld;
3168 } else {
3169 $retarr[strtoupper($fld->name)] = $fld;
3171 $rs->MoveNext();
3173 $rs->Close();
3174 return $retarr;
3176 return false;
3180 * List indexes on a table as an array.
3181 * @param table table name to query
3182 * @param primary true to only show primary keys. Not actually used for most databases
3184 * @return array of indexes on current table. Each element represents an index, and is itself an associative array.
3186 * Array(
3187 * [name_of_index] => Array(
3188 * [unique] => true or false
3189 * [columns] => Array(
3190 * [0] => firstname
3191 * [1] => lastname
3196 function MetaIndexes($table, $primary = false, $owner = false) {
3197 return false;
3201 * List columns names in a table as an array.
3202 * @param table table name to query
3204 * @return array of column names for current table.
3206 function MetaColumnNames($table, $numIndexes=false,$useattnum=false /* only for postgres */) {
3207 $objarr = $this->MetaColumns($table);
3208 if (!is_array($objarr)) {
3209 return false;
3211 $arr = array();
3212 if ($numIndexes) {
3213 $i = 0;
3214 if ($useattnum) {
3215 foreach($objarr as $v)
3216 $arr[$v->attnum] = $v->name;
3218 } else
3219 foreach($objarr as $v) $arr[$i++] = $v->name;
3220 } else
3221 foreach($objarr as $v) $arr[strtoupper($v->name)] = $v->name;
3223 return $arr;
3227 * Concatenate strings.
3229 * Different SQL databases used different methods to combine strings together.
3230 * This function provides a wrapper.
3232 * Usage: $db->Concat($str1,$str2);
3234 * @param string $s Variable number of string parameters
3236 * @return string concatenated string
3238 function Concat() {
3239 $arr = func_get_args();
3240 return implode($this->concat_operator, $arr);
3245 * Converts a date "d" to a string that the database can understand.
3247 * @param mixed $d a date in Unix date time format.
3249 * @return string date string in database date format
3251 function DBDate($d, $isfld=false) {
3252 if (empty($d) && $d !== 0) {
3253 return 'null';
3255 if ($isfld) {
3256 return $d;
3258 if (is_object($d)) {
3259 return $d->format($this->fmtDate);
3262 if (is_string($d) && !is_numeric($d)) {
3263 if ($d === 'null') {
3264 return $d;
3266 if (strncmp($d,"'",1) === 0) {
3267 $d = _adodb_safedateq($d);
3268 return $d;
3270 if ($this->isoDates) {
3271 return "'$d'";
3273 $d = ADOConnection::UnixDate($d);
3276 return adodb_date($this->fmtDate,$d);
3279 function BindDate($d) {
3280 $d = $this->DBDate($d);
3281 if (strncmp($d,"'",1)) {
3282 return $d;
3285 return substr($d,1,strlen($d)-2);
3288 function BindTimeStamp($d) {
3289 $d = $this->DBTimeStamp($d);
3290 if (strncmp($d,"'",1)) {
3291 return $d;
3294 return substr($d,1,strlen($d)-2);
3299 * Converts a timestamp "ts" to a string that the database can understand.
3301 * @param int|object $ts A timestamp in Unix date time format.
3303 * @return string $timestamp string in database timestamp format
3305 function DBTimeStamp($ts,$isfld=false) {
3306 if (empty($ts) && $ts !== 0) {
3307 return 'null';
3309 if ($isfld) {
3310 return $ts;
3312 if (is_object($ts)) {
3313 return $ts->format($this->fmtTimeStamp);
3316 # strlen(14) allows YYYYMMDDHHMMSS format
3317 if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14)) {
3318 return adodb_date($this->fmtTimeStamp,$ts);
3321 if ($ts === 'null') {
3322 return $ts;
3324 if ($this->isoDates && strlen($ts) !== 14) {
3325 $ts = _adodb_safedate($ts);
3326 return "'$ts'";
3328 $ts = ADOConnection::UnixTimeStamp($ts);
3329 return adodb_date($this->fmtTimeStamp,$ts);
3333 * Also in ADORecordSet.
3334 * @param mixed $v is a date string in YYYY-MM-DD format
3336 * @return int|false Date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
3338 static function UnixDate($v) {
3339 if (is_object($v)) {
3340 // odbtp support
3341 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
3342 return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
3345 if (is_numeric($v) && strlen($v) !== 8) {
3346 return $v;
3348 if (!preg_match( "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})|", $v, $rr)) {
3349 return false;
3352 if ($rr[1] <= TIMESTAMP_FIRST_YEAR) {
3353 return 0;
3356 // h-m-s-MM-DD-YY
3357 return @adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
3362 * Also in ADORecordSet.
3363 * @param string|object $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
3365 * @return int|false Date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
3367 static function UnixTimeStamp($v) {
3368 if (is_object($v)) {
3369 // odbtp support
3370 //( [year] => 2004 [month] => 9 [day] => 4 [hour] => 12 [minute] => 44 [second] => 8 [fraction] => 0 )
3371 return adodb_mktime($v->hour,$v->minute,$v->second,$v->month,$v->day, $v->year);
3374 if (!preg_match(
3375 "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ ,-]*(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
3376 ($v), $rr)) return false;
3378 if ($rr[1] <= TIMESTAMP_FIRST_YEAR && $rr[2]<= 1) {
3379 return 0;
3382 // h-m-s-MM-DD-YY
3383 if (!isset($rr[5])) {
3384 return adodb_mktime(0,0,0,$rr[2],$rr[3],$rr[1]);
3386 return @adodb_mktime($rr[5],$rr[6],$rr[7],$rr[2],$rr[3],$rr[1]);
3390 * Format database date based on user defined format.
3392 * Also in ADORecordSet.
3394 * @param mixed $v Date in YYYY-MM-DD format, returned by database
3395 * @param string $fmt Format to apply, using date()
3396 * @param bool $gmt
3398 * @return string Formatted date
3400 function UserDate($v,$fmt='Y-m-d',$gmt=false) {
3401 $tt = $this->UnixDate($v);
3403 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
3404 if (($tt === false || $tt == -1) && $v != false) {
3405 return $v;
3406 } else if ($tt == 0) {
3407 return $this->emptyDate;
3408 } else if ($tt == -1) {
3409 // pre-TIMESTAMP_FIRST_YEAR
3412 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
3417 * Format timestamp based on user defined format.
3419 * @param mixed $v Date in YYYY-MM-DD hh:mm:ss format
3420 * @param string $fmt Format to apply, using date()
3421 * @param bool $gmt
3423 * @return string Formatted timestamp
3425 function UserTimeStamp($v,$fmt='Y-m-d H:i:s',$gmt=false) {
3426 if (!isset($v)) {
3427 return $this->emptyTimeStamp;
3429 # strlen(14) allows YYYYMMDDHHMMSS format
3430 if (is_numeric($v) && strlen($v)<14) {
3431 return ($gmt) ? adodb_gmdate($fmt,$v) : adodb_date($fmt,$v);
3433 $tt = $this->UnixTimeStamp($v);
3434 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
3435 if (($tt === false || $tt == -1) && $v != false) {
3436 return $v;
3438 if ($tt == 0) {
3439 return $this->emptyTimeStamp;
3441 return ($gmt) ? adodb_gmdate($fmt,$tt) : adodb_date($fmt,$tt);
3445 * Alias for addQ()
3446 * @param string $s
3447 * @param bool [$magic_quotes]
3448 * @return mixed
3450 * @deprecated 5.21.0
3451 * @noinspection PhpUnusedParameterInspection
3453 function escape($s,$magic_quotes=false) {
3454 return $this->addQ($s);
3458 * Quotes a string, without prefixing nor appending quotes.
3460 * @param string $s The string to quote
3461 * @param bool $magic_quotes This param is not used since 5.21.0.
3462 * It remains for backwards compatibility.
3464 * @return string Quoted string
3466 * @noinspection PhpUnusedParameterInspection
3468 function addQ($s, $magic_quotes=false) {
3469 if ($this->replaceQuote[0] == '\\') {
3470 $s = str_replace(
3471 array('\\', "\0"),
3472 array('\\\\', "\\\0"),
3476 return str_replace("'", $this->replaceQuote, $s);
3480 * Correctly quotes a string so that all strings are escaped.
3481 * We prefix and append to the string single-quotes.
3482 * An example is $db->qstr("Don't bother");
3483 * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:qstr
3485 * @param string $s The string to quote
3486 * @param bool $magic_quotes This param is not used since 5.21.0.
3487 * It remains for backwards compatibility.
3489 * @return string Quoted string to be sent back to database
3491 * @noinspection PhpUnusedParameterInspection
3493 function qStr($s, $magic_quotes=false) {
3494 return "'" . $this->addQ($s) . "'";
3499 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
3500 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
3501 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
3503 * See docs-adodb.htm#ex8 for an example of usage.
3504 * NOTE: phpLens uses a different algorithm and does not use PageExecute().
3506 * @param string $sql
3507 * @param int $nrows Number of rows per page to get
3508 * @param int $page Page number to get (1-based)
3509 * @param mixed[]|bool $inputarr Array of bind variables
3510 * @param int $secs2cache Private parameter only used by jlim
3512 * @return mixed the recordset ($rs->databaseType == 'array')
3514 function PageExecute($sql, $nrows, $page, $inputarr=false, $secs2cache=0) {
3515 global $ADODB_INCLUDED_LIB;
3516 if (empty($ADODB_INCLUDED_LIB)) {
3517 include_once(ADODB_DIR.'/adodb-lib.inc.php');
3519 if ($this->pageExecuteCountRows) {
3520 $rs = _adodb_pageexecute_all_rows($this, $sql, $nrows, $page, $inputarr, $secs2cache);
3521 } else {
3522 $rs = _adodb_pageexecute_no_last_page($this, $sql, $nrows, $page, $inputarr, $secs2cache);
3524 return $rs;
3529 * Will select the supplied $page number from a recordset, given that it is paginated in pages of
3530 * $nrows rows per page. It also saves two boolean values saying if the given page is the first
3531 * and/or last one of the recordset. Added by Iván Oliva to provide recordset pagination.
3533 * @param int $secs2cache seconds to cache data, set to 0 to force query
3534 * @param string $sql
3535 * @param int $nrows is the number of rows per page to get
3536 * @param int $page is the page number to get (1-based)
3537 * @param mixed[]|bool $inputarr array of bind variables
3538 * @return mixed the recordset ($rs->databaseType == 'array')
3540 function CachePageExecute($secs2cache, $sql, $nrows, $page,$inputarr=false) {
3541 /*switch($this->dataProvider) {
3542 case 'postgres':
3543 case 'mysql':
3544 break;
3545 default: $secs2cache = 0; break;
3547 return $this->PageExecute($sql,$nrows,$page,$inputarr,$secs2cache);
3551 * Returns the maximum size of a MetaType C field. If the method
3552 * is not defined in the driver returns ADODB_STRINGMAX_NOTSET
3554 * @return int
3556 function charMax() {
3557 return ADODB_STRINGMAX_NOTSET;
3561 * Returns the maximum size of a MetaType X field. If the method
3562 * is not defined in the driver returns ADODB_STRINGMAX_NOTSET
3564 * @return int
3566 function textMax() {
3567 return ADODB_STRINGMAX_NOTSET;
3571 * Returns a substring of a varchar type field
3573 * Some databases have variations of the parameters, which is why
3574 * we have an ADOdb function for it
3576 * @param string $fld The field to sub-string
3577 * @param int $start The start point
3578 * @param int $length An optional length
3580 * @return string The SQL text
3582 function substr($fld,$start,$length=0) {
3583 $text = "{$this->substr}($fld,$start";
3584 if ($length > 0)
3585 $text .= ",$length";
3586 $text .= ')';
3587 return $text;
3591 * Formats the date into Month only format MM with leading zeroes
3593 * @param string $fld The name of the date to format
3595 * @return string The SQL text
3597 function month($fld) {
3598 return $this->sqlDate('m',$fld);
3602 * Formats the date into Day only format DD with leading zeroes
3604 * @param string $fld The name of the date to format
3605 * @return string The SQL text
3607 function day($fld) {
3608 return $this->sqlDate('d',$fld);
3612 * Formats the date into year only format YYYY
3614 * @param string $fld The name of the date to format
3616 * @return string The SQL text
3618 function year($fld) {
3619 return $this->sqlDate('Y',$fld);
3623 * Get the last error recorded by PHP and clear the message.
3625 * By clearing the message, it becomes possible to detect whether a new error
3626 * has occurred, even when it is the same error as before being repeated.
3628 * @return mixed[]|null Array if an error has previously occurred. Null otherwise.
3630 protected function resetLastError() {
3631 $error = error_get_last();
3633 if (is_array($error)) {
3634 $error['message'] = '';
3637 return $error;
3641 * Compare a previously stored error message with the last error recorded by PHP
3642 * to determine whether a new error has occurred.
3644 * @param mixed[]|null $old Optional. Previously stored return value of error_get_last().
3646 * @return string The error message if a new error has occurred
3647 * or an empty string if no (new) errors have occurred..
3649 protected function getChangedErrorMsg($old = null) {
3650 $new = error_get_last();
3652 if (is_null($new)) {
3653 // No error has occurred yet at all.
3654 return '';
3657 if (is_null($old)) {
3658 // First error recorded.
3659 return $new['message'];
3662 $changed = false;
3663 foreach($new as $key => $value) {
3664 if ($new[$key] !== $old[$key]) {
3665 $changed = true;
3666 break;
3670 if ($changed === true) {
3671 return $new['message'];
3674 return '';
3677 } // end class ADOConnection
3681 //==============================================================================================
3682 // CLASS ADOFetchObj
3683 //==============================================================================================
3686 * Internal placeholder for record objects. Used by ADORecordSet->FetchObj().
3688 class ADOFetchObj {
3692 * Class ADODB_Iterator_empty
3694 class ADODB_Iterator_empty implements Iterator {
3696 private $rs;
3698 function __construct($rs) {
3699 $this->rs = $rs;
3702 #[\ReturnTypeWillChange]
3703 function rewind() {}
3705 #[\ReturnTypeWillChange]
3706 function valid() {
3707 return !$this->rs->EOF;
3710 #[\ReturnTypeWillChange]
3711 function key() {
3712 return false;
3715 #[\ReturnTypeWillChange]
3716 function current() {
3717 return false;
3720 #[\ReturnTypeWillChange]
3721 function next() {}
3723 function __call($func, $params) {
3724 return call_user_func_array(array($this->rs, $func), $params);
3727 #[\ReturnTypeWillChange]
3728 function hasMore() {
3729 return false;
3736 * Lightweight recordset when there are no records to be returned
3738 class ADORecordSet_empty implements IteratorAggregate
3740 var $dataProvider = 'empty';
3741 var $databaseType = false;
3742 var $EOF = true;
3743 var $_numOfRows = 0;
3744 /** @var bool|array */
3745 var $fields = false;
3746 var $connection = false;
3748 function RowCount() {
3749 return 0;
3752 function RecordCount() {
3753 return 0;
3756 function PO_RecordCount() {
3757 return 0;
3760 function Close() {
3761 return true;
3764 function FetchRow() {
3765 return false;
3768 function FieldCount() {
3769 return 0;
3772 function Init() {}
3774 #[\ReturnTypeWillChange]
3775 function getIterator() {
3776 return new ADODB_Iterator_empty($this);
3779 function GetAssoc() {
3780 return array();
3783 function GetArray() {
3784 return array();
3787 function GetAll() {
3788 return array();
3791 function GetArrayLimit() {
3792 return array();
3795 function GetRows() {
3796 return array();
3799 function GetRowAssoc() {
3800 return array();
3803 function MaxRecordCount() {
3804 return 0;
3807 function NumRows() {
3808 return 0;
3811 function NumCols() {
3812 return 0;
3816 //==============================================================================================
3817 // DATE AND TIME FUNCTIONS
3818 //==============================================================================================
3819 if (!defined('ADODB_DATE_VERSION')) {
3820 include_once(ADODB_DIR.'/adodb-time.inc.php');
3824 * Class ADODB_Iterator
3826 class ADODB_Iterator implements Iterator {
3828 private $rs;
3830 function __construct($rs) {
3831 $this->rs = $rs;
3834 #[\ReturnTypeWillChange]
3835 function rewind() {
3836 $this->rs->MoveFirst();
3839 #[\ReturnTypeWillChange]
3840 function valid() {
3841 return !$this->rs->EOF;
3844 #[\ReturnTypeWillChange]
3845 function key() {
3846 return $this->rs->_currentRow;
3849 #[\ReturnTypeWillChange]
3850 function current() {
3851 return $this->rs->fields;
3854 #[\ReturnTypeWillChange]
3855 function next() {
3856 $this->rs->MoveNext();
3859 function __call($func, $params) {
3860 return call_user_func_array(array($this->rs, $func), $params);
3863 function hasMore() {
3864 return !$this->rs->EOF;
3871 * RecordSet class that represents the dataset returned by the database.
3873 * To keep memory overhead low, this class holds only the current row in memory.
3874 * No prefetching of data is done, so the RecordCount() can return -1 (which
3875 * means recordcount not known).
3877 class ADORecordSet implements IteratorAggregate {
3880 * public variables
3882 var $dataProvider = "native";
3883 /** @var bool|array */
3884 var $fields = false; /// holds the current row data
3885 var $blobSize = 100; /// any varchar/char field this size or greater is treated as a blob
3886 /// in other words, we use a text area for editing.
3887 var $canSeek = false; /// indicates that seek is supported
3888 var $sql; /// sql text
3889 var $EOF = false; /// Indicates that the current record position is after the last record in a Recordset object.
3891 var $emptyTimeStamp = '&nbsp;'; /// what to display when $time==0
3892 var $emptyDate = '&nbsp;'; /// what to display when $time==0
3893 var $debug = false;
3894 var $timeCreated=0; /// datetime in Unix format rs created -- for cached recordsets
3896 var $bind = false; /// used by Fields() to hold array - should be private?
3897 var $fetchMode; /// default fetch mode
3898 /** @var ADOConnection The parent connection */
3899 var $connection = false;
3901 * private variables
3903 var $_numOfRows = -1; /** number of rows, or -1 */
3904 var $_numOfFields = -1; /** number of fields in recordset */
3905 /** @var resource result link identifier */
3906 var $_queryID = -1;
3907 var $_currentRow = -1; /** This variable keeps the current row in the Recordset. */
3908 var $_closed = false; /** has recordset been closed */
3909 var $_inited = false; /** Init() should only be called once */
3910 var $_obj; /** Used by FetchObj */
3911 var $_names; /** Used by FetchObj */
3913 var $_currentPage = -1; /** Added by Iván Oliva to implement recordset pagination */
3914 var $_atFirstPage = false; /** Added by Iván Oliva to implement recordset pagination */
3915 var $_atLastPage = false; /** Added by Iván Oliva to implement recordset pagination */
3916 var $_lastPageNo = -1;
3917 var $_maxRecordCount = 0;
3918 var $datetime = false;
3920 public $customActualTypes;
3921 public $customMetaTypes;
3925 * @var ADOFieldObject[] Field metadata cache
3926 * @see fieldTypesArray()
3928 protected $fieldObjectsCache;
3931 * Constructor
3933 * @param resource|int $queryID Query ID returned by ADOConnection->_query()
3934 * @param int|bool $mode The ADODB_FETCH_MODE value
3936 function __construct($queryID,$mode=false) {
3937 $this->_queryID = $queryID;
3940 function __destruct() {
3941 $this->Close();
3944 #[\ReturnTypeWillChange]
3945 function getIterator() {
3946 return new ADODB_Iterator($this);
3949 /* this is experimental - i don't really know what to return... */
3950 function __toString() {
3951 include_once(ADODB_DIR.'/toexport.inc.php');
3952 return _adodb_export($this,',',',',false,true);
3955 function Init() {
3956 if ($this->_inited) {
3957 return;
3959 $this->_inited = true;
3960 if ($this->_queryID) {
3961 @$this->_initRS();
3962 } else {
3963 $this->_numOfRows = 0;
3964 $this->_numOfFields = 0;
3966 if ($this->_numOfRows != 0 && $this->_numOfFields && $this->_currentRow == -1) {
3967 $this->_currentRow = 0;
3968 if ($this->EOF = ($this->_fetch() === false)) {
3969 $this->_numOfRows = 0; // _numOfRows could be -1
3971 } else {
3972 $this->EOF = true;
3977 * Recordset initialization stub
3979 protected function _initRS() {}
3982 * Row fetch stub
3983 * @return bool
3985 protected function _fetch() {}
3988 * Generate a SELECT tag from a recordset, and return the HTML markup.
3990 * If the recordset has 2 columns, we treat the first one as the text to
3991 * display to the user, and the second as the return value. Extra columns
3992 * are discarded.
3994 * @param string $name Name of SELECT tag
3995 * @param string|array $defstr The value to highlight. Use an array for multiple highlight values.
3996 * @param bool|string $blank1stItem True to create an empty item (default), False not to add one;
3997 * 'string' to set its label and 'value:string' to assign a value to it.
3998 * @param bool $multiple True for multi-select list
3999 * @param int $size Number of rows to show (applies to multi-select box only)
4000 * @param string $selectAttr Additional attributes to defined for SELECT tag,
4001 * useful for holding javascript onChange='...' handlers, CSS class, etc.
4002 * @param bool $compareFirstCol When true (default), $defstr is compared against the value (column 2),
4003 * while false will compare against the description (column 1).
4005 * @return string HTML
4007 function getMenu($name, $defstr = '', $blank1stItem = true, $multiple = false,
4008 $size = 0, $selectAttr = '', $compareFirstCol = true)
4010 global $ADODB_INCLUDED_LIB;
4011 if (empty($ADODB_INCLUDED_LIB)) {
4012 include_once(ADODB_DIR.'/adodb-lib.inc.php');
4014 return _adodb_getmenu($this, $name, $defstr, $blank1stItem, $multiple,
4015 $size, $selectAttr, $compareFirstCol);
4019 * Generate a SELECT tag with groups from a recordset, and return the HTML markup.
4021 * The recordset must have 3 columns and be ordered by the 3rd column. The
4022 * first column contains the text to display to the user, the second is the
4023 * return value and the third is the option group. Extra columns are discarded.
4024 * Default strings are compared with the SECOND column.
4026 * @param string $name Name of SELECT tag
4027 * @param string|array $defstr The value to highlight. Use an array for multiple highlight values.
4028 * @param bool|string $blank1stItem True to create an empty item (default), False not to add one;
4029 * 'string' to set its label and 'value:string' to assign a value to it.
4030 * @param bool $multiple True for multi-select list
4031 * @param int $size Number of rows to show (applies to multi-select box only)
4032 * @param string $selectAttr Additional attributes to defined for SELECT tag,
4033 * useful for holding javascript onChange='...' handlers, CSS class, etc.
4034 * @param bool $compareFirstCol When true (default), $defstr is compared against the value (column 2),
4035 * while false will compare against the description (column 1).
4037 * @return string HTML
4039 function getMenuGrouped($name, $defstr = '', $blank1stItem = true, $multiple = false,
4040 $size = 0, $selectAttr = '', $compareFirstCol = true)
4042 global $ADODB_INCLUDED_LIB;
4043 if (empty($ADODB_INCLUDED_LIB)) {
4044 include_once(ADODB_DIR.'/adodb-lib.inc.php');
4046 return _adodb_getmenu_gp($this, $name, $defstr, $blank1stItem, $multiple,
4047 $size, $selectAttr, $compareFirstCol);
4051 * Generate a SELECT tag from a recordset, and return the HTML markup.
4053 * Same as GetMenu(), except that default strings are compared with the
4054 * FIRST column (the description).
4056 * @param string $name Name of SELECT tag
4057 * @param string|array $defstr The value to highlight. Use an array for multiple highlight values.
4058 * @param bool|string $blank1stItem True to create an empty item (default), False not to add one;
4059 * 'string' to set its label and 'value:string' to assign a value to it.
4060 * @param bool $multiple True for multi-select list
4061 * @param int $size Number of rows to show (applies to multi-select box only)
4062 * @param string $selectAttr Additional attributes to defined for SELECT tag,
4063 * useful for holding javascript onChange='...' handlers, CSS class, etc.
4065 * @return string HTML
4067 * @deprecated 5.21.0 Use getMenu() with $compareFirstCol = false instead.
4069 function getMenu2($name, $defstr = '', $blank1stItem = true, $multiple = false,
4070 $size = 0, $selectAttr = '')
4072 return $this->getMenu($name, $defstr, $blank1stItem, $multiple,
4073 $size, $selectAttr,false);
4077 * Generate a SELECT tag with groups from a recordset, and return the HTML markup.
4079 * Same as GetMenuGrouped(), except that default strings are compared with the
4080 * FIRST column (the description).
4082 * @param string $name Name of SELECT tag
4083 * @param string|array $defstr The value to highlight. Use an array for multiple highlight values.
4084 * @param bool|string $blank1stItem True to create an empty item (default), False not to add one;
4085 * 'string' to set its label and 'value:string' to assign a value to it.
4086 * @param bool $multiple True for multi-select list
4087 * @param int $size Number of rows to show (applies to multi-select box only)
4088 * @param string $selectAttr Additional attributes to defined for SELECT tag,
4089 * useful for holding javascript onChange='...' handlers, CSS class, etc.
4091 * @return string HTML
4093 * @deprecated 5.21.0 Use getMenuGrouped() with $compareFirstCol = false instead.
4095 function getMenu3($name, $defstr = '', $blank1stItem = true, $multiple = false,
4096 $size = 0, $selectAttr = '')
4098 return $this->getMenuGrouped($name, $defstr, $blank1stItem, $multiple,
4099 $size, $selectAttr, false);
4103 * return recordset as a 2-dimensional array.
4105 * @param int $nRows Number of rows to return. -1 means every row.
4107 * @return array indexed by the rows (0-based) from the recordset
4109 function GetArray($nRows = -1) {
4110 $results = array();
4111 $cnt = 0;
4112 while (!$this->EOF && $nRows != $cnt) {
4113 $results[] = $this->fields;
4114 $this->MoveNext();
4115 $cnt++;
4117 return $results;
4120 function GetAll($nRows = -1) {
4121 return $this->GetArray($nRows);
4125 * Checks if there is another available recordset.
4127 * Some databases allow multiple recordsets to be returned.
4129 * @return boolean true if there is a next recordset, or false if no more
4131 function NextRecordSet() {
4132 return false;
4136 * Return recordset as a 2-dimensional array.
4138 * Helper function for ADOConnection->SelectLimit()
4140 * @param int $nrows Number of rows to return
4141 * @param int $offset Starting row (1-based)
4143 * @return array an array indexed by the rows (0-based) from the recordset
4145 function getArrayLimit($nrows, $offset=-1) {
4146 if ($offset <= 0) {
4147 return $this->GetArray($nrows);
4150 $this->Move($offset);
4152 $results = array();
4153 $cnt = 0;
4154 while (!$this->EOF && $nrows != $cnt) {
4155 $results[$cnt++] = $this->fields;
4156 $this->MoveNext();
4159 return $results;
4164 * Synonym for GetArray() for compatibility with ADO.
4166 * @param int $nRows Number of rows to return. -1 means every row.
4168 * @return array an array indexed by the rows (0-based) from the recordset
4170 function getRows($nRows = -1) {
4171 return $this->GetArray($nRows);
4175 * return whole recordset as a 2-dimensional associative array if
4176 * there are more than 2 columns. The first column is treated as the
4177 * key and is not included in the array. If there is only 2 columns,
4178 * it will return a 1 dimensional array of key-value pairs unless
4179 * $force_array == true. This recordset method is currently part of
4180 * the API, but may not be in later versions of ADOdb. By preference, use
4181 * ADOconnnection::getAssoc()
4183 * @param bool $force_array (optional) Has only meaning if we have 2 data
4184 * columns. If false, a 1 dimensional
4185 * array is returned, otherwise a 2 dimensional
4186 * array is returned. If this sounds confusing,
4187 * read the source.
4189 * @param bool $first2cols (optional) Means if there are more than
4190 * 2 cols, ignore the remaining cols and
4191 * instead of returning
4192 * array[col0] => array(remaining cols),
4193 * return array[col0] => col1
4195 * @return mixed[]|false
4198 function getAssoc($force_array = false, $first2cols = false)
4201 * Insufficient rows to show data
4203 if ($this->_numOfFields < 2)
4204 return;
4207 * Empty recordset
4209 if (!$this->fields) {
4210 return array();
4214 * The number of fields is half the actual returned in BOTH mode
4216 $numberOfFields = $this->_numOfFields;
4219 * Get the fetch mode when the call was executed, this may be
4220 * different than ADODB_FETCH_MODE
4222 $fetchMode = $this->connection->fetchMode;
4223 if ($fetchMode == ADODB_FETCH_BOTH) {
4225 * If we are using BOTH, we present the data as if it
4226 * was in ASSOC mode. This could be enhanced by adding
4227 * a BOTH_ASSOC_MODE class property
4228 * We build a template of numeric keys. you could improve the
4229 * speed by caching this, indexed by number of keys
4231 $testKeys = array_fill(0,$numberOfFields,0);
4234 $showArrayMethod = 0;
4236 if ($numberOfFields == 2)
4238 * Key is always value of first element
4239 * Value is always value of second element
4241 $showArrayMethod = 1;
4243 if ($force_array)
4244 $showArrayMethod = 0;
4246 if ($first2cols)
4247 $showArrayMethod = 1;
4249 $results = array();
4251 while (!$this->EOF){
4253 $myFields = $this->fields;
4255 if ($fetchMode == ADODB_FETCH_BOTH) {
4257 * extract the associative keys
4259 $myFields = array_diff_key($myFields,$testKeys);
4263 * key is value of first element, rest is data,
4264 * The key is not case processed
4266 $key = array_shift($myFields);
4268 switch ($showArrayMethod) {
4269 case 0:
4271 if ($fetchMode == ADODB_FETCH_ASSOC
4272 || $fetchMode == ADODB_FETCH_BOTH)
4275 * The driver should have already handled the key
4276 * casing, but in case it did not. We will check and force
4277 * this in later versions of ADOdb
4279 if (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_UPPER)
4280 $myFields = array_change_key_case($myFields,CASE_UPPER);
4282 elseif (ADODB_ASSOC_CASE == ADODB_ASSOC_CASE_LOWER)
4283 $myFields = array_change_key_case($myFields,CASE_LOWER);
4286 * We have already shifted the key off
4287 * the front, so the rest is the value
4289 $results[$key] = $myFields;
4292 else
4294 * I want the values in a numeric array,
4295 * nicely re-indexed from zero
4297 $results[$key] = array_values($myFields);
4298 break;
4300 case 1:
4303 * Don't care how long the array is,
4304 * I just want value of second column, and it doesn't
4305 * matter whether the array is associative or numeric
4307 $results[$key] = array_shift($myFields);
4308 break;
4311 $this->MoveNext();
4314 * Done
4316 return $results;
4321 * @param mixed $v is the character timestamp in YYYY-MM-DD hh:mm:ss format
4322 * @param string [$fmt] is the format to apply to it, using date()
4324 * @return string a timestamp formated as user desires
4326 function UserTimeStamp($v,$fmt='Y-m-d H:i:s') {
4327 if (is_numeric($v) && strlen($v)<14) {
4328 return adodb_date($fmt,$v);
4330 $tt = $this->UnixTimeStamp($v);
4331 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
4332 if (($tt === false || $tt == -1) && $v != false) {
4333 return $v;
4335 if ($tt === 0) {
4336 return $this->emptyTimeStamp;
4338 return adodb_date($fmt,$tt);
4343 * @param mixed $v is the character date in YYYY-MM-DD format, returned by database
4344 * @param string $fmt is the format to apply to it, using date()
4346 * @return string a date formatted as user desires
4348 function UserDate($v,$fmt='Y-m-d') {
4349 $tt = $this->UnixDate($v);
4350 // $tt == -1 if pre TIMESTAMP_FIRST_YEAR
4351 if (($tt === false || $tt == -1) && $v != false) {
4352 return $v;
4353 } else if ($tt == 0) {
4354 return $this->emptyDate;
4355 } else if ($tt == -1) {
4356 // pre-TIMESTAMP_FIRST_YEAR
4358 return adodb_date($fmt,$tt);
4363 * @param mixed $v is a date string in YYYY-MM-DD format
4365 * @return string date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
4367 static function UnixDate($v) {
4368 return ADOConnection::UnixDate($v);
4373 * @param string|object $v is a timestamp string in YYYY-MM-DD HH-NN-SS format
4375 * @return mixed date in unix timestamp format, or 0 if before TIMESTAMP_FIRST_YEAR, or false if invalid date format
4377 static function UnixTimeStamp($v) {
4378 return ADOConnection::UnixTimeStamp($v);
4383 * PEAR DB Compat - do not use internally
4385 function Free() {
4386 return $this->Close();
4391 * PEAR DB compat, number of rows
4393 * @return int
4395 function NumRows() {
4396 return $this->_numOfRows;
4401 * PEAR DB compat, number of cols
4403 * @return int
4405 function NumCols() {
4406 return $this->_numOfFields;
4410 * Fetch a row, returning false if no more rows.
4411 * This is PEAR DB compat mode.
4413 * @return mixed[]|false false or array containing the current record
4415 function FetchRow() {
4416 if ($this->EOF) {
4417 return false;
4419 $arr = $this->fields;
4420 $this->_currentRow++;
4421 if (!$this->_fetch()) {
4422 $this->EOF = true;
4424 return $arr;
4429 * Fetch a row, returning PEAR_Error if no more rows.
4430 * This is PEAR DB compat mode.
4432 * @param mixed[]|false $arr
4434 * @return mixed DB_OK or error object
4436 function FetchInto(&$arr) {
4437 if ($this->EOF) {
4438 return (defined('PEAR_ERROR_RETURN')) ? new PEAR_Error('EOF',-1): false;
4440 $arr = $this->fields;
4441 $this->MoveNext();
4442 return 1; // DB_OK
4447 * Move to the first row in the recordset. Many databases do NOT support this.
4449 * @return bool true or false
4451 function MoveFirst() {
4452 if ($this->_currentRow == 0) {
4453 return true;
4455 return $this->Move(0);
4460 * Move to the last row in the recordset.
4462 * @return bool true or false
4464 function MoveLast() {
4465 if ($this->_numOfRows >= 0) {
4466 return $this->Move($this->_numOfRows-1);
4468 if ($this->EOF) {
4469 return false;
4471 while (!$this->EOF) {
4472 $f = $this->fields;
4473 $this->MoveNext();
4475 $this->fields = $f;
4476 $this->EOF = false;
4477 return true;
4482 * Move to next record in the recordset.
4484 * @return bool true if there still rows available, or false if there are no more rows (EOF).
4486 function MoveNext() {
4487 if (!$this->EOF) {
4488 $this->_currentRow++;
4489 if ($this->_fetch()) {
4490 return true;
4493 $this->EOF = true;
4494 /* -- tested error handling when scrolling cursor -- seems useless.
4495 $conn = $this->connection;
4496 if ($conn && $conn->raiseErrorFn && ($errno = $conn->ErrorNo())) {
4497 $fn = $conn->raiseErrorFn;
4498 $fn($conn->databaseType,'MOVENEXT',$errno,$conn->ErrorMsg().' ('.$this->sql.')',$conn->host,$conn->database);
4501 return false;
4506 * Random access to a specific row in the recordset. Some databases do not support
4507 * access to previous rows in the databases (no scrolling backwards).
4509 * @param int $rowNumber is the row to move to (0-based)
4511 * @return bool true if there still rows available, or false if there are no more rows (EOF).
4513 function Move($rowNumber = 0) {
4514 $this->EOF = false;
4515 if ($rowNumber == $this->_currentRow) {
4516 return true;
4518 if ($rowNumber >= $this->_numOfRows) {
4519 if ($this->_numOfRows != -1) {
4520 $rowNumber = $this->_numOfRows-2;
4524 if ($rowNumber < 0) {
4525 $this->EOF = true;
4526 return false;
4529 if ($this->canSeek) {
4530 if ($this->_seek($rowNumber)) {
4531 $this->_currentRow = $rowNumber;
4532 if ($this->_fetch()) {
4533 return true;
4535 } else {
4536 $this->EOF = true;
4537 return false;
4539 } else {
4540 if ($rowNumber < $this->_currentRow) {
4541 return false;
4543 while (! $this->EOF && $this->_currentRow < $rowNumber) {
4544 $this->_currentRow++;
4546 if (!$this->_fetch()) {
4547 $this->EOF = true;
4550 return !($this->EOF);
4553 $this->fields = false;
4554 $this->EOF = true;
4555 return false;
4559 * Adjusts the result pointer to an arbitrary row in the result.
4561 * @param int $row The row to seek to.
4563 * @return bool False if the recordset contains no rows, otherwise true.
4565 function _seek($row) {}
4568 * Get the value of a field in the current row by column name.
4569 * Will not work if ADODB_FETCH_MODE is set to ADODB_FETCH_NUM.
4571 * @param string $colname is the field to access
4573 * @return mixed the value of $colname column
4575 function Fields($colname) {
4576 return $this->fields[$colname];
4580 * Defines the function to use for table fields case conversion
4581 * depending on ADODB_ASSOC_CASE
4583 * @param int [$case]
4585 * @return string strtolower/strtoupper or false if no conversion needed
4587 protected function AssocCaseConvertFunction($case = ADODB_ASSOC_CASE) {
4588 switch($case) {
4589 case ADODB_ASSOC_CASE_UPPER:
4590 return 'strtoupper';
4591 case ADODB_ASSOC_CASE_LOWER:
4592 return 'strtolower';
4593 case ADODB_ASSOC_CASE_NATIVE:
4594 default:
4595 return false;
4600 * Builds the bind array associating keys to recordset fields
4602 * @param int [$upper] Case for the array keys, defaults to uppercase
4603 * (see ADODB_ASSOC_CASE_xxx constants)
4605 function GetAssocKeys($upper = ADODB_ASSOC_CASE) {
4606 if ($this->bind) {
4607 return;
4609 $this->bind = array();
4611 // Define case conversion function for ASSOC fetch mode
4612 $fn_change_case = $this->AssocCaseConvertFunction($upper);
4614 // Build the bind array
4615 for ($i=0; $i < $this->_numOfFields; $i++) {
4616 $o = $this->FetchField($i);
4618 // Set the array's key
4619 if(is_numeric($o->name)) {
4620 // Just use the field ID
4621 $key = $i;
4623 elseif( $fn_change_case ) {
4624 // Convert the key's case
4625 $key = $fn_change_case($o->name);
4627 else {
4628 $key = $o->name;
4631 $this->bind[$key] = $i;
4636 * Use associative array to get fields array for databases that do not support
4637 * associative arrays. Submitted by Paolo S. Asioli paolo.asioli#libero.it
4639 * @param int $upper Case for the array keys, defaults to uppercase
4640 * (see ADODB_ASSOC_CASE_xxx constants)
4641 * @return array
4643 function GetRowAssoc($upper = ADODB_ASSOC_CASE) {
4644 $record = array();
4645 $this->GetAssocKeys($upper);
4647 foreach($this->bind as $k => $v) {
4648 if( array_key_exists( $v, $this->fields ) ) {
4649 $record[$k] = $this->fields[$v];
4650 } elseif( array_key_exists( $k, $this->fields ) ) {
4651 $record[$k] = $this->fields[$k];
4652 } else {
4653 # This should not happen... trigger error ?
4654 $record[$k] = null;
4657 return $record;
4661 * Clean up recordset
4663 * @return bool
4665 function Close() {
4666 // free connection object - this seems to globally free the object
4667 // and not merely the reference, so don't do this...
4668 // $this->connection = false;
4669 if (!$this->_closed) {
4670 $this->_closed = true;
4671 return $this->_close();
4672 } else
4673 return true;
4677 * Number of rows in recordset.
4679 * @return int Number of rows or -1 if this is not supported
4681 function recordCount() {
4682 return $this->_numOfRows;
4686 * If we are using PageExecute(), this will return the maximum possible rows
4687 * that can be returned when paging a recordset.
4689 * @return int
4691 function MaxRecordCount() {
4692 return ($this->_maxRecordCount) ? $this->_maxRecordCount : $this->recordCount();
4696 * Number of rows in recordset.
4697 * Alias for {@see recordCount()}
4699 * @return int Number of rows or -1 if this is not supported
4701 function rowCount() {
4702 return $this->recordCount();
4706 * Portable RecordCount.
4708 * Be aware of possible problems in multiuser environments.
4709 * For better speed the table must be indexed by the condition.
4710 * Heavy test this before deploying.
4712 * @param string $table
4713 * @param string $condition
4715 * @return int Number of records from a previous SELECT. All databases support this.
4717 * @author Pablo Roca <pabloroca@mvps.org>
4719 function PO_RecordCount($table="", $condition="") {
4721 $lnumrows = $this->_numOfRows;
4722 // the database doesn't support native recordcount, so we do a workaround
4723 if ($lnumrows == -1 && $this->connection) {
4724 IF ($table) {
4725 if ($condition) {
4726 $condition = " WHERE " . $condition;
4728 $resultrows = $this->connection->Execute("SELECT COUNT(*) FROM $table $condition");
4729 if ($resultrows) {
4730 $lnumrows = reset($resultrows->fields);
4734 return $lnumrows;
4739 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
4741 function CurrentRow() {
4742 return $this->_currentRow;
4746 * synonym for CurrentRow -- for ADO compat
4748 * @return the current row in the recordset. If at EOF, will return the last row. 0-based.
4750 function AbsolutePosition() {
4751 return $this->_currentRow;
4755 * @return the number of columns in the recordset. Some databases will set this to 0
4756 * if no records are returned, others will return the number of columns in the query.
4758 function FieldCount() {
4759 return $this->_numOfFields;
4763 * Get a Field's metadata from database.
4765 * Must be defined by child class.
4767 * @param int $fieldOffset
4769 * @return ADOFieldObject|false
4771 function fetchField($fieldOffset)
4773 return false;
4777 * Get Field metadata for all the recordset's columns in an array.
4779 * @return ADOFieldObject[]
4781 function fieldTypesArray() {
4782 if (empty($this->fieldObjectsCache)) {
4783 for ($i = 0; $i < $this->_numOfFields; $i++) {
4784 $this->fieldObjectsCache[] = $this->fetchField($i);
4787 return $this->fieldObjectsCache;
4791 * Return the fields array of the current row as an object for convenience.
4792 * The default case is lowercase field names.
4794 * @return the object with the properties set to the fields of the current row
4796 function FetchObj() {
4797 return $this->FetchObject(false);
4801 * Return the fields array of the current row as an object for convenience.
4802 * The default case is uppercase.
4804 * @param bool $isUpper to set the object property names to uppercase
4806 * @return ADOFetchObj The object with properties set to the fields of the current row
4808 function FetchObject($isUpper=true) {
4809 if (empty($this->_obj)) {
4810 $this->_obj = new ADOFetchObj();
4811 $this->_names = array();
4812 for ($i=0; $i <$this->_numOfFields; $i++) {
4813 $f = $this->FetchField($i);
4814 $this->_names[] = $f->name;
4817 $o = clone($this->_obj);
4819 for ($i=0; $i <$this->_numOfFields; $i++) {
4820 $name = $this->_names[$i];
4821 if ($isUpper) {
4822 $n = strtoupper($name);
4823 } else {
4824 $n = $name;
4827 $o->$n = $this->Fields($name);
4829 return $o;
4833 * Return the fields array of the current row as an object for convenience.
4834 * The default is lower-case field names.
4836 * @return ADOFetchObj|false The object with properties set to the fields of the current row
4837 * or false if EOF.
4839 * Fixed bug reported by tim@orotech.net
4841 function FetchNextObj() {
4842 return $this->FetchNextObject(false);
4847 * Return the fields array of the current row as an object for convenience.
4848 * The default is upper case field names.
4850 * @param bool $isUpper to set the object property names to uppercase
4852 * @return ADOFetchObj|false The object with properties set to the fields of the current row
4853 * or false if EOF.
4855 * Fixed bug reported by tim@orotech.net
4857 function FetchNextObject($isUpper=true) {
4858 $o = false;
4859 if ($this->_numOfRows != 0 && !$this->EOF) {
4860 $o = $this->FetchObject($isUpper);
4861 $this->_currentRow++;
4862 if ($this->_fetch()) {
4863 return $o;
4866 $this->EOF = true;
4867 return $o;
4871 * Get the ADOdb metatype.
4873 * Many databases use different names for the same type, so we transform
4874 * the native type to our standardised one, which uses 1 character codes.
4875 * @see https://adodb.org/dokuwiki/doku.php?id=v5:dictionary:dictionary_index#portable_data_types
4877 * @param string|ADOFieldObject $t Native type (usually ADOFieldObject->type)
4878 * It is also possible to provide an
4879 * ADOFieldObject here.
4880 * @param int $len The field's maximum length. This is because we treat
4881 * character fields bigger than a certain size as a 'B' (blob).
4882 * @param ADOFieldObject $fieldObj Field object returned by the database driver;
4883 * can hold additional info (eg. primary_key for mysql).
4885 * @return string The ADOdb Standard type
4887 function metaType($t, $len = -1, $fieldObj = false) {
4888 if ($t instanceof ADOFieldObject) {
4889 $fieldObj = $t;
4890 $t = $fieldObj->type;
4891 $len = $fieldObj->max_length;
4894 // changed in 2.32 to hashing instead of switch stmt for speed...
4895 static $typeMap = array(
4896 'VARCHAR' => 'C',
4897 'VARCHAR2' => 'C',
4898 'CHAR' => 'C',
4899 'C' => 'C',
4900 'STRING' => 'C',
4901 'NCHAR' => 'C',
4902 'NVARCHAR' => 'C',
4903 'VARYING' => 'C',
4904 'BPCHAR' => 'C',
4905 'CHARACTER' => 'C',
4906 'INTERVAL' => 'C', # Postgres
4907 'MACADDR' => 'C', # postgres
4908 'VAR_STRING' => 'C', # mysql
4910 'LONGCHAR' => 'X',
4911 'TEXT' => 'X',
4912 'NTEXT' => 'X',
4913 'M' => 'X',
4914 'X' => 'X',
4915 'CLOB' => 'X',
4916 'NCLOB' => 'X',
4917 'LVARCHAR' => 'X',
4919 'BLOB' => 'B',
4920 'IMAGE' => 'B',
4921 'BINARY' => 'B',
4922 'VARBINARY' => 'B',
4923 'LONGBINARY' => 'B',
4924 'B' => 'B',
4926 'YEAR' => 'D', // mysql
4927 'DATE' => 'D',
4928 'D' => 'D',
4930 'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
4932 'SMALLDATETIME' => 'T',
4933 'TIME' => 'T',
4934 'TIMESTAMP' => 'T',
4935 'DATETIME' => 'T',
4936 'DATETIME2' => 'T',
4937 'TIMESTAMPTZ' => 'T',
4938 'T' => 'T',
4939 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
4941 'BOOL' => 'L',
4942 'BOOLEAN' => 'L',
4943 'BIT' => 'L',
4944 'L' => 'L',
4946 'COUNTER' => 'R',
4947 'R' => 'R',
4948 'SERIAL' => 'R', // ifx
4949 'INT IDENTITY' => 'R',
4951 'INT' => 'I',
4952 'INT2' => 'I',
4953 'INT4' => 'I',
4954 'INT8' => 'I',
4955 'INTEGER' => 'I',
4956 'INTEGER UNSIGNED' => 'I',
4957 'SHORT' => 'I',
4958 'TINYINT' => 'I',
4959 'SMALLINT' => 'I',
4960 'I' => 'I',
4962 'LONG' => 'N', // interbase is numeric, oci8 is blob
4963 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
4964 'DECIMAL' => 'N',
4965 'DEC' => 'N',
4966 'REAL' => 'N',
4967 'DOUBLE' => 'N',
4968 'DOUBLE PRECISION' => 'N',
4969 'SMALLFLOAT' => 'N',
4970 'FLOAT' => 'N',
4971 'NUMBER' => 'N',
4972 'NUM' => 'N',
4973 'NUMERIC' => 'N',
4974 'MONEY' => 'N',
4976 ## informix 9.2
4977 'SQLINT' => 'I',
4978 'SQLSERIAL' => 'I',
4979 'SQLSMINT' => 'I',
4980 'SQLSMFLOAT' => 'N',
4981 'SQLFLOAT' => 'N',
4982 'SQLMONEY' => 'N',
4983 'SQLDECIMAL' => 'N',
4984 'SQLDATE' => 'D',
4985 'SQLVCHAR' => 'C',
4986 'SQLCHAR' => 'C',
4987 'SQLDTIME' => 'T',
4988 'SQLINTERVAL' => 'N',
4989 'SQLBYTES' => 'B',
4990 'SQLTEXT' => 'X',
4991 ## informix 10
4992 "SQLINT8" => 'I8',
4993 "SQLSERIAL8" => 'I8',
4994 "SQLNCHAR" => 'C',
4995 "SQLNVCHAR" => 'C',
4996 "SQLLVARCHAR" => 'X',
4997 "SQLBOOL" => 'L'
5000 $t = strtoupper($t);
5001 $tmap = (isset($typeMap[$t])) ? $typeMap[$t] : ADODB_DEFAULT_METATYPE;
5002 switch ($tmap) {
5003 case 'C':
5004 // is the char field is too long, return as text field...
5005 if ($this->blobSize >= 0) {
5006 if ($len > $this->blobSize) {
5007 return 'X';
5009 } else if ($len > 250) {
5010 return 'X';
5012 return 'C';
5014 case 'I':
5015 if (!empty($fieldObj->primary_key)) {
5016 return 'R';
5018 return 'I';
5020 case false:
5021 return 'N';
5023 case 'B':
5024 if (isset($fieldObj->binary)) {
5025 return ($fieldObj->binary) ? 'B' : 'X';
5027 return 'B';
5029 case 'D':
5030 if (!empty($this->connection) && !empty($this->connection->datetime)) {
5031 return 'T';
5033 return 'D';
5035 default:
5036 if ($t == 'LONG' && $this->dataProvider == 'oci8') {
5037 return 'B';
5039 return $tmap;
5044 * Convert case of field names associative array, if needed
5045 * @return void
5047 protected function _updatefields()
5049 if( empty($this->fields)) {
5050 return;
5053 // Determine case conversion function
5054 $fn_change_case = $this->AssocCaseConvertFunction();
5055 if(!$fn_change_case) {
5056 // No conversion needed
5057 return;
5060 $arr = array();
5062 // Change the case
5063 foreach($this->fields as $k => $v) {
5064 if (!is_integer($k)) {
5065 $k = $fn_change_case($k);
5067 $arr[$k] = $v;
5069 $this->fields = $arr;
5072 function _close() {}
5075 * set/returns the current recordset page when paginating
5076 * @param int $page
5077 * @return int
5079 function absolutePage($page=-1) {
5080 if ($page != -1) {
5081 $this->_currentPage = $page;
5083 return $this->_currentPage;
5087 * set/returns the status of the atFirstPage flag when paginating
5088 * @param bool $status
5089 * @return bool
5091 function AtFirstPage($status=false) {
5092 if ($status != false) {
5093 $this->_atFirstPage = $status;
5095 return $this->_atFirstPage;
5099 * @param bool $page
5100 * @return bool
5102 function LastPageNo($page = false) {
5103 if ($page != false) {
5104 $this->_lastPageNo = $page;
5106 return $this->_lastPageNo;
5110 * set/returns the status of the atLastPage flag when paginating
5111 * @param bool $status
5112 * @return bool
5114 function AtLastPage($status=false) {
5115 if ($status != false) {
5116 $this->_atLastPage = $status;
5118 return $this->_atLastPage;
5121 } // end class ADORecordSet
5123 //==============================================================================================
5124 // CLASS ADORecordSet_array
5125 //==============================================================================================
5128 * This class encapsulates the concept of a recordset created in memory
5129 * as an array. This is useful for the creation of cached recordsets.
5131 * Note that the constructor is different from the standard ADORecordSet
5133 class ADORecordSet_array extends ADORecordSet
5135 var $databaseType = 'array';
5137 var $_array; // holds the 2-dimensional data array
5138 var $_types; // the array of types of each column (C B I L M)
5139 var $_colnames; // names of each column in array
5140 var $_skiprow1; // skip 1st row because it holds column names
5141 var $_fieldobjects; // holds array of field objects
5142 var $canSeek = true;
5143 var $affectedrows = false;
5144 var $insertid = false;
5145 var $sql = '';
5146 var $compat = false;
5149 * Constructor
5151 * The parameters passed to this recordset are always fake because
5152 * this class does not use the queryID
5154 * @param resource|int $queryID Ignored
5155 * @param int|bool $mode The ADODB_FETCH_MODE value
5157 function __construct($queryID, $mode=false) {
5158 global $ADODB_FETCH_MODE,$ADODB_COMPAT_FETCH;
5160 // fetch() on EOF does not delete $this->fields
5161 $this->compat = !empty($ADODB_COMPAT_FETCH);
5162 parent::__construct($queryID); // fake queryID
5163 $this->fetchMode = $ADODB_FETCH_MODE;
5168 * Setup the array.
5170 * @param array is a 2-dimensional array holding the data.
5171 * The first row should hold the column names
5172 * unless parameter $colnames is used.
5173 * @param typearr holds an array of types. These are the same types
5174 * used in MetaTypes (C,B,L,I,N).
5175 * @param string[]|false [$colnames] array of column names. If set, then the first row of
5176 * $array should not hold the column names.
5178 function InitArray($array,$typearr,$colnames=false) {
5179 $this->_array = $array;
5180 $this->_types = $typearr;
5181 if ($colnames) {
5182 $this->_skiprow1 = false;
5183 $this->_colnames = $colnames;
5184 } else {
5185 $this->_skiprow1 = true;
5186 $this->_colnames = $array[0];
5188 $this->Init();
5191 * Setup the Array and datatype file objects
5193 * @param array $array 2-dimensional array holding the data
5194 * The first row should hold the column names
5195 * unless parameter $colnames is used.
5196 * @param array $fieldarr Array of ADOFieldObject's.
5198 function InitArrayFields(&$array,&$fieldarr) {
5199 $this->_array = $array;
5200 $this->_skiprow1= false;
5201 if ($fieldarr) {
5202 $this->_fieldobjects = $fieldarr;
5204 $this->Init();
5208 * @param int [$nRows]
5209 * @return array
5211 function GetArray($nRows=-1) {
5212 if ($nRows == -1 && $this->_currentRow <= 0 && !$this->_skiprow1) {
5213 return $this->_array;
5214 } else {
5215 return ADORecordSet::GetArray($nRows);
5219 function _initrs() {
5220 $this->_numOfRows = sizeof($this->_array);
5221 if ($this->_skiprow1) {
5222 $this->_numOfRows -= 1;
5225 $this->_numOfFields = (isset($this->_fieldobjects))
5226 ? sizeof($this->_fieldobjects)
5227 : sizeof($this->_types);
5231 * Use associative array to get fields array
5233 * @param string $colname
5234 * @return mixed
5236 function Fields($colname) {
5237 $mode = isset($this->adodbFetchMode) ? $this->adodbFetchMode : $this->fetchMode;
5239 if ($mode & ADODB_FETCH_ASSOC) {
5240 if (!isset($this->fields[$colname]) && !is_null($this->fields[$colname])) {
5241 $colname = strtolower($colname);
5243 return $this->fields[$colname];
5245 if (!$this->bind) {
5246 $this->bind = array();
5247 for ($i=0; $i < $this->_numOfFields; $i++) {
5248 $o = $this->FetchField($i);
5249 $this->bind[strtoupper($o->name)] = $i;
5252 return $this->fields[$this->bind[strtoupper($colname)]];
5256 * @param int [$fieldOffset]
5258 * @return \ADOFieldObject
5260 function FetchField($fieldOffset = -1) {
5261 if (isset($this->_fieldobjects)) {
5262 return $this->_fieldobjects[$fieldOffset];
5264 $o = new ADOFieldObject();
5265 $o->name = $this->_colnames[$fieldOffset];
5266 $o->type = $this->_types[$fieldOffset];
5267 $o->max_length = -1; // length not known
5269 return $o;
5273 * @param int $row
5274 * @return bool
5276 function _seek($row) {
5277 if (sizeof($this->_array) && 0 <= $row && $row < $this->_numOfRows) {
5278 $this->_currentRow = $row;
5279 if ($this->_skiprow1) {
5280 $row += 1;
5282 $this->fields = $this->_array[$row];
5283 return true;
5285 return false;
5289 * @return bool
5291 function MoveNext() {
5292 if (!$this->EOF) {
5293 $this->_currentRow++;
5295 $pos = $this->_currentRow;
5297 if ($this->_numOfRows <= $pos) {
5298 if (!$this->compat) {
5299 $this->fields = false;
5301 } else {
5302 if ($this->_skiprow1) {
5303 $pos += 1;
5305 $this->fields = $this->_array[$pos];
5306 return true;
5308 $this->EOF = true;
5311 return false;
5315 * @return bool
5317 function _fetch() {
5318 $pos = $this->_currentRow;
5320 if ($this->_numOfRows <= $pos) {
5321 if (!$this->compat) {
5322 $this->fields = false;
5324 return false;
5326 if ($this->_skiprow1) {
5327 $pos += 1;
5329 $this->fields = $this->_array[$pos];
5330 return true;
5333 function _close() {
5334 return true;
5337 } // ADORecordSet_array
5339 //==============================================================================================
5340 // HELPER FUNCTIONS
5341 //==============================================================================================
5344 * Synonym for ADOLoadCode. Private function. Do not use.
5346 * @deprecated
5348 function ADOLoadDB($dbType) {
5349 return ADOLoadCode($dbType);
5353 * Load the code for a specific database driver. Private function. Do not use.
5355 function ADOLoadCode($dbType) {
5356 global $ADODB_LASTDB;
5358 if (!$dbType) {
5359 return false;
5361 $db = strtolower($dbType);
5362 switch ($db) {
5363 case 'ado':
5364 $db = 'ado5';
5365 $class = 'ado';
5366 break;
5368 case 'ifx':
5369 case 'maxsql':
5370 $class = $db = 'mysqlt';
5371 break;
5373 case 'pgsql':
5374 case 'postgres':
5375 $class = $db = 'postgres9';
5376 break;
5378 case 'mysql':
5379 // mysql extension removed in PHP 7.0 - automatically switch to mysqli
5380 $class = $db = 'mysqli';
5381 break;
5383 default:
5384 if (substr($db, 0, 4) === 'pdo_') {
5385 ADOConnection::outp("Invalid database type: $db");
5386 return false;
5389 $class = $db;
5390 break;
5393 $file = "drivers/adodb-$db.inc.php";
5394 @include_once(ADODB_DIR . '/' . $file);
5395 $ADODB_LASTDB = $class;
5396 if (class_exists("ADODB_" . $class)) {
5397 return $class;
5400 //ADOConnection::outp(adodb_pr(get_declared_classes(),true));
5401 if (!file_exists($file)) {
5402 ADOConnection::outp("Missing file: $file");
5403 } else {
5404 ADOConnection::outp("Syntax error in file: $file");
5406 return false;
5410 * Synonym for ADONewConnection for people like me who cannot remember the correct name
5412 * @param string [$db]
5414 * @return ADOConnection|false
5416 function NewADOConnection($db='') {
5417 return ADONewConnection($db);
5421 * Instantiate a new Connection class for a specific database driver.
5423 * @param string $db Database Connection object to create. If undefined,
5424 * use the last database driver that was loaded by ADOLoadCode().
5426 * @return ADOConnection|false The freshly created instance of the Connection class
5427 * or false in case of error.
5429 function ADONewConnection($db='') {
5430 global $ADODB_NEWCONNECTION, $ADODB_LASTDB;
5432 if (!defined('ADODB_ASSOC_CASE')) {
5433 define('ADODB_ASSOC_CASE', ADODB_ASSOC_CASE_NATIVE);
5437 * Are there special characters in the dsn password
5438 * that disrupt parse_url
5440 $needsSpecialCharacterHandling = false;
5442 $errorfn = (defined('ADODB_ERROR_HANDLER')) ? ADODB_ERROR_HANDLER : false;
5443 if (($at = strpos($db,'://')) !== FALSE) {
5444 $origdsn = $db;
5445 $fakedsn = 'fake'.substr($origdsn,$at);
5446 if (($at2 = strpos($origdsn,'@/')) !== FALSE) {
5447 // special handling of oracle, which might not have host
5448 $fakedsn = str_replace('@/','@adodb-fakehost/',$fakedsn);
5451 if ((strpos($origdsn, 'sqlite')) !== FALSE && stripos($origdsn, '%2F') === FALSE) {
5452 // special handling for SQLite, it only might have the path to the database file.
5453 // If you try to connect to a SQLite database using a dsn
5454 // like 'sqlite:///path/to/database', the 'parse_url' php function
5455 // will throw you an exception with a message such as "unable to parse url"
5456 list($scheme, $path) = explode('://', $origdsn);
5457 $dsna['scheme'] = $scheme;
5458 if ($qmark = strpos($path,'?')) {
5459 $dsn['query'] = substr($path,$qmark+1);
5460 $path = substr($path,0,$qmark);
5462 $dsna['path'] = '/' . urlencode($path);
5463 } else {
5465 * Stop # character breaking parse_url
5467 $cFakedsn = str_replace('#','\035',$fakedsn);
5468 if (strcmp($fakedsn,$cFakedsn) != 0)
5471 * There is a # in the string
5473 $needsSpecialCharacterHandling = true;
5476 * This allows us to successfully parse the url
5478 $fakedsn = $cFakedsn;
5482 $dsna = parse_url($fakedsn);
5485 if (!$dsna) {
5486 return false;
5488 $dsna['scheme'] = substr($origdsn,0,$at);
5489 if ($at2 !== FALSE) {
5490 $dsna['host'] = '';
5493 if (strncmp($origdsn,'pdo',3) == 0) {
5494 $sch = explode('_',$dsna['scheme']);
5495 if (sizeof($sch)>1) {
5496 $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
5497 if ($sch[1] == 'sqlite') {
5498 $dsna['host'] = rawurlencode($sch[1].':'.rawurldecode($dsna['host']));
5499 } else {
5500 $dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
5502 $dsna['scheme'] = 'pdo';
5506 $db = @$dsna['scheme'];
5507 if (!$db) {
5508 return false;
5511 $dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
5512 $dsna['user'] = isset($dsna['user']) ? rawurldecode($dsna['user']) : '';
5513 $dsna['pass'] = isset($dsna['pass']) ? rawurldecode($dsna['pass']) : '';
5514 $dsna['path'] = isset($dsna['path']) ? rawurldecode(substr($dsna['path'],1)) : ''; # strip off initial /
5516 if ($needsSpecialCharacterHandling)
5519 * Revert back to the original string
5521 $dsna = str_replace('\035','#',$dsna);
5524 if (isset($dsna['query'])) {
5525 $opt1 = explode('&',$dsna['query']);
5526 foreach($opt1 as $k => $v) {
5527 $arr = explode('=',$v);
5528 $opt[$arr[0]] = isset($arr[1]) ? rawurldecode($arr[1]) : 1;
5530 } else {
5531 $opt = array();
5536 * phptype: Database backend used in PHP (mysql, odbc etc.)
5537 * dbsyntax: Database used with regards to SQL syntax etc.
5538 * protocol: Communication protocol to use (tcp, unix etc.)
5539 * hostspec: Host specification (hostname[:port])
5540 * database: Database to use on the DBMS server
5541 * username: User name for login
5542 * password: Password for login
5544 if (!empty($ADODB_NEWCONNECTION)) {
5545 $obj = $ADODB_NEWCONNECTION($db);
5549 if(empty($obj)) {
5551 if (!isset($ADODB_LASTDB)) {
5552 $ADODB_LASTDB = '';
5554 if (empty($db)) {
5555 $db = $ADODB_LASTDB;
5557 if ($db != $ADODB_LASTDB) {
5558 $db = ADOLoadCode($db);
5561 if (!$db) {
5562 if (isset($origdsn)) {
5563 $db = $origdsn;
5565 if ($errorfn) {
5566 // raise an error
5567 $ignore = false;
5568 $errorfn('ADONewConnection', 'ADONewConnection', -998,
5569 "could not load the database driver for '$db'",
5570 $db,false,$ignore);
5571 } else {
5572 ADOConnection::outp( "<p>ADONewConnection: Unable to load database driver '$db'</p>",false);
5574 return false;
5577 $cls = 'ADODB_'.$db;
5578 if (!class_exists($cls)) {
5579 adodb_backtrace();
5580 return false;
5583 $obj = new $cls();
5586 # constructor should not fail
5587 if ($obj) {
5588 if ($errorfn) {
5589 $obj->raiseErrorFn = $errorfn;
5591 if (isset($dsna)) {
5592 if (isset($dsna['port'])) {
5593 $obj->port = $dsna['port'];
5595 foreach($opt as $k => $v) {
5596 switch(strtolower($k)) {
5597 case 'new':
5598 $nconnect = true; $persist = true; break;
5599 case 'persist':
5600 case 'persistent': $persist = $v; break;
5601 case 'debug': $obj->debug = (integer) $v; break;
5602 #ibase
5603 case 'role': $obj->role = $v; break;
5604 case 'dialect': $obj->dialect = (integer) $v; break;
5605 case 'charset': $obj->charset = $v; $obj->charSet=$v; break;
5606 case 'buffers': $obj->buffers = $v; break;
5607 case 'fetchmode': $obj->SetFetchMode($v); break;
5608 #ado
5609 case 'charpage': $obj->charPage = $v; break;
5610 #mysql, mysqli
5611 case 'clientflags': $obj->clientFlags = $v; break;
5612 #mysql, mysqli, postgres
5613 case 'port': $obj->port = $v; break;
5614 #mysqli
5615 case 'socket': $obj->socket = $v; break;
5616 #oci8
5617 case 'nls_date_format': $obj->NLS_DATE_FORMAT = $v; break;
5618 case 'cachesecs': $obj->cacheSecs = $v; break;
5619 case 'memcache':
5620 $varr = explode(':',$v);
5621 $vlen = sizeof($varr);
5622 if ($vlen == 0) {
5623 break;
5625 $obj->memCache = true;
5626 $obj->memCacheHost = explode(',',$varr[0]);
5627 if ($vlen == 1) {
5628 break;
5630 $obj->memCachePort = $varr[1];
5631 if ($vlen == 2) {
5632 break;
5634 $obj->memCacheCompress = $varr[2] ? true : false;
5635 break;
5638 if (empty($persist)) {
5639 $ok = $obj->Connect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
5640 } else if (empty($nconnect)) {
5641 $ok = $obj->PConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
5642 } else {
5643 $ok = $obj->NConnect($dsna['host'], $dsna['user'], $dsna['pass'], $dsna['path']);
5646 if (!$ok) {
5647 return false;
5651 return $obj;
5656 // $perf == true means called by NewPerfMonitor(), otherwise for data dictionary
5657 function _adodb_getdriver($provider,$drivername,$perf=false) {
5658 switch ($provider) {
5659 case 'odbtp':
5660 if (strncmp('odbtp_',$drivername,6)==0) {
5661 return substr($drivername,6);
5663 case 'odbc' :
5664 if (strncmp('odbc_',$drivername,5)==0) {
5665 return substr($drivername,5);
5667 case 'ado' :
5668 if (strncmp('ado_',$drivername,4)==0) {
5669 return substr($drivername,4);
5671 case 'native':
5672 break;
5673 default:
5674 return $provider;
5677 switch($drivername) {
5678 case 'mysqlt':
5679 case 'mysqli':
5680 $drivername='mysql';
5681 break;
5682 case 'postgres7':
5683 case 'postgres8':
5684 $drivername = 'postgres';
5685 break;
5686 case 'firebird15':
5687 $drivername = 'firebird';
5688 break;
5689 case 'oracle':
5690 $drivername = 'oci8';
5691 break;
5692 case 'access':
5693 if ($perf) {
5694 $drivername = '';
5696 break;
5697 case 'db2' :
5698 case 'sapdb' :
5699 break;
5700 default:
5701 $drivername = 'generic';
5702 break;
5704 return $drivername;
5707 function NewPerfMonitor(&$conn) {
5708 $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType,true);
5709 if (!$drivername || $drivername == 'generic') {
5710 return false;
5712 include_once(ADODB_DIR.'/adodb-perf.inc.php');
5713 @include_once(ADODB_DIR."/perf/perf-$drivername.inc.php");
5714 $class = "Perf_$drivername";
5715 if (!class_exists($class)) {
5716 return false;
5719 return new $class($conn);
5723 * Get a new Data Dictionary object for the connection.
5725 * @param ADOConnection $conn
5726 * @param string $drivername
5728 * @return ADODB_DataDict|false
5730 function newDataDictionary(&$conn, $drivername='') {
5731 if (!$drivername) {
5732 $drivername = _adodb_getdriver($conn->dataProvider,$conn->databaseType);
5735 include_once(ADODB_DIR.'/adodb-lib.inc.php');
5736 include_once(ADODB_DIR.'/adodb-datadict.inc.php');
5737 $path = ADODB_DIR."/datadict/datadict-$drivername.inc.php";
5739 if (!file_exists($path)) {
5740 ADOConnection::outp("Dictionary driver '$path' not available");
5741 return false;
5743 include_once($path);
5744 $class = "ADODB2_$drivername";
5745 $dict = new $class();
5746 $dict->dataProvider = $conn->dataProvider;
5747 $dict->connection = $conn;
5748 $dict->upperName = strtoupper($drivername);
5749 $dict->quote = $conn->nameQuote;
5750 if (!empty($conn->_connectionID)) {
5751 $dict->serverInfo = $conn->ServerInfo();
5754 return $dict;
5758 * Perform a print_r, with pre tags for better formatting.
5760 function adodb_pr($var,$as_string=false) {
5761 if ($as_string) {
5762 ob_start();
5765 if (isset($_SERVER['HTTP_USER_AGENT'])) {
5766 echo " <pre>\n";print_r($var);echo "</pre>\n";
5767 } else {
5768 print_r($var);
5771 if ($as_string) {
5772 $s = ob_get_contents();
5773 ob_end_clean();
5774 return $s;
5779 * Perform a stack-crawl and pretty print it.
5781 * @param bool $printOrArr Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
5782 * @param int $levels Number of levels to display
5783 * @param mixed $ishtml
5785 * @return string
5787 function adodb_backtrace($printOrArr=true,$levels=9999,$ishtml=null) {
5788 global $ADODB_INCLUDED_LIB;
5789 if (empty($ADODB_INCLUDED_LIB)) {
5790 include_once(ADODB_DIR.'/adodb-lib.inc.php');
5792 return _adodb_backtrace($printOrArr,$levels,0,$ishtml);