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