]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/adodb/drivers/adodb-postgres64.inc.php
elseif
[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         // returns true/false
162     function BeginTrans()
163     {
164         if ($this->transOff) return true;
165         $this->transCnt += 1;
166         return @pg_Exec($this->_connectionID, "begin");
167     }
168
169     function RowLock($tables,$where)
170     {
171         if (!$this->transCnt) $this->BeginTrans();
172         return $this->GetOne("select 1 as ignore from $tables where $where for update");
173     }
174
175     // returns true/false.
176     function CommitTrans($ok=true)
177     {
178         if ($this->transOff) return true;
179         if (!$ok) return $this->RollbackTrans();
180
181         $this->transCnt -= 1;
182         return @pg_Exec($this->_connectionID, "commit");
183     }
184
185     // returns true/false
186     function RollbackTrans()
187     {
188         if ($this->transOff) return true;
189         $this->transCnt -= 1;
190         return @pg_Exec($this->_connectionID, "rollback");
191     }
192
193     function &MetaTables($ttype=false,$showSchema=false,$mask=false)
194     {
195         if ($mask) {
196             $save = $this->metaTablesSQL;
197             $mask = $this->qstr(strtolower($mask));
198             $this->metaTablesSQL = "
199 select tablename,'T' from pg_tables where tablename like $mask union
200 select viewname,'V' from pg_views where viewname like $mask";
201         }
202         $ret =& ADOConnection::MetaTables($ttype,$showSchema);
203
204         if ($mask) {
205             $this->metaTablesSQL = $save;
206         }
207         return $ret;
208     }
209
210     /*
211     // if magic quotes disabled, use pg_escape_string()
212     function qstr($s,$magic_quotes=false)
213     {
214         if (!$magic_quotes) {
215             if (ADODB_PHPVER >= 0x4200) {
216                 return  "'".pg_escape_string($s)."'";
217             }
218             if ($this->replaceQuote[0] == '\\'){
219                 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
220             }
221             return  "'".str_replace("'",$this->replaceQuote,$s)."'";
222         }
223
224         // undo magic quotes for "
225         $s = str_replace('\\"','"',$s);
226         return "'$s'";
227     }
228     */
229
230
231     // Format date column in sql string given an input format that understands Y M D
232     function SQLDate($fmt, $col=false)
233     {
234         if (!$col) $col = $this->sysTimeStamp;
235         $s = 'TO_CHAR('.$col.",'";
236
237         $len = strlen($fmt);
238         for ($i=0; $i < $len; $i++) {
239             $ch = $fmt[$i];
240             switch($ch) {
241             case 'Y':
242             case 'y':
243                 $s .= 'YYYY';
244                 break;
245             case 'Q':
246             case 'q':
247                 $s .= 'Q';
248                 break;
249
250             case 'M':
251                 $s .= 'Mon';
252                 break;
253
254             case 'm':
255                 $s .= 'MM';
256                 break;
257             case 'D':
258             case 'd':
259                 $s .= 'DD';
260                 break;
261
262             case 'H':
263                 $s.= 'HH24';
264                 break;
265
266             case 'h':
267                 $s .= 'HH';
268                 break;
269
270             case 'i':
271                 $s .= 'MI';
272                 break;
273
274             case 's':
275                 $s .= 'SS';
276                 break;
277
278             case 'a':
279             case 'A':
280                 $s .= 'AM';
281                 break;
282
283             default:
284             // handle escape characters...
285                 if ($ch == '\\') {
286                     $i++;
287                     $ch = substr($fmt,$i,1);
288                 }
289                 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
290                 else $s .= '"'.$ch.'"';
291
292             }
293         }
294         return $s. "')";
295     }
296
297
298
299     /*
300     * Load a Large Object from a file
301     * - the procedure stores the object id in the table and imports the object using
302     * postgres proprietary blob handling routines
303     *
304     * contributed by Mattia Rossi mattia@technologist.com
305     * modified for safe mode by juraj chlebec
306     */
307     function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
308     {
309         pg_exec ($this->_connectionID, "begin");
310
311         $fd = fopen($path,'r');
312         $contents = fread($fd,filesize($path));
313         fclose($fd);
314
315         $oid = pg_lo_create($this->_connectionID);
316         $handle = pg_lo_open($this->_connectionID, $oid, 'w');
317         pg_lo_write($handle, $contents);
318         pg_lo_close($handle);
319
320         // $oid = pg_lo_import ($path);
321         pg_exec($this->_connectionID, "commit");
322         $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype);
323         $rez = !empty($rs);
324         return $rez;
325     }
326
327     /*
328     * If an OID is detected, then we use pg_lo_* to open the oid file and read the
329     * real blob from the db using the oid supplied as a parameter. If you are storing
330     * blobs using bytea, we autodetect and process it so this function is not needed.
331     *
332     * contributed by Mattia Rossi mattia@technologist.com
333     *
334     * see http://www.postgresql.org/idocs/index.php?largeobjects.html
335     */
336     function BlobDecode( $blob)
337     {
338         if (strlen($blob) > 24) return $blob;
339
340         @pg_exec($this->_connectionID,"begin");
341         $fd = @pg_lo_open($this->_connectionID,$blob,"r");
342         if ($fd === false) {
343             @pg_exec($this->_connectionID,"commit");
344             return $blob;
345         }
346         $realblob = @pg_loreadall($fd);
347         @pg_loclose($fd);
348         @pg_exec($this->_connectionID,"commit");
349         return $realblob;
350     }
351
352     /*
353         See http://www.postgresql.org/idocs/index.php?datatype-binary.html
354
355         NOTE: SQL string literals (input strings) must be preceded with two backslashes
356         due to the fact that they must pass through two parsers in the PostgreSQL
357         backend.
358     */
359     function BlobEncode($blob)
360     {
361         if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob);
362
363         /*92=backslash, 0=null, 39=single-quote*/
364         $badch = array(chr(92),chr(0),chr(39)); # \  null  '
365         $fixch = array('\\\\134','\\\\000','\\\\047');
366         return adodb_str_replace($badch,$fixch,$blob);
367
368         // note that there is a pg_escape_bytea function only for php 4.2.0 or later
369     }
370
371     function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
372     {
373         // do not use bind params which uses qstr(), as blobencode() already quotes data
374         return $this->Execute("UPDATE $table SET $column='".$this->BlobEncode($val)."'::bytea WHERE $where");
375     }
376
377     function OffsetDate($dayFraction,$date=false)
378     {
379         if (!$date) $date = $this->sysDate;
380         return "($date+interval'$dayFraction days')";
381     }
382
383
384     // for schema support, pass in the $table param "$schema.$tabname".
385     // converts field names to lowercase, $upper is ignored
386     function &MetaColumns($table,$upper=true)
387     {
388     global $ADODB_FETCH_MODE;
389
390         $schema = false;
391         $this->_findschema($table,$schema);
392
393         $table = strtolower($table);
394
395         $save = $ADODB_FETCH_MODE;
396         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
397         if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
398
399         if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema));
400         else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table));
401         if (isset($savem)) $this->SetFetchMode($savem);
402         $ADODB_FETCH_MODE = $save;
403
404         if ($rs === false) return false;
405
406         if (!empty($this->metaKeySQL)) {
407             // If we want the primary keys, we have to issue a separate query
408             // Of course, a modified version of the metaColumnsSQL query using a
409             // LEFT JOIN would have been much more elegant, but postgres does
410             // not support OUTER JOINS. So here is the clumsy way.
411
412             $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
413
414             $rskey = $this->Execute(sprintf($this->metaKeySQL,($table)));
415             // fetch all result in once for performance.
416             $keys =& $rskey->GetArray();
417             if (isset($savem)) $this->SetFetchMode($savem);
418             $ADODB_FETCH_MODE = $save;
419
420             $rskey->Close();
421             unset($rskey);
422         }
423
424         $rsdefa = array();
425         if (!empty($this->metaDefaultsSQL)) {
426             $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
427             $sql = sprintf($this->metaDefaultsSQL, ($table));
428             $rsdef = $this->Execute($sql);
429             if (isset($savem)) $this->SetFetchMode($savem);
430             $ADODB_FETCH_MODE = $save;
431
432             if ($rsdef) {
433                 while (!$rsdef->EOF) {
434                     $num = $rsdef->fields['num'];
435                     $s = $rsdef->fields['def'];
436                     if (substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */
437                         $s = substr($s, 1);
438                         $s = substr($s, 0, strlen($s) - 1);
439                     }
440
441                     $rsdefa[$num] = $s;
442                     $rsdef->MoveNext();
443                 }
444             } else {
445                 ADOConnection::outp( "==> SQL => " . $sql);
446             }
447             unset($rsdef);
448         }
449
450         $retarr = array();
451         while (!$rs->EOF) {
452             $fld = new ADOFieldObject();
453             $fld->name = $rs->fields[0];
454             $fld->type = $rs->fields[1];
455             $fld->max_length = $rs->fields[2];
456             if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4;
457             if ($fld->max_length <= 0) $fld->max_length = -1;
458
459             // dannym
460             // 5 hasdefault; 6 num-of-column
461             $fld->has_default = ($rs->fields[5] == 't');
462             if ($fld->has_default) {
463                 $fld->default_value = $rsdefa[$rs->fields[6]];
464             }
465
466             //Freek
467             if ($rs->fields[4] == $this->true) {
468                 $fld->not_null = true;
469             }
470
471             // Freek
472             if (is_array($keys)) {
473                 foreach($keys as $key) {
474                     if ($fld->name == $key['column_name'] AND $key['primary_key'] == $this->true)
475                         $fld->primary_key = true;
476                     if ($fld->name == $key['column_name'] AND $key['unique_key'] == $this->true)
477                         $fld->unique = true; // What name is more compatible?
478                 }
479             }
480
481             if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
482             else $retarr[($upper) ? strtoupper($fld->name) : $fld->name] = $fld;
483
484             $rs->MoveNext();
485         }
486         $rs->Close();
487         return $retarr;
488
489     }
490
491       function &MetaIndexes ($table, $primary = FALSE)
492       {
493          global $ADODB_FETCH_MODE;
494
495                 $schema = false;
496                 $this->_findschema($table,$schema);
497
498                 if ($schema) { // requires pgsql 7.3+ - pg_namespace used.
499                     $sql = '
500 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
501 FROM pg_catalog.pg_class c
502 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
503 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
504     ,pg_namespace n
505 WHERE c2.relname=\'%s\' and c.relnamespace=c2.relnamespace and c.relnamespace=n.oid and n.nspname=\'%s\' AND i.indisprimary=false';
506                 } else {
507                     $sql = '
508 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
509 FROM pg_catalog.pg_class c
510 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
511 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
512 WHERE c2.relname=\'%s\'';
513                 }
514
515                 if ($primary == FALSE) {
516                         $sql .= ' AND i.indisprimary=false;';
517                 }
518
519                 $save = $ADODB_FETCH_MODE;
520                 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
521                 if ($this->fetchMode !== FALSE) {
522                         $savem = $this->SetFetchMode(FALSE);
523                 }
524
525                 $rs = $this->Execute(sprintf($sql,$table,$schema));
526
527                 if (isset($savem)) {
528                         $this->SetFetchMode($savem);
529                 }
530                 $ADODB_FETCH_MODE = $save;
531
532                 if (!is_object($rs)) {
533                         return FALSE;
534                 }
535
536                 $col_names = $this->MetaColumnNames($table);
537                 $indexes = array();
538
539                 while ($row = $rs->FetchRow()) {
540                         $columns = array();
541
542                         foreach (explode(' ', $row[2]) as $col) {
543                                 $columns[] = $col_names[$col - 1];
544                         }
545
546                         $indexes[$row[0]] = array(
547                                 'unique' => ($row[1] == 't'),
548                                 'columns' => $columns
549                         );
550                 }
551
552                 return $indexes;
553         }
554
555     // returns true or false
556     //
557     // examples:
558     //  $db->Connect("host=host1 user=user1 password=secret port=4341");
559     //  $db->Connect('host1','user1','secret');
560     function _connect($str,$user='',$pwd='',$db='',$ctype=0)
561     {
562
563         if (!function_exists('pg_pconnect')) return false;
564
565         $this->_errorMsg = false;
566
567         if ($user || $pwd || $db) {
568             $user = adodb_addslashes($user);
569             $pwd = adodb_addslashes($pwd);
570             if (strlen($db) == 0) $db = 'template1';
571             $db = adodb_addslashes($db);
572                if ($str)  {
573                  $host = explode(":", $str);
574                 if ($host[0]) $str = "host=".adodb_addslashes($host[0]);
575                 else $str = 'host=localhost';
576                 if (isset($host[1])) $str .= " port=$host[1]";
577             }
578                    if ($user) $str .= " user=".$user;
579                    if ($pwd)  $str .= " password=".$pwd;
580                 if ($db)   $str .= " dbname=".$db;
581         }
582
583         //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432";
584
585         if ($ctype === 1) { // persistent
586             $this->_connectionID = pg_pconnect($str);
587         } else {
588             if ($ctype === -1) { // nconnect, we trick pgsql ext by changing the connection str
589             static $ncnt;
590
591                 if (empty($ncnt)) $ncnt = 1;
592                 else $ncnt += 1;
593
594                 $str .= str_repeat(' ',$ncnt);
595             }
596             $this->_connectionID = pg_connect($str);
597         }
598         if ($this->_connectionID === false) return false;
599         $this->Execute("set datestyle='ISO'");
600         return true;
601     }
602
603     function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
604     {
605          return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName,-1);
606     }
607
608     // returns true or false
609     //
610     // examples:
611     //  $db->PConnect("host=host1 user=user1 password=secret port=4341");
612     //  $db->PConnect('host1','user1','secret');
613     function _pconnect($str,$user='',$pwd='',$db='')
614     {
615         return $this->_connect($str,$user,$pwd,$db,1);
616     }
617
618     // returns queryID or false
619     function _query($sql,$inputarr)
620     {
621
622         if ($inputarr) {
623         /*
624             It appears that PREPARE/EXECUTE is slower for many queries.
625
626             For query executed 1000 times:
627             "select id,firstname,lastname from adoxyz
628                 where firstname not like ? and lastname not like ? and id = ?"
629
630             with plan = 1.51861286163 secs
631             no plan =   1.26903700829 secs
632
633         */
634             $plan = 'P'.md5($sql);
635
636             $execp = '';
637             foreach($inputarr as $v) {
638                 if ($execp) $execp .= ',';
639                 if (is_string($v)) {
640                     if (strncmp($v,"'",1) !== 0) $execp .= $this->qstr($v);
641                 } else {
642                     $execp .= $v;
643                 }
644             }
645
646             if ($execp) $exsql = "EXECUTE $plan ($execp)";
647             else $exsql = "EXECUTE $plan";
648
649             $rez = @pg_exec($this->_connectionID,$exsql);
650             if (!$rez) {
651             # Perhaps plan does not exist? Prepare/compile plan.
652                 $params = '';
653                 foreach($inputarr as $v) {
654                     if ($params) $params .= ',';
655                     if (is_string($v)) {
656                         $params .= 'VARCHAR';
657                     } elseif (is_integer($v)) {
658                         $params .= 'INTEGER';
659                     } else {
660                         $params .= "REAL";
661                     }
662                 }
663                 $sqlarr = explode('?',$sql);
664                 //print_r($sqlarr);
665                 $sql = '';
666                 $i = 1;
667                 foreach($sqlarr as $v) {
668                     $sql .= $v.' $'.$i;
669                     $i++;
670                 }
671                 $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2);
672                 //adodb_pr($s);
673                 pg_exec($this->_connectionID,$s);
674                 echo $this->ErrorMsg();
675             }
676
677             $rez = pg_exec($this->_connectionID,$exsql);
678         } else {
679             $this->_errorMsg = false;
680             //adodb_backtrace();
681             $rez = pg_exec($this->_connectionID,$sql);
682         }
683         // check if no data returned, then no need to create real recordset
684         if ($rez && pg_numfields($rez) <= 0) {
685             if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') {
686                 pg_freeresult($this->_resultid);
687             }
688             $this->_resultid = $rez;
689             return true;
690         }
691
692         return $rez;
693     }
694
695     /*  Returns: the last error message from previous database operation        */
696     function ErrorMsg()
697     {
698         if ($this->_errorMsg !== false) return $this->_errorMsg;
699         if (ADODB_PHPVER >= 0x4300) {
700             if (!empty($this->_resultid)) {
701                 $this->_errorMsg = @pg_result_error($this->_resultid);
702                 if ($this->_errorMsg) return $this->_errorMsg;
703             }
704
705             if (!empty($this->_connectionID)) {
706                 $this->_errorMsg = @pg_last_error($this->_connectionID);
707             } else $this->_errorMsg = @pg_last_error();
708         } else {
709             if (empty($this->_connectionID)) $this->_errorMsg = @pg_errormessage();
710             else $this->_errorMsg = @pg_errormessage($this->_connectionID);
711         }
712         return $this->_errorMsg;
713     }
714
715     function ErrorNo()
716     {
717         $e = $this->ErrorMsg();
718         return strlen($e) ? $e : 0;
719     }
720
721     // returns true or false
722     function _close()
723     {
724         if ($this->transCnt) $this->RollbackTrans();
725         if ($this->_resultid) {
726             @pg_freeresult($this->_resultid);
727             $this->_resultid = false;
728         }
729         @pg_close($this->_connectionID);
730         $this->_connectionID = false;
731         return true;
732     }
733
734     /*
735     * Maximum size of C field
736     */
737     function CharMax()
738     {
739         return 1000000000;  // should be 1 Gb?
740     }
741
742     /*
743     * Maximum size of X field
744     */
745     function TextMax()
746     {
747         return 1000000000; // should be 1 Gb?
748     }
749
750 }
751
752 /*--------------------------------------------------------------------------------------
753      Class Name: Recordset
754 --------------------------------------------------------------------------------------*/
755
756 class ADORecordSet_postgres64 extends ADORecordSet{
757     var $_blobArr;
758     var $databaseType = "postgres64";
759     var $canSeek = true;
760     function ADORecordSet_postgres64($queryID,$mode=false)
761     {
762         if ($mode === false) {
763             global $ADODB_FETCH_MODE;
764             $mode = $ADODB_FETCH_MODE;
765         }
766         switch ($mode)
767         {
768         case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break;
769         case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break;
770         default:
771         case ADODB_FETCH_DEFAULT:
772         case ADODB_FETCH_BOTH:$this->fetchMode = PGSQL_BOTH; break;
773         }
774         $this->ADORecordSet($queryID);
775     }
776
777     function &GetRowAssoc($upper=true)
778     {
779         if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $this->fields;
780         $row =& ADORecordSet::GetRowAssoc($upper);
781         return $row;
782     }
783
784     function _initrs()
785     {
786     global $ADODB_COUNTRECS;
787         $qid = $this->_queryID;
788         $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($qid):-1;
789         $this->_numOfFields = @pg_numfields($qid);
790
791         // cache types for blob decode check
792         for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {
793             if (pg_fieldtype($qid,$i) == 'bytea') {
794                 $this->_blobArr[$i] = pg_fieldname($qid,$off);
795             }
796         }
797     }
798
799         /* Use associative array to get fields array */
800     function Fields($colname)
801     {
802         if ($this->fetchMode != PGSQL_NUM) return @$this->fields[$colname];
803
804         if (!$this->bind) {
805             $this->bind = array();
806             for ($i=0; $i < $this->_numOfFields; $i++) {
807                 $o = $this->FetchField($i);
808                 $this->bind[strtoupper($o->name)] = $i;
809             }
810         }
811          return $this->fields[$this->bind[strtoupper($colname)]];
812     }
813
814     function &FetchField($fieldOffset = 0)
815     {
816         $off=$fieldOffset; // offsets begin at 0
817
818         $o= new ADOFieldObject();
819         $o->name = @pg_fieldname($this->_queryID,$off);
820         $o->type = @pg_fieldtype($this->_queryID,$off);
821         $o->max_length = @pg_fieldsize($this->_queryID,$off);
822         return $o;
823     }
824
825     function _seek($row)
826     {
827         return @pg_fetch_row($this->_queryID,$row);
828     }
829
830     function _decode($blob)
831     {
832         eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";');
833         return $realblob;
834     }
835
836     function _fixblobs()
837     {
838         if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) {
839             foreach($this->_blobArr as $k => $v) {
840                 $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]);
841             }
842         }
843         if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) {
844             foreach($this->_blobArr as $k => $v) {
845                 $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]);
846             }
847         }
848     }
849
850     // 10% speedup to move MoveNext to child class
851     function MoveNext()
852     {
853         if (!$this->EOF) {
854             $this->_currentRow++;
855             if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) {
856                 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
857                 if (is_array($this->fields) && $this->fields) {
858                     if ($this->fields && isset($this->_blobArr)) $this->_fixblobs();
859                     return true;
860                 }
861             }
862             $this->fields = false;
863             $this->EOF = true;
864         }
865         return false;
866     }
867
868     function _fetch()
869     {
870
871         if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0)
872             return false;
873
874         $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
875
876     if ($this->fields && isset($this->_blobArr)) $this->_fixblobs();
877
878         return (is_array($this->fields));
879     }
880
881     function _close()
882     {
883         return @pg_freeresult($this->_queryID);
884     }
885
886     function MetaType($t,$len=-1,$fieldobj=false)
887     {
888         if (is_object($t)) {
889             $fieldobj = $t;
890             $t = $fieldobj->type;
891             $len = $fieldobj->max_length;
892         }
893         switch (strtoupper($t)) {
894                 case 'MONEY': // stupid, postgres expects money to be a string
895                 case 'INTERVAL':
896                 case 'CHAR':
897                 case 'CHARACTER':
898                 case 'VARCHAR':
899                 case 'NAME':
900                    case 'BPCHAR':
901                 case '_VARCHAR':
902                     if ($len <= $this->blobSize) return 'C';
903
904                 case 'TEXT':
905                     return 'X';
906
907                 case 'IMAGE': // user defined type
908                 case 'BLOB': // user defined type
909                 case 'BIT':     // This is a bit string, not a single bit, so don't return 'L'
910                 case 'VARBIT':
911                 case 'BYTEA':
912                     return 'B';
913
914                 case 'BOOL':
915                 case 'BOOLEAN':
916                     return 'L';
917
918                 case 'DATE':
919                     return 'D';
920
921                 case 'TIME':
922                 case 'DATETIME':
923                 case 'TIMESTAMP':
924                 case 'TIMESTAMPTZ':
925                     return 'T';
926
927                 case 'SMALLINT':
928                 case 'BIGINT':
929                 case 'INTEGER':
930                 case 'INT8':
931                 case 'INT4':
932                 case 'INT2':
933                     if (isset($fieldobj) &&
934                 empty($fieldobj->primary_key) && empty($fieldobj->unique)) return 'I';
935
936                 case 'OID':
937                 case 'SERIAL':
938                     return 'R';
939
940                  default:
941                      return 'N';
942             }
943     }
944
945 }
946 ?>