3 V5.19 23-Apr-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.
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
20 See http://www.varlena.com/varlena/GeneralBits/47.php
22 -- What indexes are on my table?
23 select * from pg_indexes where tablename = 'tablename';
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';
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';
45 // security - hide paths
46 if (!defined('ADODB_DIR')) die();
48 function adodb_addslashes($s)
51 if ($len == 0) return "''";
52 if (strncmp($s,"'",1) === 0 && substr($s,$len-1) == "'") return $s; // already quoted
54 return "'".addslashes($s)."'";
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')
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";
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";
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'";
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()'; /// random function
108 var $autoRollback = true; // apparently pgsql does not autorollback properly before php 4.3.4
109 // http://bugs.php.net/bug.php?id=25404
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.
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)
125 function __construct()
127 // changes the metaColumnsSQL, adds columns: attnum[6]
130 function ServerInfo()
132 if (isset($this->version)) return $this->version;
134 $arr['description'] = $this->GetOne("select version()");
135 $arr['version'] = ADOConnection::_findvers($arr['description']);
136 $this->version = $arr;
140 function IfNull( $field, $ifNull )
142 return " coalesce($field, $ifNull) ";
145 // get the last id - never tested
146 function pg_insert_id($tablename,$fieldname)
148 $result=pg_exec($this->_connectionID, "SELECT last_value FROM ${tablename}_${fieldname}_seq");
150 $arr = @pg_fetch_row($result,0);
151 pg_freeresult($result);
152 if (isset($arr[0])) return $arr[0];
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)
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);
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()
173 if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false;
174 return pg_cmdtuples($this->_resultid);
178 // returns true/false
179 function BeginTrans()
181 if ($this->transOff) return true;
182 $this->transCnt += 1;
183 return @pg_Exec($this->_connectionID, "begin ".$this->_transmode);
186 function RowLock($tables,$where,$col='1 as adodbignore')
188 if (!$this->transCnt) $this->BeginTrans();
189 return $this->GetOne("select $col from $tables where $where for update");
192 // returns true/false.
193 function CommitTrans($ok=true)
195 if ($this->transOff) return true;
196 if (!$ok) return $this->RollbackTrans();
198 $this->transCnt -= 1;
199 return @pg_Exec($this->_connectionID, "commit");
202 // returns true/false
203 function RollbackTrans()
205 if ($this->transOff) return true;
206 $this->transCnt -= 1;
207 return @pg_Exec($this->_connectionID, "rollback");
210 function MetaTables($ttype=false,$showSchema=false,$mask=false)
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')
217 select table_name,'V' from information_schema.views where table_schema not in ( 'pg_catalog','information_schema') ";
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')
226 select table_name,'V' from information_schema.views where table_name like $mask and table_schema not in ( 'pg_catalog','information_schema') ";
228 $this->metaTablesSQL = "
229 select tablename,'T' from pg_tables where tablename like $mask
231 select viewname,'V' from pg_views where viewname like $mask";
233 $ret = ADOConnection::MetaTables($ttype,$showSchema);
236 $this->metaTablesSQL = $save;
242 // if magic quotes disabled, use pg_escape_string()
243 function qstr($s,$magic_quotes=false)
245 if (is_bool($s)) return $s ? 'true' : 'false';
247 if (!$magic_quotes) {
248 if (ADODB_PHPVER >= 0x5200 && $this->_connectionID) {
249 return "'".pg_escape_string($this->_connectionID,$s)."'";
251 if (ADODB_PHPVER >= 0x4200) {
252 return "'".pg_escape_string($s)."'";
254 if ($this->replaceQuote[0] == '\\'){
255 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\\000"),$s);
257 return "'".str_replace("'",$this->replaceQuote,$s)."'";
260 // undo magic quotes for "
261 $s = str_replace('\\"','"',$s);
267 // Format date column in sql string given an input format that understands Y M D
268 function SQLDate($fmt, $col=false)
270 if (!$col) $col = $this->sysTimeStamp;
271 $s = 'TO_CHAR('.$col.",'";
274 for ($i=0; $i < $len; $i++) {
332 // handle escape characters...
335 $ch = substr($fmt,$i,1);
337 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
338 else $s .= '"'.$ch.'"';
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
352 * contributed by Mattia Rossi mattia@technologist.com
353 * modified for safe mode by juraj chlebec
355 function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
357 pg_exec ($this->_connectionID, "begin");
359 $fd = fopen($path,'r');
360 $contents = fread($fd,filesize($path));
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);
368 // $oid = pg_lo_import ($path);
369 pg_exec($this->_connectionID, "commit");
370 $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype);
376 * Deletes/Unlinks a Blob from the database, otherwise it
377 * will be left behind
379 * Returns TRUE on success or FALSE on failure.
381 * contributed by Todd Rogers todd#windfox.net
383 function BlobDelete( $blob )
385 pg_exec ($this->_connectionID, "begin");
386 $result = @pg_lo_unlink($blob);
387 pg_exec ($this->_connectionID, "commit");
392 Hueristic - not guaranteed to work.
394 function GuessOID($oid)
396 if (strlen($oid)>16) return false;
397 return is_numeric($oid);
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.
405 * contributed by Mattia Rossi mattia@technologist.com
407 * see http://www.postgresql.org/idocs/index.php?largeobjects.html
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.
412 function BlobDecode($blob,$maxsize=false,$hastrans=true)
414 if (!$this->GuessOID($blob)) return $blob;
416 if ($hastrans) @pg_exec($this->_connectionID,"begin");
417 $fd = @pg_lo_open($this->_connectionID,$blob,"r");
419 if ($hastrans) @pg_exec($this->_connectionID,"commit");
422 if (!$maxsize) $maxsize = $this->maxblobsize;
423 $realblob = @pg_loread($fd,$maxsize);
425 if ($hastrans) @pg_exec($this->_connectionID,"commit");
430 See http://www.postgresql.org/idocs/index.php?datatype-binary.html
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
436 function BlobEncode($blob)
438 if (ADODB_PHPVER >= 0x5200) return pg_escape_bytea($this->_connectionID, $blob);
439 if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob);
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);
446 // note that there is a pg_escape_bytea function only for php 4.2.0 or later
449 // assumes bytea for blob, and varchar for clob
450 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
452 if ($blobtype == 'CLOB') {
453 return $this->Execute("UPDATE $table SET $column=" . $this->qstr($val) . " WHERE $where");
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");
459 function OffsetDate($dayFraction,$date=false)
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;
469 return "($date+interval'".($dayFraction * 1440)." minutes')";
470 #return "($date+interval'$dayFraction days')";
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)
479 global $ADODB_FETCH_MODE;
483 $this->_findschema($table,$schema);
485 if ($normalize) $table = strtolower($table);
487 $save = $ADODB_FETCH_MODE;
488 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
489 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
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;
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.
505 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
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;
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;
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 */
531 $s = substr($s, 0, strlen($s) - 1);
538 ADOConnection::outp( "==> SQL => " . $sql);
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];
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;
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]];
565 $fld->not_null = $rs->fields[4] == 't';
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?
578 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
579 else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld;
591 function Param($name,$type='C')
596 // Reset param num if $name is false
599 return '$'.$this->_pnum;
602 function MetaIndexes ($table, $primary = FALSE, $owner = false)
604 global $ADODB_FETCH_MODE;
607 $this->_findschema($table,$schema);
609 if ($schema) { // requires pgsql 7.3+ - pg_namespace used.
611 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
612 FROM pg_catalog.pg_class c
613 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
614 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
616 WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))
617 and c.relnamespace=c2.relnamespace
618 and c.relnamespace=n.oid
619 and n.nspname=\'%s\'';
622 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
623 FROM pg_catalog.pg_class c
624 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
625 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
626 WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))';
629 if ($primary == FALSE) {
630 $sql .= ' AND i.indisprimary=false;';
633 $save = $ADODB_FETCH_MODE;
634 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
635 if ($this->fetchMode !== FALSE) {
636 $savem = $this->SetFetchMode(FALSE);
639 $rs = $this->Execute(sprintf($sql,$table,$table,$schema));
641 $this->SetFetchMode($savem);
643 $ADODB_FETCH_MODE = $save;
645 if (!is_object($rs)) {
650 $col_names = $this->MetaColumnNames($table,true,true);
651 //3rd param is use attnum,
652 // see http://sourceforge.net/tracker/index.php?func=detail&aid=1451245&group_id=42718&atid=433976
654 while ($row = $rs->FetchRow()) {
656 foreach (explode(' ', $row[2]) as $col) {
657 $columns[] = $col_names[$col];
660 $indexes[$row[0]] = array(
661 'unique' => ($row[1] == 't'),
662 'columns' => $columns
668 // returns true or false
671 // $db->Connect("host=host1 user=user1 password=secret port=4341");
672 // $db->Connect('host1','user1','secret');
673 function _connect($str,$user='',$pwd='',$db='',$ctype=0)
675 if (!function_exists('pg_connect')) return null;
677 $this->_errorMsg = false;
679 if ($user || $pwd || $db) {
680 $user = adodb_addslashes($user);
681 $pwd = adodb_addslashes($pwd);
682 if (strlen($db) == 0) $db = 'template1';
683 $db = adodb_addslashes($db);
685 $host = explode(":", $str);
686 if ($host[0]) $str = "host=".adodb_addslashes($host[0]);
688 if (isset($host[1])) $str .= " port=$host[1]";
689 else if (!empty($this->port)) $str .= " port=".$this->port;
691 if ($user) $str .= " user=".$user;
692 if ($pwd) $str .= " password=".$pwd;
693 if ($db) $str .= " dbname=".$db;
696 //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432";
698 if ($ctype === 1) { // persistent
699 $this->_connectionID = pg_pconnect($str);
701 if ($ctype === -1) { // nconnect, we trick pgsql ext by changing the connection str
704 if (empty($ncnt)) $ncnt = 1;
707 $str .= str_repeat(' ',$ncnt);
709 $this->_connectionID = pg_connect($str);
711 if ($this->_connectionID === false) return false;
712 $this->Execute("set datestyle='ISO'");
714 $info = $this->ServerInfo();
715 $this->pgVersion = (float) substr($info['version'],0,3);
716 if ($this->pgVersion >= 7.1) { // good till version 999
717 $this->_nestedSQL = true;
720 # PostgreSQL 9.0 changed the default output for bytea from 'escape' to 'hex'
721 # PHP does not handle 'hex' properly ('x74657374' is returned as 't657374')
722 # https://bugs.php.net/bug.php?id=59831 states this is in fact not a bug,
723 # so we manually set bytea_output
724 if (version_compare($info['version'], '9.0', '>=')) {
725 $this->Execute('set bytea_output=escape');
731 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
733 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName,-1);
736 // returns true or false
739 // $db->PConnect("host=host1 user=user1 password=secret port=4341");
740 // $db->PConnect('host1','user1','secret');
741 function _pconnect($str,$user='',$pwd='',$db='')
743 return $this->_connect($str,$user,$pwd,$db,1);
747 // returns queryID or false
748 function _query($sql,$inputarr=false)
751 $this->_errorMsg = false;
754 It appears that PREPARE/EXECUTE is slower for many queries.
756 For query executed 1000 times:
757 "select id,firstname,lastname from adoxyz
758 where firstname not like ? and lastname not like ? and id = ?"
760 with plan = 1.51861286163 secs
761 no plan = 1.26903700829 secs
766 $plan = 'P'.md5($sql);
769 foreach($inputarr as $v) {
770 if ($execp) $execp .= ',';
772 if (strncmp($v,"'",1) !== 0) $execp .= $this->qstr($v);
778 if ($execp) $exsql = "EXECUTE $plan ($execp)";
779 else $exsql = "EXECUTE $plan";
782 $rez = @pg_exec($this->_connectionID,$exsql);
784 # Perhaps plan does not exist? Prepare/compile plan.
786 foreach($inputarr as $v) {
787 if ($params) $params .= ',';
789 $params .= 'VARCHAR';
790 } else if (is_integer($v)) {
791 $params .= 'INTEGER';
796 $sqlarr = explode('?',$sql);
800 foreach($sqlarr as $v) {
804 $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2);
806 $rez = pg_exec($this->_connectionID,$s);
807 //echo $this->ErrorMsg();
810 $rez = pg_exec($this->_connectionID,$exsql);
813 $rez = pg_exec($this->_connectionID,$sql);
815 // check if no data returned, then no need to create real recordset
816 if ($rez && pg_numfields($rez) <= 0) {
817 if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') {
818 pg_freeresult($this->_resultid);
820 $this->_resultid = $rez;
827 function _errconnect()
829 if (defined('DB_ERROR_CONNECT_FAILED')) return DB_ERROR_CONNECT_FAILED;
830 else return 'Database connection failed';
833 /* Returns: the last error message from previous database operation */
836 if ($this->_errorMsg !== false) return $this->_errorMsg;
837 if (ADODB_PHPVER >= 0x4300) {
838 if (!empty($this->_resultid)) {
839 $this->_errorMsg = @pg_result_error($this->_resultid);
840 if ($this->_errorMsg) return $this->_errorMsg;
843 if (!empty($this->_connectionID)) {
844 $this->_errorMsg = @pg_last_error($this->_connectionID);
845 } else $this->_errorMsg = $this->_errconnect();
847 if (empty($this->_connectionID)) $this->_errconnect();
848 else $this->_errorMsg = @pg_errormessage($this->_connectionID);
850 return $this->_errorMsg;
855 $e = $this->ErrorMsg();
857 return ADOConnection::MetaError($e);
862 // returns true or false
865 if ($this->transCnt) $this->RollbackTrans();
866 if ($this->_resultid) {
867 @pg_freeresult($this->_resultid);
868 $this->_resultid = false;
870 @pg_close($this->_connectionID);
871 $this->_connectionID = false;
877 * Maximum size of C field
881 return 1000000000; // should be 1 Gb?
885 * Maximum size of X field
889 return 1000000000; // should be 1 Gb?
895 /*--------------------------------------------------------------------------------------
896 Class Name: Recordset
897 --------------------------------------------------------------------------------------*/
899 class ADORecordSet_postgres64 extends ADORecordSet{
901 var $databaseType = "postgres64";
904 function __construct($queryID, $mode=false)
906 if ($mode === false) {
907 global $ADODB_FETCH_MODE;
908 $mode = $ADODB_FETCH_MODE;
912 case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break;
913 case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break;
915 case ADODB_FETCH_DEFAULT:
916 case ADODB_FETCH_BOTH:
917 default: $this->fetchMode = PGSQL_BOTH; break;
919 $this->adodbFetchMode = $mode;
921 // Parent's constructor
922 $this->ADORecordSet($queryID);
925 function GetRowAssoc($upper=true)
927 if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $this->fields;
928 $row = ADORecordSet::GetRowAssoc($upper);
935 global $ADODB_COUNTRECS;
936 $qid = $this->_queryID;
937 $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($qid):-1;
938 $this->_numOfFields = @pg_numfields($qid);
940 // cache types for blob decode check
941 // apparently pg_fieldtype actually performs an sql query on the database to get the type.
942 if (empty($this->connection->noBlobs))
943 for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {
944 if (pg_fieldtype($qid,$i) == 'bytea') {
945 $this->_blobArr[$i] = pg_fieldname($qid,$i);
950 /* Use associative array to get fields array */
951 function Fields($colname)
953 if ($this->fetchMode != PGSQL_NUM) return @$this->fields[$colname];
956 $this->bind = array();
957 for ($i=0; $i < $this->_numOfFields; $i++) {
958 $o = $this->FetchField($i);
959 $this->bind[strtoupper($o->name)] = $i;
962 return $this->fields[$this->bind[strtoupper($colname)]];
965 function FetchField($off = 0)
967 // offsets begin at 0
969 $o= new ADOFieldObject();
970 $o->name = @pg_fieldname($this->_queryID,$off);
971 $o->type = @pg_fieldtype($this->_queryID,$off);
972 $o->max_length = @pg_fieldsize($this->_queryID,$off);
978 return @pg_fetch_row($this->_queryID,$row);
981 function _decode($blob)
983 if ($blob === NULL) return NULL;
984 // eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";');
985 return pg_unescape_bytea($blob);
990 if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) {
991 foreach($this->_blobArr as $k => $v) {
992 $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]);
995 if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) {
996 foreach($this->_blobArr as $k => $v) {
997 $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]);
1002 // 10% speedup to move MoveNext to child class
1006 $this->_currentRow++;
1007 if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) {
1008 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
1009 if (is_array($this->fields) && $this->fields) {
1010 if (isset($this->_blobArr)) $this->_fixblobs();
1014 $this->fields = false;
1023 if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0)
1026 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
1028 if ($this->fields && isset($this->_blobArr)) $this->_fixblobs();
1030 return (is_array($this->fields));
1035 return @pg_freeresult($this->_queryID);
1038 function MetaType($t,$len=-1,$fieldobj=false)
1040 if (is_object($t)) {
1042 $t = $fieldobj->type;
1043 $len = $fieldobj->max_length;
1045 switch (strtoupper($t)) {
1046 case 'MONEY': // stupid, postgres expects money to be a string
1056 if ($len <= $this->blobSize) return 'C';
1061 case 'IMAGE': // user defined type
1062 case 'BLOB': // user defined type
1063 case 'BIT': // This is a bit string, not a single bit, so don't return 'L'
1076 case 'TIMESTAMP WITHOUT TIME ZONE':
1089 if (isset($fieldobj) &&
1090 empty($fieldobj->primary_key) && (!$this->connection->uniqueIisR || empty($fieldobj->unique))) return 'I';