"TYPO3 4.0.1: typo3_src-4.0.1/typo3/sysext/adodb/adodb/drivers/adodb-postgres64.inc.php Source File", "datetime" => "Sat Dec 2 19:22:26 2006", "date" => "2 Dec 2006", "doxygenversion" => "1.4.6", "projectname" => "TYPO3 4.0.1", "projectnumber" => "4.0.1" ); get_header($doxygen_vars); ?>

adodb-postgres64.inc.php

00001 <?php
00002 /*
00003  V4.90 8 June 2006  (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved.
00004   Released under both BSD license and Lesser GPL library license. 
00005   Whenever there is any discrepancy between the two licenses, 
00006   the BSD license will take precedence.
00007   Set tabs to 8.
00008   
00009   Original version derived from Alberto Cerezal (acerezalp@dbnet.es) - DBNet Informatica & Comunicaciones. 
00010   08 Nov 2000 jlim - Minor corrections, removing mysql stuff
00011   09 Nov 2000 jlim - added insertid support suggested by "Christopher Kings-Lynne" <chriskl@familyhealth.com.au>
00012                                         jlim - changed concat operator to || and data types to MetaType to match documented pgsql types 
00013                         see http://www.postgresql.org/devel-corner/docs/postgres/datatype.htm  
00014   22 Nov 2000 jlim - added changes to FetchField() and MetaTables() contributed by "raser" <raser@mail.zen.com.tw>
00015   27 Nov 2000 jlim - added changes to _connect/_pconnect from ideas by "Lennie" <leen@wirehub.nl>
00016   15 Dec 2000 jlim - added changes suggested by Additional code changes by "Eric G. Werk" egw@netguide.dk. 
00017   31 Jan 2002 jlim - finally installed postgresql. testing
00018   01 Mar 2001 jlim - Freek Dijkstra changes, also support for text type
00019   
00020   See http://www.varlena.com/varlena/GeneralBits/47.php
00021   
00022         -- What indexes are on my table?
00023         select * from pg_indexes where tablename = 'tablename';
00024         
00025         -- What triggers are on my table?
00026         select c.relname as "Table", t.tgname as "Trigger Name", 
00027            t.tgconstrname as "Constraint Name", t.tgenabled as "Enabled",
00028            t.tgisconstraint as "Is Constraint", cc.relname as "Referenced Table",
00029            p.proname as "Function Name"
00030         from pg_trigger t, pg_class c, pg_class cc, pg_proc p
00031         where t.tgfoid = p.oid and t.tgrelid = c.oid
00032            and t.tgconstrrelid = cc.oid
00033            and c.relname = 'tablename';
00034         
00035         -- What constraints are on my table?
00036         select r.relname as "Table", c.conname as "Constraint Name",
00037            contype as "Constraint Type", conkey as "Key Columns",
00038            confkey as "Foreign Columns", consrc as "Source"
00039         from pg_class r, pg_constraint c
00040         where r.oid = c.conrelid
00041            and relname = 'tablename';
00042 
00043 */
00044 
00045 // security - hide paths
00046 if (!defined('ADODB_DIR')) die();
00047 
00048 function adodb_addslashes($s)
00049 {
00050         $len = strlen($s);
00051         if ($len == 0) return "''";
00052         if (strncmp($s,"'",1) === 0 && substr($s,$len-1) == "'") return $s; // already quoted
00053         
00054         return "'".addslashes($s)."'";
00055 }
00056 
00057 class ADODB_postgres64 extends ADOConnection{
00058         var $databaseType = 'postgres64';
00059         var $dataProvider = 'postgres';
00060         var $hasInsertID = true;
00061         var $_resultid = false;
00062         var $concat_operator='||';
00063         var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1";
00064     var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%'
00065         and tablename not in ('sql_features', 'sql_implementation_info', 'sql_languages',
00066          'sql_packages', 'sql_sizing', 'sql_sizing_profiles') 
00067         union 
00068         select viewname,'V' from pg_views where viewname not like 'pg\_%'";
00069         //"select tablename from pg_tables where tablename not like 'pg_%' order by 1";
00070         var $isoDates = true; // accepts dates in ISO format
00071         var $sysDate = "CURRENT_DATE";
00072         var $sysTimeStamp = "CURRENT_TIMESTAMP";
00073         var $blobEncodeType = 'C';
00074         var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum 
00075                 FROM pg_class c, pg_attribute a,pg_type t 
00076                 WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and a.attname not like '....%%'
00077 AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
00078 
00079         // used when schema defined
00080         var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum 
00081 FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n 
00082 WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s'))
00083  and c.relnamespace=n.oid and n.nspname='%s' 
00084         and a.attname not like '....%%' AND a.attnum > 0 
00085         AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
00086         
00087         // get primary key etc -- from Freek Dijkstra
00088         var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key 
00089         FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid 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) AND a.attrelid = bc.oid AND bc.relname = '%s'";
00090         
00091         var $hasAffectedRows = true;
00092         var $hasLimit = false;  // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10
00093         // below suggested by Freek Dijkstra 
00094         var $true = 'TRUE';             // string that represents TRUE for a database
00095         var $false = 'FALSE';           // string that represents FALSE for a database
00096         var $fmtDate = "'Y-m-d'";       // used by DBDate() as the default date format used by the database
00097         var $fmtTimeStamp = "'Y-m-d H:i:s'"; // used by DBTimeStamp as the default timestamp fmt.
00098         var $hasMoveFirst = true;
00099         var $hasGenID = true;
00100         var $_genIDSQL = "SELECT NEXTVAL('%s')";
00101         var $_genSeqSQL = "CREATE SEQUENCE %s START %s";
00102         var $_dropSeqSQL = "DROP SEQUENCE %s";
00103         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";
00104         var $random = 'random()';               
00105         var $autoRollback = true; // apparently pgsql does not autorollback properly before php 4.3.4
00106                                                         // http://bugs.php.net/bug.php?id=25404
00107                                                         
00108         var $_bindInputArray = false; // requires postgresql 7.3+ and ability to modify database
00109         var $disableBlobs = false; // set to true to disable blob checking, resulting in 2-5% improvement in performance.
00110         
00111         // The last (fmtTimeStamp is not entirely correct: 
00112         // PostgreSQL also has support for time zones, 
00113         // and writes these time in this format: "2001-03-01 18:59:26+02". 
00114         // There is no code for the "+02" time zone information, so I just left that out. 
00115         // I'm not familiar enough with both ADODB as well as Postgres 
00116         // to know what the concequences are. The other values are correct (wheren't in 0.94)
00117         // -- Freek Dijkstra 
00118 
00119         function ADODB_postgres64() 
00120         {
00121         // changes the metaColumnsSQL, adds columns: attnum[6]
00122         }
00123         
00124         function ServerInfo()
00125         {
00126                 if (isset($this->version)) return $this->version;
00127                 
00128                 $arr['description'] = $this->GetOne("select version()");
00129                 $arr['version'] = ADOConnection::_findvers($arr['description']);
00130                 $this->version = $arr;
00131                 return $arr;
00132         }
00133 
00134         function IfNull( $field, $ifNull ) 
00135         {
00136                 return " coalesce($field, $ifNull) "; 
00137         }
00138 
00139         // get the last id - never tested
00140         function pg_insert_id($tablename,$fieldname)
00141         {
00142                 $result=pg_exec($this->_connectionID, "SELECT last_value FROM ${tablename}_${fieldname}_seq");
00143                 if ($result) {
00144                         $arr = @pg_fetch_row($result,0);
00145                         pg_freeresult($result);
00146                         if (isset($arr[0])) return $arr[0];
00147                 }
00148                 return false;
00149         }
00150         
00151 /* Warning from http://www.php.net/manual/function.pg-getlastoid.php:
00152 Using a OID as a unique identifier is not generally wise. 
00153 Unless you are very careful, you might end up with a tuple having 
00154 a different OID if a database must be reloaded. */
00155         function _insertid($table,$column)
00156         {
00157                 if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false;
00158                 $oid = pg_getlastoid($this->_resultid);
00159                 // to really return the id, we need the table and column-name, else we can only return the oid != id
00160                 return empty($table) || empty($column) ? $oid : $this->GetOne("SELECT $column FROM $table WHERE oid=".(int)$oid);
00161         }
00162 
00163 // I get this error with PHP before 4.0.6 - jlim
00164 // Warning: This compilation does not support pg_cmdtuples() in adodb-postgres.inc.php on line 44
00165    function _affectedrows()
00166    {
00167                 if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false;
00168                 return pg_cmdtuples($this->_resultid);
00169    }
00170    
00171         
00172                 // returns true/false
00173         function BeginTrans()
00174         {
00175                 if ($this->transOff) return true;
00176                 $this->transCnt += 1;
00177                 return @pg_Exec($this->_connectionID, "begin ".$this->_transmode);
00178         }
00179         
00180         function RowLock($tables,$where,$flds='1 as ignore') 
00181         {
00182                 if (!$this->transCnt) $this->BeginTrans();
00183                 return $this->GetOne("select $flds from $tables where $where for update");
00184         }
00185 
00186         // returns true/false. 
00187         function CommitTrans($ok=true) 
00188         { 
00189                 if ($this->transOff) return true;
00190                 if (!$ok) return $this->RollbackTrans();
00191                 
00192                 $this->transCnt -= 1;
00193                 return @pg_Exec($this->_connectionID, "commit");
00194         }
00195         
00196         // returns true/false
00197         function RollbackTrans()
00198         {
00199                 if ($this->transOff) return true;
00200                 $this->transCnt -= 1;
00201                 return @pg_Exec($this->_connectionID, "rollback");
00202         }
00203         
00204         function &MetaTables($ttype=false,$showSchema=false,$mask=false) 
00205         {
00206                 $info = $this->ServerInfo();
00207                 if ($info['version'] >= 7.3) {
00208                 $this->metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%'
00209                           and schemaname  not in ( 'pg_catalog','information_schema')
00210         union 
00211         select viewname,'V' from pg_views where viewname not like 'pg\_%'  and schemaname  not in ( 'pg_catalog','information_schema') ";
00212                 }
00213                 if ($mask) {
00214                         $save = $this->metaTablesSQL;
00215                         $mask = $this->qstr(strtolower($mask));
00216                         if ($info['version']>=7.3)
00217                                 $this->metaTablesSQL = "
00218 select tablename,'T' from pg_tables where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema')  
00219  union 
00220 select viewname,'V' from pg_views where viewname like $mask and schemaname  not in ( 'pg_catalog','information_schema')  ";
00221                         else
00222                                 $this->metaTablesSQL = "
00223 select tablename,'T' from pg_tables where tablename like $mask 
00224  union 
00225 select viewname,'V' from pg_views where viewname like $mask";
00226                 }
00227                 $ret =& ADOConnection::MetaTables($ttype,$showSchema);
00228                 
00229                 if ($mask) {
00230                         $this->metaTablesSQL = $save;
00231                 }
00232                 return $ret;
00233         }
00234         
00235         
00236         // if magic quotes disabled, use pg_escape_string()
00237         function qstr($s,$magic_quotes=false)
00238         {
00239                 if (!$magic_quotes) {
00240                         if (ADODB_PHPVER >= 0x4200) {
00241                                 return  "'".pg_escape_string($s)."'";
00242                         }
00243                         if ($this->replaceQuote[0] == '\\'){
00244                                 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\\000"),$s);
00245                         }
00246                         return  "'".str_replace("'",$this->replaceQuote,$s)."'"; 
00247                 }
00248                 
00249                 // undo magic quotes for "
00250                 $s = str_replace('\\"','"',$s);
00251                 return "'$s'";
00252         }
00253         
00254         
00255         
00256         // Format date column in sql string given an input format that understands Y M D
00257         function SQLDate($fmt, $col=false)
00258         {       
00259                 if (!$col) $col = $this->sysTimeStamp;
00260                 $s = 'TO_CHAR('.$col.",'";
00261                 
00262                 $len = strlen($fmt);
00263                 for ($i=0; $i < $len; $i++) {
00264                         $ch = $fmt[$i];
00265                         switch($ch) {
00266                         case 'Y':
00267                         case 'y':
00268                                 $s .= 'YYYY';
00269                                 break;
00270                         case 'Q':
00271                         case 'q':
00272                                 $s .= 'Q';
00273                                 break;
00274                                 
00275                         case 'M':
00276                                 $s .= 'Mon';
00277                                 break;
00278                                 
00279                         case 'm':
00280                                 $s .= 'MM';
00281                                 break;
00282                         case 'D':
00283                         case 'd':
00284                                 $s .= 'DD';
00285                                 break;
00286                         
00287                         case 'H':
00288                                 $s.= 'HH24';
00289                                 break;
00290                                 
00291                         case 'h':
00292                                 $s .= 'HH';
00293                                 break;
00294                                 
00295                         case 'i':
00296                                 $s .= 'MI';
00297                                 break;
00298                         
00299                         case 's':
00300                                 $s .= 'SS';
00301                                 break;
00302                         
00303                         case 'a':
00304                         case 'A':
00305                                 $s .= 'AM';
00306                                 break;
00307                                 
00308                         case 'w':
00309                                 $s .= 'D';
00310                                 break;
00311                         
00312                         case 'l':
00313                                 $s .= 'DAY';
00314                                 break;
00315                         
00316                          case 'W':
00317                                 $s .= 'WW';
00318                                 break;
00319 
00320                         default:
00321                         // handle escape characters...
00322                                 if ($ch == '\\') {
00323                                         $i++;
00324                                         $ch = substr($fmt,$i,1);
00325                                 }
00326                                 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
00327                                 else $s .= '"'.$ch.'"';
00328                                 
00329                         }
00330                 }
00331                 return $s. "')";
00332         }
00333         
00334         
00335         
00336         /* 
00337         * Load a Large Object from a file 
00338         * - the procedure stores the object id in the table and imports the object using 
00339         * postgres proprietary blob handling routines 
00340         *
00341         * contributed by Mattia Rossi mattia@technologist.com
00342         * modified for safe mode by juraj chlebec
00343         */ 
00344         function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') 
00345         { 
00346                 pg_exec ($this->_connectionID, "begin"); 
00347                 
00348                 $fd = fopen($path,'r');
00349                 $contents = fread($fd,filesize($path));
00350                 fclose($fd);
00351                 
00352                 $oid = pg_lo_create($this->_connectionID);
00353                 $handle = pg_lo_open($this->_connectionID, $oid, 'w');
00354                 pg_lo_write($handle, $contents);
00355                 pg_lo_close($handle);
00356                 
00357                 // $oid = pg_lo_import ($path); 
00358                 pg_exec($this->_connectionID, "commit"); 
00359                 $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype); 
00360                 $rez = !empty($rs); 
00361                 return $rez; 
00362         } 
00363         
00364         /*
00365         * Deletes/Unlinks a Blob from the database, otherwise it 
00366         * will be left behind
00367         *
00368         * Returns TRUE on success or FALSE on failure.
00369         *
00370         * contributed by Todd Rogers todd#windfox.net
00371         */
00372         function BlobDelete( $blob )
00373         {
00374                 pg_exec ($this->_connectionID, "begin");
00375                 $result = @pg_lo_unlink($blob);
00376                 pg_exec ($this->_connectionID, "commit");
00377                 return( $result );
00378         }
00379 
00380         /*
00381                 Hueristic - not guaranteed to work.
00382         */
00383         function GuessOID($oid)
00384         {
00385                 if (strlen($oid)>16) return false;
00386                 return is_numeric($oid);
00387         }
00388         
00389         /* 
00390         * If an OID is detected, then we use pg_lo_* to open the oid file and read the
00391         * real blob from the db using the oid supplied as a parameter. If you are storing
00392         * blobs using bytea, we autodetect and process it so this function is not needed.
00393         *
00394         * contributed by Mattia Rossi mattia@technologist.com
00395         *
00396         * see http://www.postgresql.org/idocs/index.php?largeobjects.html
00397         *
00398         * Since adodb 4.54, this returns the blob, instead of sending it to stdout. Also
00399         * added maxsize parameter, which defaults to $db->maxblobsize if not defined.
00400         */ 
00401         function BlobDecode($blob,$maxsize=false,$hastrans=true) 
00402         {
00403                 if (!$this->GuessOID($blob)) return $blob;
00404                 
00405                 if ($hastrans) @pg_exec($this->_connectionID,"begin"); 
00406                 $fd = @pg_lo_open($this->_connectionID,$blob,"r");
00407                 if ($fd === false) {
00408                         if ($hastrans) @pg_exec($this->_connectionID,"commit");
00409                         return $blob;
00410                 }
00411                 if (!$maxsize) $maxsize = $this->maxblobsize;
00412                 $realblob = @pg_loread($fd,$maxsize); 
00413                 @pg_loclose($fd); 
00414                 if ($hastrans) @pg_exec($this->_connectionID,"commit"); 
00415                 return $realblob;
00416         }
00417         
00418         /* 
00419                 See http://www.postgresql.org/idocs/index.php?datatype-binary.html
00420                 
00421                 NOTE: SQL string literals (input strings) must be preceded with two backslashes 
00422                 due to the fact that they must pass through two parsers in the PostgreSQL 
00423                 backend.
00424         */
00425         function BlobEncode($blob)
00426         {
00427                 if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob);
00428                 
00429                 /*92=backslash, 0=null, 39=single-quote*/
00430                 $badch = array(chr(92),chr(0),chr(39)); # \  null  '
00431                 $fixch = array('\\\\134','\\\\000','\\\\047');
00432                 return adodb_str_replace($badch,$fixch,$blob);
00433                 
00434                 // note that there is a pg_escape_bytea function only for php 4.2.0 or later
00435         }
00436         
00437         // assumes bytea for blob, and varchar for clob
00438         function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
00439         {
00440         
00441                 if ($blobtype == 'CLOB') {
00442                 return $this->Execute("UPDATE $table SET $column=" . $this->qstr($val) . " WHERE $where");
00443                 }
00444                 // do not use bind params which uses qstr(), as blobencode() already quotes data
00445                 return $this->Execute("UPDATE $table SET $column='".$this->BlobEncode($val)."'::bytea WHERE $where");
00446         }
00447         
00448         function OffsetDate($dayFraction,$date=false)
00449         {               
00450                 if (!$date) $date = $this->sysDate;
00451                 else if (strncmp($date,"'",1) == 0) {
00452                         $len = strlen($date);
00453                         if (10 <= $len && $len <= 12) $date = 'date '.$date;
00454                         else $date = 'timestamp '.$date;
00455                 }
00456                 return "($date+interval'$dayFraction days')";
00457         }
00458         
00459 
00460         // for schema support, pass in the $table param "$schema.$tabname".
00461         // converts field names to lowercase, $upper is ignored
00462         // see http://phplens.com/lens/lensforum/msgs.php?id=14018 for more info
00463         function &MetaColumns($table,$normalize=true) 
00464         {
00465         global $ADODB_FETCH_MODE;
00466         
00467                 $schema = false;
00468                 $false = false;
00469                 $this->_findschema($table,$schema);
00470                 
00471                 if ($normalize) $table = strtolower($table);
00472 
00473                 $save = $ADODB_FETCH_MODE;
00474                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
00475                 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
00476                 
00477                 if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema));
00478                 else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table));
00479                 if (isset($savem)) $this->SetFetchMode($savem);
00480                 $ADODB_FETCH_MODE = $save;
00481                 
00482                 if ($rs === false) {
00483                         return $false;
00484                 }
00485                 if (!empty($this->metaKeySQL)) {
00486                         // If we want the primary keys, we have to issue a separate query
00487                         // Of course, a modified version of the metaColumnsSQL query using a 
00488                         // LEFT JOIN would have been much more elegant, but postgres does 
00489                         // not support OUTER JOINS. So here is the clumsy way.
00490                         
00491                         $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
00492                         
00493                         $rskey = $this->Execute(sprintf($this->metaKeySQL,($table)));
00494                         // fetch all result in once for performance.
00495                         $keys =& $rskey->GetArray();
00496                         if (isset($savem)) $this->SetFetchMode($savem);
00497                         $ADODB_FETCH_MODE = $save;
00498                         
00499                         $rskey->Close();
00500                         unset($rskey);
00501                 }
00502 
00503                 $rsdefa = array();
00504                 if (!empty($this->metaDefaultsSQL)) {
00505                         $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
00506                         $sql = sprintf($this->metaDefaultsSQL, ($table));
00507                         $rsdef = $this->Execute($sql);
00508                         if (isset($savem)) $this->SetFetchMode($savem);
00509                         $ADODB_FETCH_MODE = $save;
00510                         
00511                         if ($rsdef) {
00512                                 while (!$rsdef->EOF) {
00513                                         $num = $rsdef->fields['num'];
00514                                         $s = $rsdef->fields['def'];
00515                                         if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */
00516                                                 $s = substr($s, 1);
00517                                                 $s = substr($s, 0, strlen($s) - 1);
00518                                         }
00519 
00520                                         $rsdefa[$num] = $s;
00521                                         $rsdef->MoveNext();
00522                                 }
00523                         } else {
00524                                 ADOConnection::outp( "==> SQL => " . $sql);
00525                         }
00526                         unset($rsdef);
00527                 }
00528         
00529                 $retarr = array();
00530                 while (!$rs->EOF) {     
00531                         $fld = new ADOFieldObject();
00532                         $fld->name = $rs->fields[0];
00533                         $fld->type = $rs->fields[1];
00534                         $fld->max_length = $rs->fields[2];
00535                         $fld->attnum = $rs->fields[6];
00536                         
00537                         if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4;
00538                         if ($fld->max_length <= 0) $fld->max_length = -1;
00539                         if ($fld->type == 'numeric') {
00540                                 $fld->scale = $fld->max_length & 0xFFFF;
00541                                 $fld->max_length >>= 16;
00542                         }
00543                         // dannym
00544                         // 5 hasdefault; 6 num-of-column
00545                         $fld->has_default = ($rs->fields[5] == 't');
00546                         if ($fld->has_default) {
00547                                 $fld->default_value = $rsdefa[$rs->fields[6]];
00548                         }
00549 
00550                         //Freek
00551                         $fld->not_null = $rs->fields[4] == 't';
00552                         
00553                         
00554                         // Freek
00555                         if (is_array($keys)) {
00556                                 foreach($keys as $key) {
00557                                         if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't') 
00558                                                 $fld->primary_key = true;
00559                                         if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't') 
00560                                                 $fld->unique = true; // What name is more compatible?
00561                                 }
00562                         }
00563                         
00564                         if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;     
00565                         else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld;
00566                         
00567                         $rs->MoveNext();
00568                 }
00569                 $rs->Close();
00570                 if (empty($retarr))
00571                         return  $false;
00572                 else
00573                         return $retarr; 
00574                 
00575         }
00576 
00577           function &MetaIndexes ($table, $primary = FALSE)
00578       {
00579          global $ADODB_FETCH_MODE;
00580                 
00581                                 $schema = false;
00582                                 $this->_findschema($table,$schema);
00583 
00584                                 if ($schema) { // requires pgsql 7.3+ - pg_namespace used.
00585                                         $sql = '
00586 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" 
00587 FROM pg_catalog.pg_class c 
00588 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid 
00589 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
00590         ,pg_namespace n 
00591 WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\')) and c.relnamespace=c2.relnamespace and c.relnamespace=n.oid and n.nspname=\'%s\'';
00592                                 } else {
00593                         $sql = '
00594 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
00595 FROM pg_catalog.pg_class c
00596 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
00597 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
00598 WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))';
00599                         }
00600                                             
00601                 if ($primary == FALSE) {
00602                         $sql .= ' AND i.indisprimary=false;';
00603                 }
00604                 
00605                 $save = $ADODB_FETCH_MODE;
00606                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
00607                 if ($this->fetchMode !== FALSE) {
00608                         $savem = $this->SetFetchMode(FALSE);
00609                 }
00610                 
00611                 $rs = $this->Execute(sprintf($sql,$table,$table,$schema));
00612                 if (isset($savem)) {
00613                         $this->SetFetchMode($savem);
00614                 }
00615                 $ADODB_FETCH_MODE = $save;
00616 
00617                 if (!is_object($rs)) {
00618                         $false = false;
00619                                         return $false;
00620                 }
00621                                 
00622                 $col_names = $this->MetaColumnNames($table,true,true); 
00623                                 //3rd param is use attnum, 
00624                                 // see http://sourceforge.net/tracker/index.php?func=detail&aid=1451245&group_id=42718&atid=433976
00625                 $indexes = array();
00626                 while ($row = $rs->FetchRow()) {
00627                         $columns = array();
00628                         foreach (explode(' ', $row[2]) as $col) {
00629                                 $columns[] = $col_names[$col];
00630                         }
00631                         
00632                         $indexes[$row[0]] = array(
00633                                 'unique' => ($row[1] == 't'),
00634                                 'columns' => $columns
00635                         );
00636                 }
00637                 return $indexes;
00638         }
00639 
00640         // returns true or false
00641         //
00642         // examples:
00643         //      $db->Connect("host=host1 user=user1 password=secret port=4341");
00644         //      $db->Connect('host1','user1','secret');
00645         function _connect($str,$user='',$pwd='',$db='',$ctype=0)
00646         {
00647                 
00648                 if (!function_exists('pg_connect')) return null;
00649                 
00650                 $this->_errorMsg = false;
00651                 
00652                 if ($user || $pwd || $db) {
00653                         $user = adodb_addslashes($user);
00654                         $pwd = adodb_addslashes($pwd);
00655                         if (strlen($db) == 0) $db = 'template1';
00656                         $db = adodb_addslashes($db);
00657                         if ($str)  {
00658                                 $host = split(":", $str);
00659                                 if ($host[0]) $str = "host=".adodb_addslashes($host[0]);
00660                                 else $str = 'host=localhost';
00661                                 if (isset($host[1])) $str .= " port=$host[1]";
00662                                 else if (!empty($this->port)) $str .= " port=".$this->port;
00663                         }
00664                                 if ($user) $str .= " user=".$user;
00665                                 if ($pwd)  $str .= " password=".$pwd;
00666                                 if ($db)   $str .= " dbname=".$db;
00667                 }
00668 
00669                 //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432";
00670                 
00671                 if ($ctype === 1) { // persistent
00672                         $this->_connectionID = pg_pconnect($str);
00673                 } else {
00674                         if ($ctype === -1) { // nconnect, we trick pgsql ext by changing the connection str
00675                         static $ncnt;
00676                         
00677                                 if (empty($ncnt)) $ncnt = 1;
00678                                 else $ncnt += 1;
00679                                 
00680                                 $str .= str_repeat(' ',$ncnt);
00681                         }
00682                         $this->_connectionID = pg_connect($str);
00683                 }
00684                 if ($this->_connectionID === false) return false;
00685                 $this->Execute("set datestyle='ISO'");
00686                 return true;
00687         }
00688         
00689         function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
00690         {
00691                 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName,-1);
00692         }
00693          
00694         // returns true or false
00695         //
00696         // examples:
00697         //      $db->PConnect("host=host1 user=user1 password=secret port=4341");
00698         //      $db->PConnect('host1','user1','secret');
00699         function _pconnect($str,$user='',$pwd='',$db='')
00700         {
00701                 return $this->_connect($str,$user,$pwd,$db,1);
00702         }
00703         
00704 
00705         // returns queryID or false
00706         function _query($sql,$inputarr)
00707         {
00708                 $this->_errorMsg = false;
00709                 if ($inputarr) {
00710                 /*
00711                         It appears that PREPARE/EXECUTE is slower for many queries.
00712                         
00713                         For query executed 1000 times:
00714                         "select id,firstname,lastname from adoxyz 
00715                                 where firstname not like ? and lastname not like ? and id = ?"
00716                                 
00717                         with plan = 1.51861286163 secs
00718                         no plan =   1.26903700829 secs
00719 
00720                         
00721 
00722                 */
00723                         $plan = 'P'.md5($sql);
00724                                 
00725                         $execp = '';
00726                         foreach($inputarr as $v) {
00727                                 if ($execp) $execp .= ',';
00728                                 if (is_string($v)) {
00729                                         if (strncmp($v,"'",1) !== 0) $execp .= $this->qstr($v);
00730                                 } else {
00731                                         $execp .= $v;
00732                                 }
00733                         }
00734                         
00735                         if ($execp) $exsql = "EXECUTE $plan ($execp)";
00736                         else $exsql = "EXECUTE $plan";
00737                         
00738                         
00739                         $rez = @pg_exec($this->_connectionID,$exsql);
00740                         if (!$rez) {
00741                         # Perhaps plan does not exist? Prepare/compile plan.
00742                                 $params = '';
00743                                 foreach($inputarr as $v) {
00744                                         if ($params) $params .= ',';
00745                                         if (is_string($v)) {
00746                                                 $params .= 'VARCHAR';
00747                                         } else if (is_integer($v)) {
00748                                                 $params .= 'INTEGER';
00749                                         } else {
00750                                                 $params .= "REAL";
00751                                         }
00752                                 }
00753                                 $sqlarr = explode('?',$sql);
00754                                 //print_r($sqlarr);
00755                                 $sql = '';
00756                                 $i = 1;
00757                                 foreach($sqlarr as $v) {
00758                                         $sql .= $v.' $'.$i;
00759                                         $i++;
00760                                 }
00761                                 $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2);               
00762                                 //adodb_pr($s);
00763                                 pg_exec($this->_connectionID,$s);
00764                                 //echo $this->ErrorMsg();
00765                         }
00766                         
00767                         $rez = pg_exec($this->_connectionID,$exsql);
00768                 } else {
00769                         //adodb_backtrace();
00770                         $rez = pg_exec($this->_connectionID,$sql);
00771                 }
00772                 // check if no data returned, then no need to create real recordset
00773                 if ($rez && pg_numfields($rez) <= 0) {
00774                         if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') {
00775                                 pg_freeresult($this->_resultid);
00776                         }
00777                         $this->_resultid = $rez;
00778                         return true;
00779                 }
00780                 
00781                 return $rez;
00782         }
00783         
00784         function _errconnect()
00785         {
00786                 if (defined('DB_ERROR_CONNECT_FAILED')) return DB_ERROR_CONNECT_FAILED;
00787                 else return 'Database connection failed';
00788         }
00789 
00790         /*      Returns: the last error message from previous database operation        */      
00791         function ErrorMsg() 
00792         {
00793                 if ($this->_errorMsg !== false) return $this->_errorMsg;
00794                 if (ADODB_PHPVER >= 0x4300) {
00795                         if (!empty($this->_resultid)) {
00796                                 $this->_errorMsg = @pg_result_error($this->_resultid);
00797                                 if ($this->_errorMsg) return $this->_errorMsg;
00798                         }
00799                         
00800                         if (!empty($this->_connectionID)) {
00801                                 $this->_errorMsg = @pg_last_error($this->_connectionID);
00802                         } else $this->_errorMsg = $this->_errconnect();
00803                 } else {
00804                         if (empty($this->_connectionID)) $this->_errconnect();
00805                         else $this->_errorMsg = @pg_errormessage($this->_connectionID);
00806                 }
00807                 return $this->_errorMsg;
00808         }
00809         
00810         function ErrorNo()
00811         {
00812                 $e = $this->ErrorMsg();
00813                 if (strlen($e)) {
00814                         return ADOConnection::MetaError($e);
00815                  }
00816                  return 0;
00817         }
00818 
00819         // returns true or false
00820         function _close()
00821         {
00822                 if ($this->transCnt) $this->RollbackTrans();
00823                 if ($this->_resultid) {
00824                         @pg_freeresult($this->_resultid);
00825                         $this->_resultid = false;
00826                 }
00827                 @pg_close($this->_connectionID);
00828                 $this->_connectionID = false;
00829                 return true;
00830         }
00831         
00832         
00833         /*
00834         * Maximum size of C field
00835         */
00836         function CharMax()
00837         {
00838                 return 1000000000;  // should be 1 Gb?
00839         }
00840         
00841         /*
00842         * Maximum size of X field
00843         */
00844         function TextMax()
00845         {
00846                 return 1000000000; // should be 1 Gb?
00847         }
00848         
00849                 
00850 }
00851         
00852 /*--------------------------------------------------------------------------------------
00853          Class Name: Recordset
00854 --------------------------------------------------------------------------------------*/
00855 
00856 class ADORecordSet_postgres64 extends ADORecordSet{
00857         var $_blobArr;
00858         var $databaseType = "postgres64";
00859         var $canSeek = true;
00860         function ADORecordSet_postgres64($queryID,$mode=false) 
00861         {
00862                 if ($mode === false) { 
00863                         global $ADODB_FETCH_MODE;
00864                         $mode = $ADODB_FETCH_MODE;
00865                 }
00866                 switch ($mode)
00867                 {
00868                 case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break;
00869                 case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break;
00870                 
00871                 case ADODB_FETCH_DEFAULT:
00872                 case ADODB_FETCH_BOTH:
00873                 default: $this->fetchMode = PGSQL_BOTH; break;
00874                 }
00875                 $this->adodbFetchMode = $mode;
00876                 $this->ADORecordSet($queryID);
00877         }
00878         
00879         function &GetRowAssoc($upper=true)
00880         {
00881                 if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $this->fields;
00882                 $row =& ADORecordSet::GetRowAssoc($upper);
00883                 return $row;
00884         }
00885 
00886         function _initrs()
00887         {
00888         global $ADODB_COUNTRECS;
00889                 $qid = $this->_queryID;
00890                 $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($qid):-1;
00891                 $this->_numOfFields = @pg_numfields($qid);
00892                 
00893                 // cache types for blob decode check
00894                 // apparently pg_fieldtype actually performs an sql query on the database to get the type.
00895                 if (empty($this->connection->noBlobs))
00896                 for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {  
00897                         if (pg_fieldtype($qid,$i) == 'bytea') {
00898                                 $this->_blobArr[$i] = pg_fieldname($qid,$i);
00899                         }
00900                 }
00901         }
00902 
00903                 /* Use associative array to get fields array */
00904         function Fields($colname)
00905         {
00906                 if ($this->fetchMode != PGSQL_NUM) return @$this->fields[$colname];
00907                 
00908                 if (!$this->bind) {
00909                         $this->bind = array();
00910                         for ($i=0; $i < $this->_numOfFields; $i++) {
00911                                 $o = $this->FetchField($i);
00912                                 $this->bind[strtoupper($o->name)] = $i;
00913                         }
00914                 }
00915                  return $this->fields[$this->bind[strtoupper($colname)]];
00916         }
00917 
00918         function &FetchField($off = 0) 
00919         {
00920                 // offsets begin at 0
00921                 
00922                 $o= new ADOFieldObject();
00923                 $o->name = @pg_fieldname($this->_queryID,$off);
00924                 $o->type = @pg_fieldtype($this->_queryID,$off);
00925                 $o->max_length = @pg_fieldsize($this->_queryID,$off);
00926                 return $o;      
00927         }
00928 
00929         function _seek($row)
00930         {
00931                 return @pg_fetch_row($this->_queryID,$row);
00932         }
00933         
00934         function _decode($blob)
00935         {
00936                 eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";');
00937                 return $realblob;       
00938         }
00939         
00940         function _fixblobs()
00941         {
00942                 if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) {
00943                         foreach($this->_blobArr as $k => $v) {
00944                                 $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]);
00945                         }
00946                 }
00947                 if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) {
00948                         foreach($this->_blobArr as $k => $v) {
00949                                 $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]);
00950                         }
00951                 }
00952         }
00953         
00954         // 10% speedup to move MoveNext to child class
00955         function MoveNext() 
00956         {
00957                 if (!$this->EOF) {
00958                         $this->_currentRow++;
00959                         if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) {
00960                                 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
00961                                 if (is_array($this->fields) && $this->fields) {
00962                                         if (isset($this->_blobArr)) $this->_fixblobs();
00963                                         return true;
00964                                 }
00965                         }
00966                         $this->fields = false;
00967                         $this->EOF = true;
00968                 }
00969                 return false;
00970         }               
00971         
00972         function _fetch()
00973         {
00974                                 
00975                 if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0)
00976                 return false;
00977 
00978                 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
00979                 
00980                 if ($this->fields && isset($this->_blobArr)) $this->_fixblobs();
00981                         
00982                 return (is_array($this->fields));
00983         }
00984 
00985         function _close() 
00986         { 
00987                 return @pg_freeresult($this->_queryID);
00988         }
00989 
00990         function MetaType($t,$len=-1,$fieldobj=false)
00991         {
00992                 if (is_object($t)) {
00993                         $fieldobj = $t;
00994                         $t = $fieldobj->type;
00995                         $len = $fieldobj->max_length;
00996                 }
00997                 switch (strtoupper($t)) {
00998                                 case 'MONEY': // stupid, postgres expects money to be a string
00999                                 case 'INTERVAL':
01000                                 case 'CHAR':
01001                                 case 'CHARACTER':
01002                                 case 'VARCHAR':
01003                                 case 'NAME':
01004                                 case 'BPCHAR':
01005                                 case '_VARCHAR':
01006                                 case 'INET':
01007                                 case 'MACADDR':
01008                                         if ($len <= $this->blobSize) return 'C';
01009                                 
01010                                 case 'TEXT':
01011                                         return 'X';
01012                 
01013                                 case 'IMAGE': // user defined type
01014                                 case 'BLOB': // user defined type
01015                                 case 'BIT':     // This is a bit string, not a single bit, so don't return 'L'
01016                                 case 'VARBIT':
01017                                 case 'BYTEA':
01018                                         return 'B';
01019                                 
01020                                 case 'BOOL':
01021                                 case 'BOOLEAN':
01022                                         return 'L';
01023                                 
01024                                 case 'DATE':
01025                                         return 'D';
01026                                 
01027                                 
01028                                 case 'TIMESTAMP WITHOUT TIME ZONE':
01029                                 case 'TIME':
01030                                 case 'DATETIME':
01031                                 case 'TIMESTAMP':
01032                                 case 'TIMESTAMPTZ':
01033                                         return 'T';
01034                                 
01035                                 case 'SMALLINT': 
01036                                 case 'BIGINT': 
01037                                 case 'INTEGER': 
01038                                 case 'INT8': 
01039                                 case 'INT4':
01040                                 case 'INT2':
01041                                         if (isset($fieldobj) &&
01042                                 empty($fieldobj->primary_key) && empty($fieldobj->unique)) return 'I';
01043                                 
01044                                 case 'OID':
01045                                 case 'SERIAL':
01046                                         return 'R';
01047                                 
01048                                  default:
01049                                         return 'N';
01050                         }
01051         }
01052 
01053 }
01054 ?>