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