C:/lib/adodb/drivers/adodb-postgres64.inc.php Quellcode

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




Korrekturen, Hinweise und Ergänzungen

Bitte scheuen Sie sich nicht und melden Sie, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Support Forum wählen. Es ist eine kostenlose Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden kommentarlos entfernt.