]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/adodb/drivers/adodb-postgres64.inc.php
No tabs
[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 ?>