Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  /**
   3   * ADOdb PostgreSQL 6.4 driver
   4   *
   5   * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
   6   *
   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
  10   *
  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
  17   *
  18   * @copyright 2000-2013 John Lim
  19   * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
  20   */
  21  
  22  // security - hide paths
  23  if (!defined('ADODB_DIR')) die();
  24  
  25  class ADODB_postgres64 extends ADOConnection{
  26  	 var $databaseType = 'postgres64';
  27  	 var $dataProvider = 'postgres';
  28  	 var $hasInsertID = true;
  29  	 /** @var PgSql\Connection|resource|false */
  30  	 var $_resultid = false;
  31  	 var $concat_operator='||';
  32  	 var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1";
  33  	 var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%'
  34  	 	 and tablename not in ('sql_features', 'sql_implementation_info', 'sql_languages',
  35  	 	 	 'sql_packages', 'sql_sizing', 'sql_sizing_profiles')
  36  	 union
  37  	 	 select viewname,'V' from pg_views where viewname not like 'pg\_%'";
  38  	 //"select tablename from pg_tables where tablename not like 'pg_%' order by 1";
  39  	 var $isoDates = true; // accepts dates in ISO format
  40  	 var $sysDate = "CURRENT_DATE";
  41  	 var $sysTimeStamp = "CURRENT_TIMESTAMP";
  42  	 var $blobEncodeType = 'C';
  43  	 var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum
  44  	 	 FROM pg_class c, pg_attribute a,pg_type t
  45  	 	 WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and a.attname not like '....%%'
  46  	 	 AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
  47  
  48  	 // used when schema defined
  49  	 var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum
  50  	 	 FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n
  51  	 	 WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s'))
  52  	 	 and c.relnamespace=n.oid and n.nspname='%s'
  53  	 	 and a.attname not like '....%%' AND a.attnum > 0
  54  	 	 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
  55  
  56  	 // get primary key etc -- from Freek Dijkstra
  57  	 var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key
  58  	 	 FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a
  59  	 	 WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid
  60  	 	 AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum)
  61  	 	 AND a.attrelid = bc.oid AND bc.relname = '%s'";
  62  
  63  	 var $hasAffectedRows = true;
  64  	 var $hasLimit = false;	 // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10
  65  	 // below suggested by Freek Dijkstra
  66  	 var $true = 'TRUE';	 	 // string that represents TRUE for a database
  67  	 var $false = 'FALSE';	 	 // string that represents FALSE for a database
  68  	 var $fmtDate = "'Y-m-d'";	 // used by DBDate() as the default date format used by the database
  69  	 var $fmtTimeStamp = "'Y-m-d H:i:s'"; // used by DBTimeStamp as the default timestamp fmt.
  70  	 var $hasMoveFirst = true;
  71  	 var $hasGenID = true;
  72  	 var $_genIDSQL = "SELECT NEXTVAL('%s')";
  73  	 var $_genSeqSQL = "CREATE SEQUENCE %s START %s";
  74  	 var $_dropSeqSQL = "DROP SEQUENCE %s";
  75  	 var $metaDefaultsSQL = "SELECT d.adnum as num, d.adsrc as def from pg_attrdef d, pg_class c where d.adrelid=c.oid and c.relname='%s' order by d.adnum";
  76  	 var $random = 'random()';	 	 /// random function
  77  	 var $autoRollback = true; // apparently pgsql does not autorollback properly before php 4.3.4
  78  	 	 	 	 	 	 	 // http://bugs.php.net/bug.php?id=25404
  79  
  80  	 var $uniqueIisR = true;
  81  	 var $_bindInputArray = false; // requires postgresql 7.3+ and ability to modify database
  82  	 var $disableBlobs = false; // set to true to disable blob checking, resulting in 2-5% improvement in performance.
  83  
  84  	 /** @var int $_pnum Number of the last assigned query parameter {@see param()} */
  85  	 var $_pnum = 0;
  86  
  87  	 // The last (fmtTimeStamp is not entirely correct:
  88  	 // PostgreSQL also has support for time zones,
  89  	 // and writes these time in this format: "2001-03-01 18:59:26+02".
  90  	 // There is no code for the "+02" time zone information, so I just left that out.
  91  	 // I'm not familiar enough with both ADODB as well as Postgres
  92  	 // to know what the concequences are. The other values are correct (wheren't in 0.94)
  93  	 // -- Freek Dijkstra
  94  
  95  	 /**
  96  	  * Retrieve Server information.
  97  	  * In addition to server version and description, the function also returns
  98  	  * the client version.
  99  	  * @param bool $detailed If true, retrieve detailed version string (executes
 100  	  *                       a SQL query) in addition to the version number
 101  	  * @return array|bool Server info or false if version could not be retrieved
 102  	  *                    e.g. if there is no active connection
 103  	  */
 104  	function ServerInfo($detailed = true)
 105  	 {
 106  	 	 if (empty($this->version['version'])) {
 107  	 	 	 // We don't have a connection, so we can't retrieve server info
 108  	 	 	 if (!$this->_connectionID) {
 109  	 	 	 	 return false;
 110  	 	 	 }
 111  
 112  	 	 	 $version = pg_version($this->_connectionID);
 113  	 	 	 $this->version = array(
 114  	 	 	 	 // If PHP has been compiled with PostgreSQL 7.3 or lower, then
 115  	 	 	 	 // server version is not set so we use pg_parameter_status()
 116  	 	 	 	 // which includes logic to obtain values server_version
 117  	 	 	 	 'version' => isset($version['server'])
 118  	 	 	 	 	 ? $version['server']
 119  	 	 	 	 	 : pg_parameter_status($this->_connectionID, 'server_version'),
 120  	 	 	 	 'client' => $version['client'],
 121  	 	 	 	 'description' => null,
 122  	 	 	 );
 123  	 	 }
 124  	 	 if ($detailed && $this->version['description'] === null) {
 125  	 	 	 $this->version['description'] = $this->GetOne('select version()');
 126  	 	 }
 127  
 128  	 	 return $this->version;
 129  	 }
 130  
 131  	function IfNull( $field, $ifNull )
 132  	 {
 133  	 	 return " coalesce($field, $ifNull) ";
 134  	 }
 135  
 136  	 // get the last id - never tested
 137  	function pg_insert_id($tablename,$fieldname)
 138  	 {
 139  	 	 $result=pg_query($this->_connectionID, 'SELECT last_value FROM '. $tablename .'_'. $fieldname .'_seq');
 140  	 	 if ($result) {
 141  	 	 	 $arr = @pg_fetch_row($result,0);
 142  	 	 	 pg_free_result($result);
 143  	 	 	 if (isset($arr[0])) return $arr[0];
 144  	 	 }
 145  	 	 return false;
 146  	 }
 147  
 148  	 /**
 149  	  * Warning from http://www.php.net/manual/function.pg-getlastoid.php:
 150  	  * Using a OID as a unique identifier is not generally wise.
 151  	  * Unless you are very careful, you might end up with a tuple having
 152  	  * a different OID if a database must be reloaded.
 153  	  *
 154  	  * @inheritDoc
 155  	  */
 156  	protected function _insertID($table = '', $column = '')
 157  	 {
 158  	 	 if ($this->_resultid === false) return false;
 159  	 	 $oid = pg_last_oid($this->_resultid);
 160  	 	 // to really return the id, we need the table and column-name, else we can only return the oid != id
 161  	 	 return empty($table) || empty($column) ? $oid : $this->GetOne("SELECT $column FROM $table WHERE oid=".(int)$oid);
 162  	 }
 163  
 164  	function _affectedrows()
 165  	 {
 166  	 	 if ($this->_resultid === false) return false;
 167  	 	 return pg_affected_rows($this->_resultid);
 168  	 }
 169  
 170  
 171  	 /**
 172  	  * @return bool
 173  	  */
 174  	function BeginTrans()
 175  	 {
 176  	 	 if ($this->transOff) return true;
 177  	 	 $this->transCnt += 1;
 178  	 	 return pg_query($this->_connectionID, 'begin '.$this->_transmode);
 179  	 }
 180  
 181  	function RowLock($tables,$where,$col='1 as adodbignore')
 182  	 {
 183  	 	 if (!$this->transCnt) $this->BeginTrans();
 184  	 	 return $this->GetOne("select $col from $tables where $where for update");
 185  	 }
 186  
 187  	 // returns true/false.
 188  	function CommitTrans($ok=true)
 189  	 {
 190  	 	 if ($this->transOff) return true;
 191  	 	 if (!$ok) return $this->RollbackTrans();
 192  
 193  	 	 $this->transCnt -= 1;
 194  	 	 return pg_query($this->_connectionID, 'commit');
 195  	 }
 196  
 197  	 // returns true/false
 198  	function RollbackTrans()
 199  	 {
 200  	 	 if ($this->transOff) return true;
 201  	 	 $this->transCnt -= 1;
 202  	 	 return pg_query($this->_connectionID, 'rollback');
 203  	 }
 204  
 205  	function MetaTables($ttype=false,$showSchema=false,$mask=false)
 206  	 {
 207  	 	 $info = $this->ServerInfo();
 208  	 	 if ($info['version'] >= 7.3) {
 209  	 	 $this->metaTablesSQL = "
 210  	 	 	 select table_name,'T' from information_schema.tables where table_schema not in ( 'pg_catalog','information_schema')
 211  	 	 	 union
 212  	 	 	 select table_name,'V' from information_schema.views where table_schema not in ( 'pg_catalog','information_schema') ";
 213  	 	 }
 214  	 	 if ($mask) {
 215  	 	 	 $save = $this->metaTablesSQL;
 216  	 	 	 $mask = $this->qstr(strtolower($mask));
 217  	 	 	 if ($info['version']>=7.3)
 218  	 	 	 	 $this->metaTablesSQL = "
 219  	 	 	 	 	 select table_name,'T' from information_schema.tables where table_name like $mask and table_schema not in ( 'pg_catalog','information_schema')
 220  	 	 	 	 	 union
 221  	 	 	 	 	 select table_name,'V' from information_schema.views where table_name like $mask and table_schema not in ( 'pg_catalog','information_schema') ";
 222  	 	 	 else
 223  	 	 	 	 $this->metaTablesSQL = "
 224  	 	 	 	 	 select tablename,'T' from pg_tables where tablename like $mask
 225  	 	 	 	 	 union
 226  	 	 	 	 	 select viewname,'V' from pg_views where viewname like $mask";
 227  	 	 }
 228  	 	 $ret = ADOConnection::MetaTables($ttype,$showSchema);
 229  
 230  	 	 if ($mask) {
 231  	 	 	 $this->metaTablesSQL = $save;
 232  	 	 }
 233  	 	 return $ret;
 234  	 }
 235  
 236  
 237  	 /**
 238  	  * Quotes a string to be sent to the database.
 239  	  *
 240  	  * Relies on pg_escape_string()
 241  	  * @link https://adodb.org/dokuwiki/doku.php?id=v5:reference:connection:qstr
 242  	  *
 243  	  * @param string $s            The string to quote
 244  	  * @param bool   $magic_quotes This param is not used since 5.21.0.
 245  	  *                             It remains for backwards compatibility.
 246  	  *
 247  	  * @return string Quoted string
 248  	  */
 249  	function qStr($s, $magic_quotes=false)
 250  	 {
 251  	 	 if (is_bool($s)) {
 252  	 	 	 return $s ? 'true' : 'false';
 253  	 	 }
 254  
 255  	 	 if ($this->_connectionID) {
 256  	 	 	 return "'" . pg_escape_string($this->_connectionID, $s) . "'";
 257  	 	 } else {
 258  	 	 	 return "'" . pg_escape_string($s) . "'";
 259  	 	 }
 260  	 }
 261  
 262  
 263  	 // Format date column in sql string given an input format that understands Y M D
 264  	function SQLDate($fmt, $col=false)
 265  	 {
 266  	 	 if (!$col) $col = $this->sysTimeStamp;
 267  	 	 $s = 'TO_CHAR('.$col.",'";
 268  
 269  	 	 $len = strlen($fmt);
 270  	 	 for ($i=0; $i < $len; $i++) {
 271  	 	 	 $ch = $fmt[$i];
 272  	 	 	 switch($ch) {
 273  	 	 	 case 'Y':
 274  	 	 	 case 'y':
 275  	 	 	 	 $s .= 'YYYY';
 276  	 	 	 	 break;
 277  	 	 	 case 'Q':
 278  	 	 	 case 'q':
 279  	 	 	 	 $s .= 'Q';
 280  	 	 	 	 break;
 281  
 282  	 	 	 case 'M':
 283  	 	 	 	 $s .= 'Mon';
 284  	 	 	 	 break;
 285  
 286  	 	 	 case 'm':
 287  	 	 	 	 $s .= 'MM';
 288  	 	 	 	 break;
 289  	 	 	 case 'D':
 290  	 	 	 case 'd':
 291  	 	 	 	 $s .= 'DD';
 292  	 	 	 	 break;
 293  
 294  	 	 	 case 'H':
 295  	 	 	 	 $s.= 'HH24';
 296  	 	 	 	 break;
 297  
 298  	 	 	 case 'h':
 299  	 	 	 	 $s .= 'HH';
 300  	 	 	 	 break;
 301  
 302  	 	 	 case 'i':
 303  	 	 	 	 $s .= 'MI';
 304  	 	 	 	 break;
 305  
 306  	 	 	 case 's':
 307  	 	 	 	 $s .= 'SS';
 308  	 	 	 	 break;
 309  
 310  	 	 	 case 'a':
 311  	 	 	 case 'A':
 312  	 	 	 	 $s .= 'AM';
 313  	 	 	 	 break;
 314  
 315  	 	 	 case 'w':
 316  	 	 	 	 $s .= 'D';
 317  	 	 	 	 break;
 318  
 319  	 	 	 case 'l':
 320  	 	 	 	 $s .= 'DAY';
 321  	 	 	 	 break;
 322  
 323  	 	 	 case 'W':
 324  	 	 	 	 $s .= 'WW';
 325  	 	 	 	 break;
 326  
 327  	 	 	 default:
 328  	 	 	 // handle escape characters...
 329  	 	 	 	 if ($ch == '\\') {
 330  	 	 	 	 	 $i++;
 331  	 	 	 	 	 $ch = substr($fmt,$i,1);
 332  	 	 	 	 }
 333  	 	 	 	 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
 334  	 	 	 	 else $s .= '"'.$ch.'"';
 335  
 336  	 	 	 }
 337  	 	 }
 338  	 	 return $s. "')";
 339  	 }
 340  
 341  
 342  
 343  	 /*
 344  	 * Load a Large Object from a file
 345  	 * - the procedure stores the object id in the table and imports the object using
 346  	 * postgres proprietary blob handling routines
 347  	 *
 348  	 * contributed by Mattia Rossi mattia@technologist.com
 349  	 * modified for safe mode by juraj chlebec
 350  	 */
 351  	function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
 352  	 {
 353  	 	 pg_query($this->_connectionID, 'begin');
 354  
 355  	 	 $fd = fopen($path,'r');
 356  	 	 $contents = fread($fd,filesize($path));
 357  	 	 fclose($fd);
 358  
 359  	 	 $oid = pg_lo_create($this->_connectionID);
 360  	 	 $handle = pg_lo_open($this->_connectionID, $oid, 'w');
 361  	 	 pg_lo_write($handle, $contents);
 362  	 	 pg_lo_close($handle);
 363  
 364  	 	 // $oid = pg_lo_import ($path);
 365  	 	 pg_query($this->_connectionID, 'commit');
 366  	 	 $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype);
 367  	 	 $rez = !empty($rs);
 368  	 	 return $rez;
 369  	 }
 370  
 371  	 /*
 372  	 * Deletes/Unlinks a Blob from the database, otherwise it
 373  	 * will be left behind
 374  	 *
 375  	 * Returns TRUE on success or FALSE on failure.
 376  	 *
 377  	 * contributed by Todd Rogers todd#windfox.net
 378  	 */
 379  	function BlobDelete( $blob )
 380  	 {
 381  	 	 pg_query($this->_connectionID, 'begin');
 382  	 	 $result = @pg_lo_unlink($this->_connectionID, $blob);
 383  	 	 pg_query($this->_connectionID, 'commit');
 384  	 	 return( $result );
 385  	 }
 386  
 387  	 /*
 388  	 	 Heuristic - not guaranteed to work.
 389  	 */
 390  	function GuessOID($oid)
 391  	 {
 392  	 	 if (strlen($oid)>16) return false;
 393  	 	 return is_numeric($oid);
 394  	 }
 395  
 396  	 /*
 397  	 * If an OID is detected, then we use pg_lo_* to open the oid file and read the
 398  	 * real blob from the db using the oid supplied as a parameter. If you are storing
 399  	 * blobs using bytea, we autodetect and process it so this function is not needed.
 400  	 *
 401  	 * contributed by Mattia Rossi mattia@technologist.com
 402  	 *
 403  	 * see http://www.postgresql.org/idocs/index.php?largeobjects.html
 404  	 *
 405  	 * Since adodb 4.54, this returns the blob, instead of sending it to stdout. Also
 406  	 * added maxsize parameter, which defaults to $db->maxblobsize if not defined.
 407  	 */
 408  	function BlobDecode($blob,$maxsize=false,$hastrans=true)
 409  	 {
 410  	 	 if (!$this->GuessOID($blob)) return $blob;
 411  
 412  	 	 if ($hastrans) pg_query($this->_connectionID,'begin');
 413  	 	 $fd = @pg_lo_open($this->_connectionID,$blob,'r');
 414  	 	 if ($fd === false) {
 415  	 	 	 if ($hastrans) pg_query($this->_connectionID,'commit');
 416  	 	 	 return $blob;
 417  	 	 }
 418  	 	 if (!$maxsize) $maxsize = $this->maxblobsize;
 419  	 	 $realblob = @pg_lo_read($fd,$maxsize);
 420  	 	 @pg_lo_close($fd);
 421  	 	 if ($hastrans) pg_query($this->_connectionID,'commit');
 422  	 	 return $realblob;
 423  	 }
 424  
 425  	 /**
 426  	  * Encode binary value prior to DB storage.
 427  	  *
 428  	  * See https://www.postgresql.org/docs/current/static/datatype-binary.html
 429  	  *
 430  	  * NOTE: SQL string literals (input strings) must be preceded with two
 431  	  * backslashes due to the fact that they must pass through two parsers in
 432  	  * the PostgreSQL backend.
 433  	  *
 434  	  * @param string $blob
 435  	  */
 436  	function BlobEncode($blob)
 437  	 {
 438  	 	 return pg_escape_bytea($this->_connectionID, $blob);
 439  	 }
 440  
 441  	 // assumes bytea for blob, and varchar for clob
 442  	function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
 443  	 {
 444  	 	 if ($blobtype == 'CLOB') {
 445  	 	 	 return $this->Execute("UPDATE $table SET $column=" . $this->qstr($val) . " WHERE $where");
 446  	 	 }
 447  	 	 // do not use bind params which uses qstr(), as blobencode() already quotes data
 448  	 	 return $this->Execute("UPDATE $table SET $column='".$this->BlobEncode($val)."'::bytea WHERE $where");
 449  	 }
 450  
 451  	function OffsetDate($dayFraction,$date=false)
 452  	 {
 453  	 	 if (!$date) $date = $this->sysDate;
 454  	 	 else if (strncmp($date,"'",1) == 0) {
 455  	 	 	 $len = strlen($date);
 456  	 	 	 if (10 <= $len && $len <= 12) $date = 'date '.$date;
 457  	 	 	 else $date = 'timestamp '.$date;
 458  	 	 }
 459  
 460  
 461  	 	 return "($date+interval'".($dayFraction * 1440)." minutes')";
 462  	 	 #return "($date+interval'$dayFraction days')";
 463  	 }
 464  
 465  	 /**
 466  	  * Generate the SQL to retrieve MetaColumns data
 467  	  * @param string $table Table name
 468  	  * @param string $schema Schema name (can be blank)
 469  	  * @return string SQL statement to execute
 470  	  */
 471  	protected function _generateMetaColumnsSQL($table, $schema)
 472  	 {
 473  	 	 if ($schema) {
 474  	 	 	 return sprintf($this->metaColumnsSQL1, $table, $table, $schema);
 475  	 	 }
 476  	 	 else {
 477  	 	 	 return sprintf($this->metaColumnsSQL, $table, $table, $schema);
 478  	 	 }
 479  	 }
 480  
 481  	 // for schema support, pass in the $table param "$schema.$tabname".
 482  	 // converts field names to lowercase, $upper is ignored
 483  	 // see PHPLens Issue No: 14018 for more info
 484  	function MetaColumns($table,$normalize=true)
 485  	 {
 486  	 	 global $ADODB_FETCH_MODE;
 487  
 488  	 	 $schema = false;
 489  	 	 $false = false;
 490  	 	 $this->_findschema($table,$schema);
 491  
 492  	 	 if ($normalize) $table = strtolower($table);
 493  
 494  	 	 $save = $ADODB_FETCH_MODE;
 495  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 496  	 	 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
 497  
 498  	 	 $rs = $this->Execute($this->_generateMetaColumnsSQL($table, $schema));
 499  	 	 if (isset($savem)) $this->SetFetchMode($savem);
 500  	 	 $ADODB_FETCH_MODE = $save;
 501  
 502  	 	 if ($rs === false) {
 503  	 	 	 return $false;
 504  	 	 }
 505  	 	 if (!empty($this->metaKeySQL)) {
 506  	 	 	 // If we want the primary keys, we have to issue a separate query
 507  	 	 	 // Of course, a modified version of the metaColumnsSQL query using a
 508  	 	 	 // LEFT JOIN would have been much more elegant, but postgres does
 509  	 	 	 // not support OUTER JOINS. So here is the clumsy way.
 510  
 511  	 	 	 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
 512  
 513  	 	 	 $rskey = $this->Execute(sprintf($this->metaKeySQL,($table)));
 514  	 	 	 // fetch all result in once for performance.
 515  	 	 	 $keys = $rskey->GetArray();
 516  	 	 	 if (isset($savem)) $this->SetFetchMode($savem);
 517  	 	 	 $ADODB_FETCH_MODE = $save;
 518  
 519  	 	 	 $rskey->Close();
 520  	 	 	 unset($rskey);
 521  	 	 }
 522  
 523  	 	 $rsdefa = array();
 524  	 	 if (!empty($this->metaDefaultsSQL)) {
 525  	 	 	 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
 526  	 	 	 $sql = sprintf($this->metaDefaultsSQL, ($table));
 527  	 	 	 $rsdef = $this->Execute($sql);
 528  	 	 	 if (isset($savem)) $this->SetFetchMode($savem);
 529  	 	 	 $ADODB_FETCH_MODE = $save;
 530  
 531  	 	 	 if ($rsdef) {
 532  	 	 	 	 while (!$rsdef->EOF) {
 533  	 	 	 	 	 $num = $rsdef->fields['num'];
 534  	 	 	 	 	 $s = $rsdef->fields['def'];
 535  	 	 	 	 	 if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */
 536  	 	 	 	 	 	 $s = substr($s, 1);
 537  	 	 	 	 	 	 $s = substr($s, 0, strlen($s) - 1);
 538  	 	 	 	 	 }
 539  
 540  	 	 	 	 	 $rsdefa[$num] = $s;
 541  	 	 	 	 	 $rsdef->MoveNext();
 542  	 	 	 	 }
 543  	 	 	 } else {
 544  	 	 	 	 ADOConnection::outp( "==> SQL => " . $sql);
 545  	 	 	 }
 546  	 	 	 unset($rsdef);
 547  	 	 }
 548  
 549  	 	 $retarr = array();
 550  	 	 while (!$rs->EOF) {
 551  	 	 	 $fld = new ADOFieldObject();
 552  	 	 	 $fld->name = $rs->fields[0];
 553  	 	 	 $fld->type = $rs->fields[1];
 554  	 	 	 $fld->max_length = $rs->fields[2];
 555  	 	 	 $fld->attnum = $rs->fields[6];
 556  
 557  	 	 	 if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4;
 558  	 	 	 if ($fld->max_length <= 0) $fld->max_length = -1;
 559  	 	 	 if ($fld->type == 'numeric') {
 560  	 	 	 	 $fld->scale = $fld->max_length & 0xFFFF;
 561  	 	 	 	 $fld->max_length >>= 16;
 562  	 	 	 }
 563  	 	 	 // dannym
 564  	 	 	 // 5 hasdefault; 6 num-of-column
 565  	 	 	 $fld->has_default = ($rs->fields[5] == 't');
 566  	 	 	 if ($fld->has_default) {
 567  	 	 	 	 $fld->default_value = $rsdefa[$rs->fields[6]];
 568  	 	 	 }
 569  
 570  	 	 	 //Freek
 571  	 	 	 $fld->not_null = $rs->fields[4] == 't';
 572  
 573  
 574  	 	 	 // Freek
 575  	 	 	 if (is_array($keys)) {
 576  	 	 	 	 foreach($keys as $key) {
 577  	 	 	 	 	 if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't')
 578  	 	 	 	 	 	 $fld->primary_key = true;
 579  	 	 	 	 	 if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't')
 580  	 	 	 	 	 	 $fld->unique = true; // What name is more compatible?
 581  	 	 	 	 }
 582  	 	 	 }
 583  
 584  	 	 	 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
 585  	 	 	 else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld;
 586  
 587  	 	 	 $rs->MoveNext();
 588  	 	 }
 589  	 	 $rs->Close();
 590  	 	 if (empty($retarr))
 591  	 	 	 return  $false;
 592  	 	 else
 593  	 	 	 return $retarr;
 594  
 595  	 }
 596  
 597  	function param($name, $type='C')
 598  	 {
 599  	 	 if (!$name) {
 600  	 	 	 // Reset parameter number if $name is falsy
 601  	 	 	 $this->_pnum = 0;
 602  	 	 	 if ($name === false) {
 603  	 	 	 	 // and don't return placeholder if false (see #380)
 604  	 	 	 	 return '';
 605  	 	 	 }
 606  	 	 }
 607  
 608  	 	 return '$' . ++$this->_pnum;
 609  	 }
 610  
 611  	function MetaIndexes ($table, $primary = FALSE, $owner = false)
 612  	 {
 613  	 	 global $ADODB_FETCH_MODE;
 614  
 615  	 	 $schema = false;
 616  	 	 $this->_findschema($table,$schema);
 617  
 618  	 	 if ($schema) { // requires pgsql 7.3+ - pg_namespace used.
 619  	 	 	 $sql = '
 620  	 	 	 	 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
 621  	 	 	 	 FROM pg_catalog.pg_class c
 622  	 	 	 	 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
 623  	 	 	 	 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
 624  	 	 	 	 	 ,pg_namespace n
 625  	 	 	 	 WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))
 626  	 	 	 	 and c.relnamespace=c2.relnamespace
 627  	 	 	 	 and c.relnamespace=n.oid
 628  	 	 	 	 and n.nspname=\'%s\'';
 629  	 	 } else {
 630  	 	 	 $sql = '
 631  	 	 	 	 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
 632  	 	 	 	 FROM pg_catalog.pg_class c
 633  	 	 	 	 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
 634  	 	 	 	 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
 635  	 	 	 	 WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))';
 636  	 	 }
 637  
 638  	 	 if ($primary == FALSE) {
 639  	 	 	 $sql .= ' AND i.indisprimary=false;';
 640  	 	 }
 641  
 642  	 	 $save = $ADODB_FETCH_MODE;
 643  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
 644  	 	 if ($this->fetchMode !== FALSE) {
 645  	 	 	 $savem = $this->SetFetchMode(FALSE);
 646  	 	 }
 647  
 648  	 	 $rs = $this->Execute(sprintf($sql,$table,$table,$schema));
 649  	 	 if (isset($savem)) {
 650  	 	 	 $this->SetFetchMode($savem);
 651  	 	 }
 652  	 	 $ADODB_FETCH_MODE = $save;
 653  
 654  	 	 if (!is_object($rs)) {
 655  	 	 	 $false = false;
 656  	 	 	 return $false;
 657  	 	 }
 658  
 659  	 	 $col_names = $this->MetaColumnNames($table,true,true);
 660  	 	 // 3rd param is use attnum,
 661  	 	 // see https://sourceforge.net/p/adodb/bugs/45/
 662  	 	 $indexes = array();
 663  	 	 while ($row = $rs->FetchRow()) {
 664  	 	 	 $columns = array();
 665  	 	 	 foreach (explode(' ', $row[2]) as $col) {
 666  	 	 	 	 $columns[] = $col_names[$col];
 667  	 	 	 }
 668  
 669  	 	 	 $indexes[$row[0]] = array(
 670  	 	 	 	 'unique' => ($row[1] == 't'),
 671  	 	 	 	 'columns' => $columns
 672  	 	 	 );
 673  	 	 }
 674  	 	 return $indexes;
 675  	 }
 676  
 677  	 /**
 678  	  * Connect to a database.
 679  	  *
 680  	  * Examples:
 681  	  *   $db->Connect("host=host1 user=user1 password=secret port=4341");
 682  	  *   $db->Connect('host1:4341', 'user1', 'secret');
 683  	  *
 684  	  * @param string $str  pg_connect() Connection string or Hostname[:port]
 685  	  * @param string $user (Optional) The username to connect as.
 686  	  * @param string $pwd  (Optional) The password to connect with.
 687  	  * @param string $db   (Optional) The name of the database to start in when connected.
 688  	  * @param int $ctype   Connection type
 689  	  * @return bool|null   True if connected successfully, false if connection failed, or
 690  	  *                     null if the PostgreSQL extension is not loaded.
 691  	  */
 692  	function _connect($str, $user='', $pwd='', $db='', $ctype=0)
 693  	 {
 694  	 	 if (!function_exists('pg_connect')) {
 695  	 	 	 return null;
 696  	 	 }
 697  
 698  	 	 $this->_errorMsg = false;
 699  
 700  	 	 // If $user, $pwd and $db are all null, then $str is a pg_connect()
 701  	 	 // connection string. Otherwise we expect it to be a hostname,
 702  	 	 // with optional port separated by ':'
 703  	 	 if ($user || $pwd || $db) {
 704  	 	 	 // Hostname & port
 705  	 	 	 if ($str) {
 706  	 	 	 	 $host = explode(':', $str);
 707  	 	 	 	 if ($host[0]) {
 708  	 	 	 	 	 $conn['host'] = $host[0];
 709  	 	 	 	 }
 710  	 	 	 	 if (isset($host[1])) {
 711  	 	 	 	 	 $conn['port'] = (int)$host[1];
 712  	 	 	 	 } elseif (!empty($this->port)) {
 713  	 	 	 	 	 $conn['port'] = $this->port;
 714  	 	 	 	 }
 715  	 	 	 }
 716  	 	 	 $conn['user'] = $user;
 717  	 	 	 $conn['password'] = $pwd;
 718  	 	 	 // @TODO not sure why we default to 'template1', pg_connect() uses the username when dbname is empty
 719  	 	 	 $conn['dbname'] = $db ?: 'template1';
 720  
 721  	 	 	 // Generate connection string
 722  	 	 	 $str = '';
 723  	 	 	 foreach ($conn as $param => $value) {
 724  	 	 	 	 // Escaping single quotes and backslashes per pg_connect() documentation
 725  	 	 	 	 $str .= $param . "='" . addcslashes($value, "'\\") . "' ";
 726  	 	 	 }
 727  	 	 }
 728  
 729  	 	 if ($ctype === 1) { // persistent
 730  	 	 	 $this->_connectionID = pg_pconnect($str);
 731  	 	 } else {
 732  	 	 	 if ($ctype === -1) { // nconnect, we trick pgsql ext by changing the connection str
 733  	 	 	 	 static $ncnt;
 734  
 735  	 	 	 	 if (empty($ncnt)) $ncnt = 1;
 736  	 	 	 	 else $ncnt += 1;
 737  
 738  	 	 	 	 $str .= str_repeat(' ',$ncnt);
 739  	 	 	 }
 740  	 	 	 $this->_connectionID = pg_connect($str);
 741  	 	 }
 742  	 	 if ($this->_connectionID === false) return false;
 743  	 	 $this->Execute("set datestyle='ISO'");
 744  
 745  	 	 $info = $this->ServerInfo(false);
 746  
 747  	 	 if (version_compare($info['version'], '7.1', '>=')) {
 748  	 	 	 $this->_nestedSQL = true;
 749  	 	 }
 750  
 751  	 	 # PostgreSQL 9.0 changed the default output for bytea from 'escape' to 'hex'
 752  	 	 # PHP does not handle 'hex' properly ('x74657374' is returned as 't657374')
 753  	 	 # https://bugs.php.net/bug.php?id=59831 states this is in fact not a bug,
 754  	 	 # so we manually set bytea_output
 755  	 	 if (!empty($this->connection->noBlobs)
 756  	 	 	 && version_compare($info['version'], '9.0', '>=')
 757  	 	 	 && version_compare($info['client'], '9.2', '<')
 758  	 	 ) {
 759  	 	 	 $this->Execute('set bytea_output=escape');
 760  	 	 }
 761  
 762  	 	 return true;
 763  	 }
 764  
 765  	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
 766  	 {
 767  	 	 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName,-1);
 768  	 }
 769  
 770  	 // returns true or false
 771  	 //
 772  	 // examples:
 773  	 // 	 $db->PConnect("host=host1 user=user1 password=secret port=4341");
 774  	 // 	 $db->PConnect('host1','user1','secret');
 775  	function _pconnect($str,$user='',$pwd='',$db='')
 776  	 {
 777  	 	 return $this->_connect($str,$user,$pwd,$db,1);
 778  	 }
 779  
 780  
 781  	 // returns queryID or false
 782  	function _query($sql,$inputarr=false)
 783  	 {
 784  	 	 $this->_pnum = 0;
 785  	 	 $this->_errorMsg = false;
 786  	 	 if ($inputarr) {
 787  	 	 /*
 788  	 	 	 It appears that PREPARE/EXECUTE is slower for many queries.
 789  
 790  	 	 	 For query executed 1000 times:
 791  	 	 	 "select id,firstname,lastname from adoxyz
 792  	 	 	 	 where firstname not like ? and lastname not like ? and id = ?"
 793  
 794  	 	 	 with plan = 1.51861286163 secs
 795  	 	 	 no plan =   1.26903700829 secs
 796  	 	 */
 797  	 	 	 $plan = 'P'.md5($sql);
 798  
 799  	 	 	 $execp = '';
 800  	 	 	 foreach($inputarr as $v) {
 801  	 	 	 	 if ($execp) $execp .= ',';
 802  	 	 	 	 if (is_string($v)) {
 803  	 	 	 	 	 if (strncmp($v,"'",1) !== 0) $execp .= $this->qstr($v);
 804  	 	 	 	 } else {
 805  	 	 	 	 	 $execp .= $v;
 806  	 	 	 	 }
 807  	 	 	 }
 808  
 809  	 	 	 if ($execp) $exsql = "EXECUTE $plan ($execp)";
 810  	 	 	 else $exsql = "EXECUTE $plan";
 811  
 812  	 	 	 $rez = @pg_query($this->_connectionID, $exsql);
 813  	 	 	 if (!$rez) {
 814  	 	 	 # Perhaps plan does not exist? Prepare/compile plan.
 815  	 	 	 	 $params = '';
 816  	 	 	 	 foreach($inputarr as $v) {
 817  	 	 	 	 	 if ($params) $params .= ',';
 818  	 	 	 	 	 if (is_string($v)) {
 819  	 	 	 	 	 	 $params .= 'VARCHAR';
 820  	 	 	 	 	 } else if (is_integer($v)) {
 821  	 	 	 	 	 	 $params .= 'INTEGER';
 822  	 	 	 	 	 } else {
 823  	 	 	 	 	 	 $params .= "REAL";
 824  	 	 	 	 	 }
 825  	 	 	 	 }
 826  	 	 	 	 $sqlarr = explode('?',$sql);
 827  	 	 	 	 //print_r($sqlarr);
 828  	 	 	 	 $sql = '';
 829  	 	 	 	 $i = 1;
 830  	 	 	 	 foreach($sqlarr as $v) {
 831  	 	 	 	 	 $sql .= $v.' $'.$i;
 832  	 	 	 	 	 $i++;
 833  	 	 	 	 }
 834  	 	 	 	 $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2);
 835  	 	 	 	 //adodb_pr($s);
 836  	 	 	 	 $rez = pg_query($this->_connectionID, $s);
 837  	 	 	 	 //echo $this->ErrorMsg();
 838  	 	 	 }
 839  	 	 	 if ($rez)
 840  	 	 	 	 $rez = pg_query($this->_connectionID, $exsql);
 841  	 	 } else {
 842  	 	 	 //adodb_backtrace();
 843  	 	 	 $rez = pg_query($this->_connectionID, $sql);
 844  	 	 }
 845  	 	 // check if no data returned, then no need to create real recordset
 846  	 	 if ($rez && pg_num_fields($rez) <= 0) {
 847  	 	 	 if ($this->_resultid !== false) {
 848  	 	 	 	 pg_free_result($this->_resultid);
 849  	 	 	 }
 850  	 	 	 $this->_resultid = $rez;
 851  	 	 	 return true;
 852  	 	 }
 853  
 854  	 	 return $rez;
 855  	 }
 856  
 857  	function _errconnect()
 858  	 {
 859  	 	 if (defined('DB_ERROR_CONNECT_FAILED')) return DB_ERROR_CONNECT_FAILED;
 860  	 	 else return 'Database connection failed';
 861  	 }
 862  
 863  	 /*	 Returns: the last error message from previous database operation	 */
 864  	function ErrorMsg()
 865  	 {
 866  	 	 if ($this->_errorMsg !== false) {
 867  	 	 	 return $this->_errorMsg;
 868  	 	 }
 869  
 870  	 	 if (!empty($this->_resultid)) {
 871  	 	 	 $this->_errorMsg = @pg_result_error($this->_resultid);
 872  	 	 	 if ($this->_errorMsg) {
 873  	 	 	 	 return $this->_errorMsg;
 874  	 	 	 }
 875  	 	 }
 876  
 877  	 	 if (!empty($this->_connectionID)) {
 878  	 	 	 $this->_errorMsg = @pg_last_error($this->_connectionID);
 879  	 	 } else {
 880  	 	 	 $this->_errorMsg = $this->_errconnect();
 881  	 	 }
 882  
 883  	 	 return $this->_errorMsg;
 884  	 }
 885  
 886  	function ErrorNo()
 887  	 {
 888  	 	 $e = $this->ErrorMsg();
 889  	 	 if (strlen($e)) {
 890  	 	 	 return ADOConnection::MetaError($e);
 891  	 	 }
 892  	 	 return 0;
 893  	 }
 894  
 895  	 // returns true or false
 896  	function _close()
 897  	 {
 898  	 	 if ($this->transCnt) $this->RollbackTrans();
 899  	 	 if ($this->_resultid) {
 900  	 	 	 @pg_free_result($this->_resultid);
 901  	 	 	 $this->_resultid = false;
 902  	 	 }
 903  	 	 @pg_close($this->_connectionID);
 904  	 	 $this->_connectionID = false;
 905  	 	 return true;
 906  	 }
 907  
 908  
 909  	 /*
 910  	 * Maximum size of C field
 911  	 */
 912  	function CharMax()
 913  	 {
 914  	 	 return 1000000000;  // should be 1 Gb?
 915  	 }
 916  
 917  	 /*
 918  	 * Maximum size of X field
 919  	 */
 920  	function TextMax()
 921  	 {
 922  	 	 return 1000000000; // should be 1 Gb?
 923  	 }
 924  
 925  
 926  }
 927  
 928  /*--------------------------------------------------------------------------------------
 929  	 Class Name: Recordset
 930  --------------------------------------------------------------------------------------*/
 931  
 932  class ADORecordSet_postgres64 extends ADORecordSet{
 933  	 var $_blobArr;
 934  	 var $databaseType = "postgres64";
 935  	 var $canSeek = true;
 936  
 937  	function __construct($queryID, $mode=false)
 938  	 {
 939  	 	 if ($mode === false) {
 940  	 	 	 global $ADODB_FETCH_MODE;
 941  	 	 	 $mode = $ADODB_FETCH_MODE;
 942  	 	 }
 943  	 	 switch ($mode)
 944  	 	 {
 945  	 	 case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break;
 946  	 	 case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break;
 947  
 948  	 	 case ADODB_FETCH_DEFAULT:
 949  	 	 case ADODB_FETCH_BOTH:
 950  	 	 default: $this->fetchMode = PGSQL_BOTH; break;
 951  	 	 }
 952  	 	 $this->adodbFetchMode = $mode;
 953  
 954  	 	 // Parent's constructor
 955  	 	 parent::__construct($queryID);
 956  	 }
 957  
 958  	function GetRowAssoc($upper = ADODB_ASSOC_CASE)
 959  	 {
 960  	 	 if ($this->fetchMode == PGSQL_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) {
 961  	 	 	 return $this->fields;
 962  	 	 }
 963  	 	 $row = ADORecordSet::GetRowAssoc($upper);
 964  	 	 return $row;
 965  	 }
 966  
 967  	function _initRS()
 968  	 {
 969  	 	 global $ADODB_COUNTRECS;
 970  	 	 $qid = $this->_queryID;
 971  	 	 $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_num_rows($qid):-1;
 972  	 	 $this->_numOfFields = @pg_num_fields($qid);
 973  
 974  	 	 // cache types for blob decode check
 975  	 	 // apparently pg_field_type actually performs an sql query on the database to get the type.
 976  	 	 if (empty($this->connection->noBlobs))
 977  	 	 for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {
 978  	 	 	 if (pg_field_type($qid,$i) == 'bytea') {
 979  	 	 	 	 $this->_blobArr[$i] = pg_field_name($qid,$i);
 980  	 	 	 }
 981  	 	 }
 982  	 }
 983  
 984  	function fields($colname)
 985  	 {
 986  	 	 if ($this->fetchMode != PGSQL_NUM) {
 987  	 	 	 return @$this->fields[$colname];
 988  	 	 }
 989  
 990  	 	 if (!$this->bind) {
 991  	 	 	 $this->bind = array();
 992  	 	 	 for ($i=0; $i < $this->_numOfFields; $i++) {
 993  	 	 	 	 $o = $this->FetchField($i);
 994  	 	 	 	 $this->bind[strtoupper($o->name)] = $i;
 995  	 	 	 }
 996  	 	 }
 997  	 	 return $this->fields[$this->bind[strtoupper($colname)]];
 998  	 }
 999  
1000  	function fetchField($fieldOffset = 0)
1001  	 {
1002  	 	 // offsets begin at 0
1003  
1004  	 	 $o = new ADOFieldObject();
1005  	 	 $o->name = @pg_field_name($this->_queryID, $fieldOffset);
1006  	 	 $o->type = @pg_field_type($this->_queryID, $fieldOffset);
1007  	 	 $o->max_length = @pg_field_size($this->_queryID, $fieldOffset);
1008  	 	 return $o;
1009  	 }
1010  
1011  	function _seek($row)
1012  	 {
1013  	 	 return @pg_fetch_row($this->_queryID,$row);
1014  	 }
1015  
1016  	function _decode($blob)
1017  	 {
1018  	 	 if ($blob === NULL) return NULL;
1019  //	 	 eval('$realblob="'.str_replace(array('"','$'),array('\"','\$'),$blob).'";');
1020  	 	 return pg_unescape_bytea($blob);
1021  	 }
1022  
1023  	 /**
1024  	  * Fetches and prepares the RecordSet's fields.
1025  	  *
1026  	  * Fixes the blobs if there are any.
1027  	  */
1028  	protected function _prepFields()
1029  	 {
1030  	 	 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
1031  
1032  	 	 // Check prerequisites and bail early if we do not have what we need.
1033  	 	 if (!isset($this->_blobArr) || $this->fields === false) {
1034  	 	 	 return;
1035  	 	 }
1036  
1037  	 	 if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) {
1038  	 	 	 foreach($this->_blobArr as $k => $v) {
1039  	 	 	 	 $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]);
1040  	 	 	 }
1041  	 	 }
1042  	 	 if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) {
1043  	 	 	 foreach($this->_blobArr as $k => $v) {
1044  	 	 	 	 $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]);
1045  	 	 	 }
1046  	 	 }
1047  	 }
1048  
1049  	 // 10% speedup to move MoveNext to child class
1050  	function MoveNext()
1051  	 {
1052  	 	 if (!$this->EOF) {
1053  	 	 	 $this->_currentRow++;
1054  	 	 	 if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) {
1055  	 	 	 	 $this->_prepfields();
1056  	 	 	 	 if ($this->fields !== false) {
1057  	 	 	 	 	 return true;
1058  	 	 	 	 }
1059  	 	 	 }
1060  	 	 	 $this->fields = false;
1061  	 	 	 $this->EOF = true;
1062  	 	 }
1063  	 	 return false;
1064  	 }
1065  
1066  	function _fetch()
1067  	 {
1068  	 	 if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) {
1069  	 	 	 return false;
1070  	 	 }
1071  
1072  	 	 $this->_prepfields();
1073  	 	 return $this->fields !== false;
1074  	 }
1075  
1076  	function _close()
1077  	 {
1078  	 	 if ($this->_queryID === false) {
1079  	 	 	 return true;
1080  	 	 }
1081  	 	 return pg_free_result($this->_queryID);
1082  	 }
1083  
1084  	function MetaType($t,$len=-1,$fieldobj=false)
1085  	 {
1086  	 	 if (is_object($t)) {
1087  	 	 	 $fieldobj = $t;
1088  	 	 	 $t = $fieldobj->type;
1089  	 	 	 $len = $fieldobj->max_length;
1090  	 	 }
1091  
1092  	 	 $t = strtoupper($t);
1093  
1094  	 	 if (array_key_exists($t,$this->connection->customActualTypes))
1095  	 	 	 return  $this->connection->customActualTypes[$t];
1096  
1097  	 	 switch ($t) {
1098  	 	 	 	 case 'MONEY': // stupid, postgres expects money to be a string
1099  	 	 	 	 case 'INTERVAL':
1100  	 	 	 	 case 'CHAR':
1101  	 	 	 	 case 'CHARACTER':
1102  	 	 	 	 case 'VARCHAR':
1103  	 	 	 	 case 'NAME':
1104  	 	 	 	 case 'BPCHAR':
1105  	 	 	 	 case '_VARCHAR':
1106  	 	 	 	 case 'CIDR':
1107  	 	 	 	 case 'INET':
1108  	 	 	 	 case 'MACADDR':
1109  	 	 	 	 case 'UUID':
1110  	 	 	 	 	 if ($len <= $this->blobSize) return 'C';
1111  
1112  	 	 	 	 case 'TEXT':
1113  	 	 	 	 	 return 'X';
1114  
1115  	 	 	 	 case 'IMAGE': // user defined type
1116  	 	 	 	 case 'BLOB': // user defined type
1117  	 	 	 	 case 'BIT':	 // This is a bit string, not a single bit, so don't return 'L'
1118  	 	 	 	 case 'VARBIT':
1119  	 	 	 	 case 'BYTEA':
1120  	 	 	 	 	 return 'B';
1121  
1122  	 	 	 	 case 'BOOL':
1123  	 	 	 	 case 'BOOLEAN':
1124  	 	 	 	 	 return 'L';
1125  
1126  	 	 	 	 case 'DATE':
1127  	 	 	 	 	 return 'D';
1128  
1129  
1130  	 	 	 	 case 'TIMESTAMP WITHOUT TIME ZONE':
1131  	 	 	 	 case 'TIME':
1132  	 	 	 	 case 'DATETIME':
1133  	 	 	 	 case 'TIMESTAMP':
1134  	 	 	 	 case 'TIMESTAMPTZ':
1135  	 	 	 	 	 return 'T';
1136  
1137  	 	 	 	 case 'SMALLINT':
1138  	 	 	 	 case 'BIGINT':
1139  	 	 	 	 case 'INTEGER':
1140  	 	 	 	 case 'INT8':
1141  	 	 	 	 case 'INT4':
1142  	 	 	 	 case 'INT2':
1143  	 	 	 	 	 if (isset($fieldobj) &&
1144  	 	 	 	 empty($fieldobj->primary_key) && (!$this->connection->uniqueIisR || empty($fieldobj->unique))) return 'I';
1145  
1146  	 	 	 	 case 'OID':
1147  	 	 	 	 case 'SERIAL':
1148  	 	 	 	 	 return 'R';
1149  
1150  	 	 	 	 case 'NUMERIC':
1151  	 	 	 	 case 'DECIMAL':
1152  	 	 	 	 case 'FLOAT4':
1153  	 	 	 	 case 'FLOAT8':
1154  	 	 	 	 	 return 'N';
1155  
1156  	 	 	 	 default:
1157  	 	 	 	 	 return ADODB_DEFAULT_METATYPE;
1158  	 	 	 }
1159  	 }
1160  
1161  }