]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/adodb/drivers/adodb-postgres64.inc.php
split is deprecated, replace with explode
[SourceForge/phpwiki.git] / lib / WikiDB / adodb / drivers / adodb-postgres64.inc.php
1 <?php
2 /*
3  V4.22 15 Apr 2004  (c) 2000-2004 John Lim (jlim@natsoft.com.my). All rights reserved.
4   Released under both BSD license and Lesser GPL library license. 
5   Whenever there is any discrepancy between the two licenses, 
6   the BSD license will take precedence.
7   Set tabs to 8.
8   
9   Original version derived from Alberto Cerezal (acerezalp@dbnet.es) - DBNet Informatica & Comunicaciones. 
10   08 Nov 2000 jlim - Minor corrections, removing mysql stuff
11   09 Nov 2000 jlim - added insertid support suggested by "Christopher Kings-Lynne" <chriskl@familyhealth.com.au>
12                                         jlim - changed concat operator to || and data types to MetaType to match documented pgsql types 
13                         see http://www.postgresql.org/devel-corner/docs/postgres/datatype.htm  
14   22 Nov 2000 jlim - added changes to FetchField() and MetaTables() contributed by "raser" <raser@mail.zen.com.tw>
15   27 Nov 2000 jlim - added changes to _connect/_pconnect from ideas by "Lennie" <leen@wirehub.nl>
16   15 Dec 2000 jlim - added changes suggested by Additional code changes by "Eric G. Werk" egw@netguide.dk. 
17   31 Jan 2002 jlim - finally installed postgresql. testing
18   01 Mar 2001 jlim - Freek Dijkstra changes, also support for text type
19   
20   See http://www.varlena.com/varlena/GeneralBits/47.php
21   
22         -- What indexes are on my table?
23         select * from pg_indexes where tablename = 'tablename';
24         
25         -- What triggers are on my table?
26         select c.relname as "Table", t.tgname as "Trigger Name", 
27            t.tgconstrname as "Constraint Name", t.tgenabled as "Enabled",
28            t.tgisconstraint as "Is Constraint", cc.relname as "Referenced Table",
29            p.proname as "Function Name"
30         from pg_trigger t, pg_class c, pg_class cc, pg_proc p
31         where t.tgfoid = p.oid and t.tgrelid = c.oid
32            and t.tgconstrrelid = cc.oid
33            and c.relname = 'tablename';
34         
35         -- What constraints are on my table?
36         select r.relname as "Table", c.conname as "Constraint Name",
37            contype as "Constraint Type", conkey as "Key Columns",
38            confkey as "Foreign Columns", consrc as "Source"
39         from pg_class r, pg_constraint c
40         where r.oid = c.conrelid
41            and relname = 'tablename';
42
43 */
44
45 function adodb_addslashes($s)
46 {
47         $len = strlen($s);
48         if ($len == 0) return "''";
49         if (strncmp($s,"'",1) === 0 && substr(s,$len-1) == "'") return $s; // already quoted
50         
51         return "'".addslashes($s)."'";
52 }
53
54 class ADODB_postgres64 extends ADOConnection{
55         var $databaseType = 'postgres64';
56         var $dataProvider = 'postgres';
57         var $hasInsertID = true;
58         var $_resultid = false;
59         var $concat_operator='||';
60         var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1";
61     var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' union 
62         select viewname,'V' from pg_views where viewname not like 'pg\_%'";
63         //"select tablename from pg_tables where tablename not like 'pg_%' order by 1";
64         var $isoDates = true; // accepts dates in ISO format
65         var $sysDate = "CURRENT_DATE";
66         var $sysTimeStamp = "CURRENT_TIMESTAMP";
67         var $blobEncodeType = 'C';
68         var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum 
69                 FROM pg_class c, pg_attribute a,pg_type t 
70                 WHERE relkind = 'r' AND (c.relname='%s' or c.relname = lower('%s')) and a.attname not like '....%%'
71 AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
72
73         var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum 
74 FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n 
75 WHERE relkind = 'r' AND (c.relname='%s' or c.relname = lower('%s'))
76  and c.relnamespace=n.oid and n.nspname='%s' 
77         and a.attname not like '....%%' AND a.attnum > 0 
78         AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
79         
80         // get primary key etc -- from Freek Dijkstra
81         var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key 
82         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'";
83         
84         var $hasAffectedRows = true;
85         var $hasLimit = false;  // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10
86         // below suggested by Freek Dijkstra 
87         var $true = 't';                // string that represents TRUE for a database
88         var $false = 'f';               // string that represents FALSE for a database
89         var $fmtDate = "'Y-m-d'";       // used by DBDate() as the default date format used by the database
90         var $fmtTimeStamp = "'Y-m-d G:i:s'"; // used by DBTimeStamp as the default timestamp fmt.
91         var $hasMoveFirst = true;
92         var $hasGenID = true;
93         var $_genIDSQL = "SELECT NEXTVAL('%s')";
94         var $_genSeqSQL = "CREATE SEQUENCE %s START %s";
95         var $_dropSeqSQL = "DROP SEQUENCE %s";
96         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";
97         var $random = 'random()';               /// random function
98         var $autoRollback = true; // apparently pgsql does not autorollback properly before 4.3.4
99                                                         // http://bugs.php.net/bug.php?id=25404
100                                                         
101         var $_bindInputArray = false; // requires postgresql 7.3+ and ability to modify database
102         
103         // The last (fmtTimeStamp is not entirely correct: 
104         // PostgreSQL also has support for time zones, 
105         // and writes these time in this format: "2001-03-01 18:59:26+02". 
106         // There is no code for the "+02" time zone information, so I just left that out. 
107         // I'm not familiar enough with both ADODB as well as Postgres 
108         // to know what the concequences are. The other values are correct (wheren't in 0.94)
109         // -- Freek Dijkstra 
110
111         function ADODB_postgres64() 
112         {
113         // changes the metaColumnsSQL, adds columns: attnum[6]
114         }
115         
116         function ServerInfo()
117         {
118                 if (isset($this->version)) return $this->version;
119                 
120                 $arr['description'] = $this->GetOne("select version()");
121                 $arr['version'] = ADOConnection::_findvers($arr['description']);
122                 $this->version = $arr;
123                 return $arr;
124         }
125 /*
126         function IfNull( $field, $ifNull ) 
127         {
128                 return " NULLIF($field, $ifNull) "; // if PGSQL
129         }
130 */
131         // get the last id - never tested
132         function pg_insert_id($tablename,$fieldname)
133         {
134                 $result=pg_exec($this->_connectionID, "SELECT last_value FROM ${tablename}_${fieldname}_seq");
135                 if ($result) {
136                         $arr = @pg_fetch_row($result,0);
137                         pg_freeresult($result);
138                         if (isset($arr[0])) return $arr[0];
139                 }
140                 return false;
141         }
142         
143 /* Warning from http://www.php.net/manual/function.pg-getlastoid.php:
144 Using a OID as a unique identifier is not generally wise. 
145 Unless you are very careful, you might end up with a tuple having 
146 a different OID if a database must be reloaded. */
147         function _insertid()
148         {
149                 if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false;
150                 return pg_getlastoid($this->_resultid);
151         }
152
153 // I get this error with PHP before 4.0.6 - jlim
154 // Warning: This compilation does not support pg_cmdtuples() in d:/inetpub/wwwroot/php/adodb/adodb-postgres.inc.php on line 44
155    function _affectedrows()
156    {
157                 if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false;
158                 return pg_cmdtuples($this->_resultid);
159    }
160    
161         
162                 // returns true/false
163         function BeginTrans()
164         {
165                 if ($this->transOff) return true;
166                 $this->transCnt += 1;
167                 return @pg_Exec($this->_connectionID, "begin");
168         }
169         
170         function RowLock($tables,$where) 
171         {
172                 if (!$this->transCnt) $this->BeginTrans();
173                 return $this->GetOne("select 1 as ignore from $tables where $where for update");
174         }
175
176         // returns true/false. 
177         function CommitTrans($ok=true) 
178         { 
179                 if ($this->transOff) return true;
180                 if (!$ok) return $this->RollbackTrans();
181                 
182                 $this->transCnt -= 1;
183                 return @pg_Exec($this->_connectionID, "commit");
184         }
185         
186         // returns true/false
187         function RollbackTrans()
188         {
189                 if ($this->transOff) return true;
190                 $this->transCnt -= 1;
191                 return @pg_Exec($this->_connectionID, "rollback");
192         }
193         
194         function &MetaTables($ttype=false,$showSchema=false,$mask=false) 
195         {       
196                 if ($mask) {
197                         $save = $this->metaTablesSQL;
198                         $mask = $this->qstr(strtolower($mask));
199                         $this->metaTablesSQL = "
200 select tablename,'T' from pg_tables where tablename like $mask union 
201 select viewname,'V' from pg_views where viewname like $mask";
202                 }
203                 $ret =& ADOConnection::MetaTables($ttype,$showSchema);
204                 
205                 if ($mask) {
206                         $this->metaTablesSQL = $save;
207                 }
208                 return $ret;
209         }
210         
211         /*
212         // if magic quotes disabled, use pg_escape_string()
213         function qstr($s,$magic_quotes=false)
214         {
215                 if (!$magic_quotes) {
216                         if (ADODB_PHPVER >= 0x4200) {
217                                 return  "'".pg_escape_string($s)."'";
218                         }
219                         if ($this->replaceQuote[0] == '\\'){
220                                 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
221                         }
222                         return  "'".str_replace("'",$this->replaceQuote,$s)."'"; 
223                 }
224                 
225                 // undo magic quotes for "
226                 $s = str_replace('\\"','"',$s);
227                 return "'$s'";
228         }
229         */
230         
231         
232         // Format date column in sql string given an input format that understands Y M D
233         function SQLDate($fmt, $col=false)
234         {       
235                 if (!$col) $col = $this->sysTimeStamp;
236                 $s = 'TO_CHAR('.$col.",'";
237                 
238                 $len = strlen($fmt);
239                 for ($i=0; $i < $len; $i++) {
240                         $ch = $fmt[$i];
241                         switch($ch) {
242                         case 'Y':
243                         case 'y':
244                                 $s .= 'YYYY';
245                                 break;
246                         case 'Q':
247                         case 'q':
248                                 $s .= 'Q';
249                                 break;
250                                 
251                         case 'M':
252                                 $s .= 'Mon';
253                                 break;
254                                 
255                         case 'm':
256                                 $s .= 'MM';
257                                 break;
258                         case 'D':
259                         case 'd':
260                                 $s .= 'DD';
261                                 break;
262                         
263                         case 'H':
264                                 $s.= 'HH24';
265                                 break;
266                                 
267                         case 'h':
268                                 $s .= 'HH';
269                                 break;
270                                 
271                         case 'i':
272                                 $s .= 'MI';
273                                 break;
274                         
275                         case 's':
276                                 $s .= 'SS';
277                                 break;
278                         
279                         case 'a':
280                         case 'A':
281                                 $s .= 'AM';
282                                 break;
283                                 
284                         default:
285                         // handle escape characters...
286                                 if ($ch == '\\') {
287                                         $i++;
288                                         $ch = substr($fmt,$i,1);
289                                 }
290                                 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
291                                 else $s .= '"'.$ch.'"';
292                                 
293                         }
294                 }
295                 return $s. "')";
296         }
297         
298         
299         
300         /* 
301         * Load a Large Object from a file 
302         * - the procedure stores the object id in the table and imports the object using 
303         * postgres proprietary blob handling routines 
304         *
305         * contributed by Mattia Rossi mattia@technologist.com
306         * modified for safe mode by juraj chlebec
307         */ 
308         function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') 
309         { 
310                 pg_exec ($this->_connectionID, "begin"); 
311                 
312                 $fd = fopen($path,'r');
313                 $contents = fread($fd,filesize($path));
314                 fclose($fd);
315                 
316                 $oid = pg_lo_create($this->_connectionID);
317                 $handle = pg_lo_open($this->_connectionID, $oid, 'w');
318                 pg_lo_write($handle, $contents);
319                 pg_lo_close($handle);
320                 
321                 // $oid = pg_lo_import ($path); 
322                 pg_exec($this->_connectionID, "commit"); 
323                 $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype); 
324                 $rez = !empty($rs); 
325                 return $rez; 
326         } 
327         
328         /* 
329         * If an OID is detected, then we use pg_lo_* to open the oid file and read the
330         * real blob from the db using the oid supplied as a parameter. If you are storing
331         * blobs using bytea, we autodetect and process it so this function is not needed.
332         *
333         * contributed by Mattia Rossi mattia@technologist.com
334         *
335         * see http://www.postgresql.org/idocs/index.php?largeobjects.html
336         */ 
337         function BlobDecode( $blob) 
338         { 
339                 if (strlen($blob) > 24) return $blob;
340                 
341                 @pg_exec($this->_connectionID,"begin"); 
342                 $fd = @pg_lo_open($this->_connectionID,$blob,"r");
343                 if ($fd === false) {
344                         @pg_exec($this->_connectionID,"commit");
345                         return $blob;
346                 }
347                 $realblob = @pg_loreadall($fd); 
348                 @pg_loclose($fd); 
349                 @pg_exec($this->_connectionID,"commit"); 
350                 return $realblob;
351         } 
352         
353         /* 
354                 See http://www.postgresql.org/idocs/index.php?datatype-binary.html
355                 
356                 NOTE: SQL string literals (input strings) must be preceded with two backslashes 
357                 due to the fact that they must pass through two parsers in the PostgreSQL 
358                 backend.
359         */
360         function BlobEncode($blob)
361         {
362                 if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob);
363                 
364                 /*92=backslash, 0=null, 39=single-quote*/
365                 $badch = array(chr(92),chr(0),chr(39)); # \  null  '
366                 $fixch = array('\\\\134','\\\\000','\\\\047');
367                 return adodb_str_replace($badch,$fixch,$blob);
368                 
369                 // note that there is a pg_escape_bytea function only for php 4.2.0 or later
370         }
371         
372         function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
373         {
374                 // do not use bind params which uses qstr(), as blobencode() already quotes data
375                 return $this->Execute("UPDATE $table SET $column='".$this->BlobEncode($val)."'::bytea WHERE $where");
376         }
377         
378         function OffsetDate($dayFraction,$date=false)
379         {               
380                 if (!$date) $date = $this->sysDate;
381                 return "($date+interval'$dayFraction days')";
382         }
383         
384
385         // for schema support, pass in the $table param "$schema.$tabname".
386         // converts field names to lowercase, $upper is ignored
387         function &MetaColumns($table,$upper=true) 
388         {
389         global $ADODB_FETCH_MODE;
390         
391                 $schema = false;
392                 $this->_findschema($table,$schema);
393                 
394                 $table = strtolower($table);
395
396                 $save = $ADODB_FETCH_MODE;
397                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
398                 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
399                 
400                 if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema));
401                 else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table));
402                 if (isset($savem)) $this->SetFetchMode($savem);
403                 $ADODB_FETCH_MODE = $save;
404                 
405                 if ($rs === false) return false;
406                 
407                 if (!empty($this->metaKeySQL)) {
408                         // If we want the primary keys, we have to issue a separate query
409                         // Of course, a modified version of the metaColumnsSQL query using a 
410                         // LEFT JOIN would have been much more elegant, but postgres does 
411                         // not support OUTER JOINS. So here is the clumsy way.
412                         
413                         $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
414                         
415                         $rskey = $this->Execute(sprintf($this->metaKeySQL,($table)));
416                         // fetch all result in once for performance.
417                         $keys =& $rskey->GetArray();
418                         if (isset($savem)) $this->SetFetchMode($savem);
419                         $ADODB_FETCH_MODE = $save;
420                         
421                         $rskey->Close();
422                         unset($rskey);
423                 }
424
425                 $rsdefa = array();
426                 if (!empty($this->metaDefaultsSQL)) {
427                         $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
428                         $sql = sprintf($this->metaDefaultsSQL, ($table));
429                         $rsdef = $this->Execute($sql);
430                         if (isset($savem)) $this->SetFetchMode($savem);
431                         $ADODB_FETCH_MODE = $save;
432                         
433                         if ($rsdef) {
434                                 while (!$rsdef->EOF) {
435                                         $num = $rsdef->fields['num'];
436                                         $s = $rsdef->fields['def'];
437                                         if (substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */
438                                                 $s = substr($s, 1);
439                                                 $s = substr($s, 0, strlen($s) - 1);
440                                         }
441
442                                         $rsdefa[$num] = $s;
443                                         $rsdef->MoveNext();
444                                 }
445                         } else {
446                                 ADOConnection::outp( "==> SQL => " . $sql);
447                         }
448                         unset($rsdef);
449                 }
450         
451                 $retarr = array();
452                 while (!$rs->EOF) {     
453                         $fld = new ADOFieldObject();
454                         $fld->name = $rs->fields[0];
455                         $fld->type = $rs->fields[1];
456                         $fld->max_length = $rs->fields[2];
457                         if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4;
458                         if ($fld->max_length <= 0) $fld->max_length = -1;
459                         
460                         // dannym
461                         // 5 hasdefault; 6 num-of-column
462                         $fld->has_default = ($rs->fields[5] == 't');
463                         if ($fld->has_default) {
464                                 $fld->default_value = $rsdefa[$rs->fields[6]];
465                         }
466
467                         //Freek
468                         if ($rs->fields[4] == $this->true) {
469                                 $fld->not_null = true;
470                         }
471                         
472                         // Freek
473                         if (is_array($keys)) {
474                                 foreach($keys as $key) {
475                                         if ($fld->name == $key['column_name'] AND $key['primary_key'] == $this->true) 
476                                                 $fld->primary_key = true;
477                                         if ($fld->name == $key['column_name'] AND $key['unique_key'] == $this->true) 
478                                                 $fld->unique = true; // What name is more compatible?
479                                 }
480                         }
481                         
482                         if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;     
483                         else $retarr[($upper) ? strtoupper($fld->name) : $fld->name] = $fld;
484                         
485                         $rs->MoveNext();
486                 }
487                 $rs->Close();
488                 return $retarr; 
489                 
490         }
491
492           function &MetaIndexes ($table, $primary = FALSE)
493       {
494          global $ADODB_FETCH_MODE;
495                 
496                                 $schema = false;
497                                 $this->_findschema($table,$schema);
498                                 
499                                 if ($schema) { // requires pgsql 7.3+ - pg_namespace used.
500                                         $sql = '
501 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" 
502 FROM pg_catalog.pg_class c 
503 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid 
504 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
505         ,pg_namespace n 
506 WHERE c2.relname=\'%s\' and c.relnamespace=c2.relnamespace and c.relnamespace=n.oid and n.nspname=\'%s\' AND i.indisprimary=false';
507                                 } else {
508                         $sql = '
509 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
510 FROM pg_catalog.pg_class c
511 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
512 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
513 WHERE c2.relname=\'%s\'';
514                         }
515                                             
516                 if ($primary == FALSE) {
517                         $sql .= ' AND i.indisprimary=false;';
518                 }
519                 
520                 $save = $ADODB_FETCH_MODE;
521                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
522                 if ($this->fetchMode !== FALSE) {
523                         $savem = $this->SetFetchMode(FALSE);
524                 }
525                 
526                 $rs = $this->Execute(sprintf($sql,$table,$schema));
527                 
528                 if (isset($savem)) {
529                         $this->SetFetchMode($savem);
530                 }
531                 $ADODB_FETCH_MODE = $save;
532                 
533                 if (!is_object($rs)) {
534                         return FALSE;
535                 }
536                 
537                 $col_names = $this->MetaColumnNames($table);
538                 $indexes = array();
539                 
540                 while ($row = $rs->FetchRow()) {
541                         $columns = array();
542                         
543                         foreach (explode(' ', $row[2]) as $col) {
544                                 $columns[] = $col_names[$col - 1];
545                         }
546                         
547                         $indexes[$row[0]] = array(
548                                 'unique' => ($row[1] == 't'),
549                                 'columns' => $columns
550                         );
551                 }
552                 
553                 return $indexes;
554         }
555
556         // returns true or false
557         //
558         // examples:
559         //      $db->Connect("host=host1 user=user1 password=secret port=4341");
560         //      $db->Connect('host1','user1','secret');
561         function _connect($str,$user='',$pwd='',$db='',$ctype=0)
562         {
563                 
564                 if (!function_exists('pg_pconnect')) return false;
565                 
566                 $this->_errorMsg = false;
567                 
568                 if ($user || $pwd || $db) {
569                         $user = adodb_addslashes($user);
570                         $pwd = adodb_addslashes($pwd);
571                         if (strlen($db) == 0) $db = 'template1';
572                         $db = adodb_addslashes($db);
573                         if ($str)  {
574                                 $host = explode(":", $str);
575                                 if ($host[0]) $str = "host=".adodb_addslashes($host[0]);
576                                 else $str = 'host=localhost';
577                                 if (isset($host[1])) $str .= " port=$host[1]";
578                         }
579                                 if ($user) $str .= " user=".$user;
580                                 if ($pwd)  $str .= " password=".$pwd;
581                                 if ($db)   $str .= " dbname=".$db;
582                 }
583
584                 //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432";
585                 
586                 if ($ctype === 1) { // persistent
587                         $this->_connectionID = pg_pconnect($str);
588                 } else {
589                         if ($ctype === -1) { // nconnect, we trick pgsql ext by changing the connection str
590                         static $ncnt;
591                         
592                                 if (empty($ncnt)) $ncnt = 1;
593                                 else $ncnt += 1;
594                                 
595                                 $str .= str_repeat(' ',$ncnt);
596                         }
597                         $this->_connectionID = pg_connect($str);
598                 }
599                 if ($this->_connectionID === false) return false;
600                 $this->Execute("set datestyle='ISO'");
601                 return true;
602         }
603         
604         function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
605         {
606                 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName,-1);
607         }
608          
609         // returns true or false
610         //
611         // examples:
612         //      $db->PConnect("host=host1 user=user1 password=secret port=4341");
613         //      $db->PConnect('host1','user1','secret');
614         function _pconnect($str,$user='',$pwd='',$db='')
615         {
616                 return $this->_connect($str,$user,$pwd,$db,1);
617         }
618         
619
620         // returns queryID or false
621         function _query($sql,$inputarr)
622         {
623                 
624                 if ($inputarr) {
625                 /*
626                         It appears that PREPARE/EXECUTE is slower for many queries.
627                         
628                         For query executed 1000 times:
629                         "select id,firstname,lastname from adoxyz 
630                                 where firstname not like ? and lastname not like ? and id = ?"
631                                 
632                         with plan = 1.51861286163 secs
633                         no plan =   1.26903700829 secs
634
635                         
636
637                 */
638                         $plan = 'P'.md5($sql);
639                                 
640                         $execp = '';
641                         foreach($inputarr as $v) {
642                                 if ($execp) $execp .= ',';
643                                 if (is_string($v)) {
644                                         if (strncmp($v,"'",1) !== 0) $execp .= $this->qstr($v);
645                                 } else {
646                                         $execp .= $v;
647                                 }
648                         }
649                         
650                         if ($execp) $exsql = "EXECUTE $plan ($execp)";
651                         else $exsql = "EXECUTE $plan";
652                         
653                         $rez = @pg_exec($this->_connectionID,$exsql);
654                         if (!$rez) {
655                         # Perhaps plan does not exist? Prepare/compile plan.
656                                 $params = '';
657                                 foreach($inputarr as $v) {
658                                         if ($params) $params .= ',';
659                                         if (is_string($v)) {
660                                                 $params .= 'VARCHAR';
661                                         } else if (is_integer($v)) {
662                                                 $params .= 'INTEGER';
663                                         } else {
664                                                 $params .= "REAL";
665                                         }
666                                 }
667                                 $sqlarr = explode('?',$sql);
668                                 //print_r($sqlarr);
669                                 $sql = '';
670                                 $i = 1;
671                                 foreach($sqlarr as $v) {
672                                         $sql .= $v.' $'.$i;
673                                         $i++;
674                                 }
675                                 $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2);               
676                                 //adodb_pr($s);
677                                 pg_exec($this->_connectionID,$s);
678                                 echo $this->ErrorMsg();
679                         }
680                         
681                         $rez = pg_exec($this->_connectionID,$exsql);
682                 } else {
683                         $this->_errorMsg = false;
684                         //adodb_backtrace();
685                         $rez = pg_exec($this->_connectionID,$sql);
686                 }
687                 // check if no data returned, then no need to create real recordset
688                 if ($rez && pg_numfields($rez) <= 0) {
689                         if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') {
690                                 pg_freeresult($this->_resultid);
691                         }
692                         $this->_resultid = $rez;
693                         return true;
694                 }
695                 
696                 return $rez;
697         }
698         
699
700         /*      Returns: the last error message from previous database operation        */      
701         function ErrorMsg() 
702         {
703                 if ($this->_errorMsg !== false) return $this->_errorMsg;
704                 if (ADODB_PHPVER >= 0x4300) {
705                         if (!empty($this->_resultid)) {
706                                 $this->_errorMsg = @pg_result_error($this->_resultid);
707                                 if ($this->_errorMsg) return $this->_errorMsg;
708                         }
709                         
710                         if (!empty($this->_connectionID)) {
711                                 $this->_errorMsg = @pg_last_error($this->_connectionID);
712                         } else $this->_errorMsg = @pg_last_error();
713                 } else {
714                         if (empty($this->_connectionID)) $this->_errorMsg = @pg_errormessage();
715                         else $this->_errorMsg = @pg_errormessage($this->_connectionID);
716                 }
717                 return $this->_errorMsg;
718         }
719         
720         function ErrorNo()
721         {
722                 $e = $this->ErrorMsg();
723                 return strlen($e) ? $e : 0;
724         }
725
726         // returns true or false
727         function _close()
728         {
729                 if ($this->transCnt) $this->RollbackTrans();
730                 if ($this->_resultid) {
731                         @pg_freeresult($this->_resultid);
732                         $this->_resultid = false;
733                 }
734                 @pg_close($this->_connectionID);
735                 $this->_connectionID = false;
736                 return true;
737         }
738         
739         
740         /*
741         * Maximum size of C field
742         */
743         function CharMax()
744         {
745                 return 1000000000;  // should be 1 Gb?
746         }
747         
748         /*
749         * Maximum size of X field
750         */
751         function TextMax()
752         {
753                 return 1000000000; // should be 1 Gb?
754         }
755         
756                 
757 }
758         
759 /*--------------------------------------------------------------------------------------
760          Class Name: Recordset
761 --------------------------------------------------------------------------------------*/
762
763 class ADORecordSet_postgres64 extends ADORecordSet{
764         var $_blobArr;
765         var $databaseType = "postgres64";
766         var $canSeek = true;
767         function ADORecordSet_postgres64($queryID,$mode=false) 
768         {
769                 if ($mode === false) { 
770                         global $ADODB_FETCH_MODE;
771                         $mode = $ADODB_FETCH_MODE;
772                 }
773                 switch ($mode)
774                 {
775                 case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break;
776                 case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break;
777                 default:
778                 case ADODB_FETCH_DEFAULT:
779                 case ADODB_FETCH_BOTH:$this->fetchMode = PGSQL_BOTH; break;
780                 }
781                 $this->ADORecordSet($queryID);
782         }
783         
784         function &GetRowAssoc($upper=true)
785         {
786                 if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $this->fields;
787                 $row =& ADORecordSet::GetRowAssoc($upper);
788                 return $row;
789         }
790
791         function _initrs()
792         {
793         global $ADODB_COUNTRECS;
794                 $qid = $this->_queryID;
795                 $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($qid):-1;
796                 $this->_numOfFields = @pg_numfields($qid);
797                 
798                 // cache types for blob decode check
799                 for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {  
800                         if (pg_fieldtype($qid,$i) == 'bytea') {
801                                 $this->_blobArr[$i] = pg_fieldname($qid,$off);
802                         }
803                 }               
804         }
805
806                 /* Use associative array to get fields array */
807         function Fields($colname)
808         {
809                 if ($this->fetchMode != PGSQL_NUM) return @$this->fields[$colname];
810                 
811                 if (!$this->bind) {
812                         $this->bind = array();
813                         for ($i=0; $i < $this->_numOfFields; $i++) {
814                                 $o = $this->FetchField($i);
815                                 $this->bind[strtoupper($o->name)] = $i;
816                         }
817                 }
818                  return $this->fields[$this->bind[strtoupper($colname)]];
819         }
820
821         function &FetchField($fieldOffset = 0) 
822         {
823                 $off=$fieldOffset; // offsets begin at 0
824                 
825                 $o= new ADOFieldObject();
826                 $o->name = @pg_fieldname($this->_queryID,$off);
827                 $o->type = @pg_fieldtype($this->_queryID,$off);
828                 $o->max_length = @pg_fieldsize($this->_queryID,$off);
829                 return $o;      
830         }
831
832         function _seek($row)
833         {
834                 return @pg_fetch_row($this->_queryID,$row);
835         }
836         
837         function _decode($blob)
838         {
839                 eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";');
840                 return $realblob;       
841         }
842         
843         function _fixblobs()
844         {
845                 if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) {
846                         foreach($this->_blobArr as $k => $v) {
847                                 $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]);
848                         }
849                 }
850                 if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) {
851                         foreach($this->_blobArr as $k => $v) {
852                                 $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]);
853                         }
854                 }
855         }
856         
857         // 10% speedup to move MoveNext to child class
858         function MoveNext() 
859         {
860                 if (!$this->EOF) {
861                         $this->_currentRow++;
862                         if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) {
863                                 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
864                                 if (is_array($this->fields) && $this->fields) {
865                                         if ($this->fields && isset($this->_blobArr)) $this->_fixblobs();
866                                         return true;
867                                 }
868                         }
869                         $this->fields = false;
870                         $this->EOF = true;
871                 }
872                 return false;
873         }               
874         
875         function _fetch()
876         {
877                                 
878                 if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0)
879                 return false;
880
881                 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
882                 
883         if ($this->fields && isset($this->_blobArr)) $this->_fixblobs();
884                         
885                 return (is_array($this->fields));
886         }
887
888         function _close() 
889         { 
890                 return @pg_freeresult($this->_queryID);
891         }
892
893         function MetaType($t,$len=-1,$fieldobj=false)
894         {
895                 if (is_object($t)) {
896                         $fieldobj = $t;
897                         $t = $fieldobj->type;
898                         $len = $fieldobj->max_length;
899                 }
900                 switch (strtoupper($t)) {
901                                 case 'MONEY': // stupid, postgres expects money to be a string
902                                 case 'INTERVAL':
903                                 case 'CHAR':
904                                 case 'CHARACTER':
905                                 case 'VARCHAR':
906                                 case 'NAME':
907                                 case 'BPCHAR':
908                                 case '_VARCHAR':
909                                         if ($len <= $this->blobSize) return 'C';
910                                 
911                                 case 'TEXT':
912                                         return 'X';
913                 
914                                 case 'IMAGE': // user defined type
915                                 case 'BLOB': // user defined type
916                                 case 'BIT':     // This is a bit string, not a single bit, so don't return 'L'
917                                 case 'VARBIT':
918                                 case 'BYTEA':
919                                         return 'B';
920                                 
921                                 case 'BOOL':
922                                 case 'BOOLEAN':
923                                         return 'L';
924                                 
925                                 case 'DATE':
926                                         return 'D';
927                                 
928                                 case 'TIME':
929                                 case 'DATETIME':
930                                 case 'TIMESTAMP':
931                                 case 'TIMESTAMPTZ':
932                                         return 'T';
933                                 
934                                 case 'SMALLINT': 
935                                 case 'BIGINT': 
936                                 case 'INTEGER': 
937                                 case 'INT8': 
938                                 case 'INT4':
939                                 case 'INT2':
940                                         if (isset($fieldobj) &&
941                                 empty($fieldobj->primary_key) && empty($fieldobj->unique)) return 'I';
942                                 
943                                 case 'OID':
944                                 case 'SERIAL':
945                                         return 'R';
946                                 
947                                  default:
948                                         return 'N';
949                         }
950         }
951
952 }
953 ?>