| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118 |
- <?php
- /*
- @version v5.20.17 31-Mar-2020
- @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
- @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
- Released under both BSD license and Lesser GPL library license.
- Whenever there is any discrepancy between the two licenses,
- the BSD license will take precedence.
- Set tabs to 8.
- Original version derived from Alberto Cerezal (acerezalp@dbnet.es) - DBNet Informatica & Comunicaciones.
- 08 Nov 2000 jlim - Minor corrections, removing mysql stuff
- 09 Nov 2000 jlim - added insertid support suggested by "Christopher Kings-Lynne" <chriskl@familyhealth.com.au>
- jlim - changed concat operator to || and data types to MetaType to match documented pgsql types
- see http://www.postgresql.org/devel-corner/docs/postgres/datatype.htm
- 22 Nov 2000 jlim - added changes to FetchField() and MetaTables() contributed by "raser" <raser@mail.zen.com.tw>
- 27 Nov 2000 jlim - added changes to _connect/_pconnect from ideas by "Lennie" <leen@wirehub.nl>
- 15 Dec 2000 jlim - added changes suggested by Additional code changes by "Eric G. Werk" egw@netguide.dk.
- 31 Jan 2002 jlim - finally installed postgresql. testing
- 01 Mar 2001 jlim - Freek Dijkstra changes, also support for text type
- See http://www.varlena.com/varlena/GeneralBits/47.php
- -- What indexes are on my table?
- select * from pg_indexes where tablename = 'tablename';
- -- What triggers are on my table?
- select c.relname as "Table", t.tgname as "Trigger Name",
- t.tgconstrname as "Constraint Name", t.tgenabled as "Enabled",
- t.tgisconstraint as "Is Constraint", cc.relname as "Referenced Table",
- p.proname as "Function Name"
- from pg_trigger t, pg_class c, pg_class cc, pg_proc p
- where t.tgfoid = p.oid and t.tgrelid = c.oid
- and t.tgconstrrelid = cc.oid
- and c.relname = 'tablename';
- -- What constraints are on my table?
- select r.relname as "Table", c.conname as "Constraint Name",
- contype as "Constraint Type", conkey as "Key Columns",
- confkey as "Foreign Columns", consrc as "Source"
- from pg_class r, pg_constraint c
- where r.oid = c.conrelid
- and relname = 'tablename';
- */
- // security - hide paths
- if (!defined('ADODB_DIR')) die();
- function adodb_addslashes($s)
- {
- $len = strlen($s);
- if ($len == 0) return "''";
- if (strncmp($s,"'",1) === 0 && substr($s,$len-1) == "'") return $s; // already quoted
- return "'".addslashes($s)."'";
- }
- class ADODB_postgres64 extends ADOConnection{
- var $databaseType = 'postgres64';
- var $dataProvider = 'postgres';
- var $hasInsertID = true;
- var $_resultid = false;
- var $concat_operator='||';
- var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1";
- var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%'
- and tablename not in ('sql_features', 'sql_implementation_info', 'sql_languages',
- 'sql_packages', 'sql_sizing', 'sql_sizing_profiles')
- union
- select viewname,'V' from pg_views where viewname not like 'pg\_%'";
- //"select tablename from pg_tables where tablename not like 'pg_%' order by 1";
- var $isoDates = true; // accepts dates in ISO format
- var $sysDate = "CURRENT_DATE";
- var $sysTimeStamp = "CURRENT_TIMESTAMP";
- var $blobEncodeType = 'C';
- var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum
- FROM pg_class c, pg_attribute a,pg_type t
- WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and a.attname not like '....%%'
- AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
- // used when schema defined
- var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum
- FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n
- WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s'))
- and c.relnamespace=n.oid and n.nspname='%s'
- and a.attname not like '....%%' AND a.attnum > 0
- AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum";
- // get primary key etc -- from Freek Dijkstra
- var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key
- FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a
- WHERE bc.oid = i.indrelid AND ic.oid = i.indexrelid
- AND (i.indkey[0] = a.attnum OR i.indkey[1] = a.attnum OR i.indkey[2] = a.attnum OR i.indkey[3] = a.attnum OR i.indkey[4] = a.attnum OR i.indkey[5] = a.attnum OR i.indkey[6] = a.attnum OR i.indkey[7] = a.attnum)
- AND a.attrelid = bc.oid AND bc.relname = '%s'";
- var $hasAffectedRows = true;
- var $hasLimit = false; // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10
- // below suggested by Freek Dijkstra
- var $true = 'TRUE'; // string that represents TRUE for a database
- var $false = 'FALSE'; // string that represents FALSE for a database
- var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database
- var $fmtTimeStamp = "'Y-m-d H:i:s'"; // used by DBTimeStamp as the default timestamp fmt.
- var $hasMoveFirst = true;
- var $hasGenID = true;
- var $_genIDSQL = "SELECT NEXTVAL('%s')";
- var $_genSeqSQL = "CREATE SEQUENCE %s START %s";
- var $_dropSeqSQL = "DROP SEQUENCE %s";
- 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";
- var $random = 'random()'; /// random function
- var $autoRollback = true; // apparently pgsql does not autorollback properly before php 4.3.4
- // http://bugs.php.net/bug.php?id=25404
- var $uniqueIisR = true;
- var $_bindInputArray = false; // requires postgresql 7.3+ and ability to modify database
- var $disableBlobs = false; // set to true to disable blob checking, resulting in 2-5% improvement in performance.
- var $_pnum = 0;
- // The last (fmtTimeStamp is not entirely correct:
- // PostgreSQL also has support for time zones,
- // and writes these time in this format: "2001-03-01 18:59:26+02".
- // There is no code for the "+02" time zone information, so I just left that out.
- // I'm not familiar enough with both ADODB as well as Postgres
- // to know what the concequences are. The other values are correct (wheren't in 0.94)
- // -- Freek Dijkstra
- function __construct()
- {
- // changes the metaColumnsSQL, adds columns: attnum[6]
- }
- function ServerInfo()
- {
- if (isset($this->version)) return $this->version;
- $arr['description'] = $this->GetOne("select version()");
- $arr['version'] = ADOConnection::_findvers($arr['description']);
- $this->version = $arr;
- return $arr;
- }
- function IfNull( $field, $ifNull )
- {
- return " coalesce($field, $ifNull) ";
- }
- // get the last id - never tested
- function pg_insert_id($tablename,$fieldname)
- {
- $result=pg_query($this->_connectionID, 'SELECT last_value FROM '. $tablename .'_'. $fieldname .'_seq');
- if ($result) {
- $arr = @pg_fetch_row($result,0);
- pg_free_result($result);
- if (isset($arr[0])) return $arr[0];
- }
- return false;
- }
- /**
- * Warning from http://www.php.net/manual/function.pg-getlastoid.php:
- * Using a OID as a unique identifier is not generally wise.
- * Unless you are very careful, you might end up with a tuple having
- * a different OID if a database must be reloaded.
- */
- function _insertid($table,$column)
- {
- if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false;
- $oid = pg_getlastoid($this->_resultid);
- // to really return the id, we need the table and column-name, else we can only return the oid != id
- return empty($table) || empty($column) ? $oid : $this->GetOne("SELECT $column FROM $table WHERE oid=".(int)$oid);
- }
- function _affectedrows()
- {
- if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false;
- return pg_affected_rows($this->_resultid);
- }
- /**
- * @return true/false
- */
- function BeginTrans()
- {
- if ($this->transOff) return true;
- $this->transCnt += 1;
- return pg_query($this->_connectionID, 'begin '.$this->_transmode);
- }
- function RowLock($tables,$where,$col='1 as adodbignore')
- {
- if (!$this->transCnt) $this->BeginTrans();
- return $this->GetOne("select $col from $tables where $where for update");
- }
- // returns true/false.
- function CommitTrans($ok=true)
- {
- if ($this->transOff) return true;
- if (!$ok) return $this->RollbackTrans();
- $this->transCnt -= 1;
- return pg_query($this->_connectionID, 'commit');
- }
- // returns true/false
- function RollbackTrans()
- {
- if ($this->transOff) return true;
- $this->transCnt -= 1;
- return pg_query($this->_connectionID, 'rollback');
- }
- function MetaTables($ttype=false,$showSchema=false,$mask=false)
- {
- $info = $this->ServerInfo();
- if ($info['version'] >= 7.3) {
- $this->metaTablesSQL = "
- select table_name,'T' from information_schema.tables where table_schema not in ( 'pg_catalog','information_schema')
- union
- select table_name,'V' from information_schema.views where table_schema not in ( 'pg_catalog','information_schema') ";
- }
- if ($mask) {
- $save = $this->metaTablesSQL;
- $mask = $this->qstr(strtolower($mask));
- if ($info['version']>=7.3)
- $this->metaTablesSQL = "
- select table_name,'T' from information_schema.tables where table_name like $mask and table_schema not in ( 'pg_catalog','information_schema')
- union
- select table_name,'V' from information_schema.views where table_name like $mask and table_schema not in ( 'pg_catalog','information_schema') ";
- else
- $this->metaTablesSQL = "
- select tablename,'T' from pg_tables where tablename like $mask
- union
- select viewname,'V' from pg_views where viewname like $mask";
- }
- $ret = ADOConnection::MetaTables($ttype,$showSchema);
- if ($mask) {
- $this->metaTablesSQL = $save;
- }
- return $ret;
- }
- // if magic quotes disabled, use pg_escape_string()
- function qstr($s,$magic_quotes=false)
- {
- if (is_bool($s)) return $s ? 'true' : 'false';
- if (!$magic_quotes) {
- if (ADODB_PHPVER >= 0x5200 && $this->_connectionID) {
- return "'".pg_escape_string($this->_connectionID,$s)."'";
- }
- if (ADODB_PHPVER >= 0x4200) {
- return "'".pg_escape_string($s)."'";
- }
- if ($this->replaceQuote[0] == '\\'){
- $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\\000"),$s);
- }
- return "'".str_replace("'",$this->replaceQuote,$s)."'";
- }
- // undo magic quotes for "
- $s = str_replace('\\"','"',$s);
- return "'$s'";
- }
- // Format date column in sql string given an input format that understands Y M D
- function SQLDate($fmt, $col=false)
- {
- if (!$col) $col = $this->sysTimeStamp;
- $s = 'TO_CHAR('.$col.",'";
- $len = strlen($fmt);
- for ($i=0; $i < $len; $i++) {
- $ch = $fmt[$i];
- switch($ch) {
- case 'Y':
- case 'y':
- $s .= 'YYYY';
- break;
- case 'Q':
- case 'q':
- $s .= 'Q';
- break;
- case 'M':
- $s .= 'Mon';
- break;
- case 'm':
- $s .= 'MM';
- break;
- case 'D':
- case 'd':
- $s .= 'DD';
- break;
- case 'H':
- $s.= 'HH24';
- break;
- case 'h':
- $s .= 'HH';
- break;
- case 'i':
- $s .= 'MI';
- break;
- case 's':
- $s .= 'SS';
- break;
- case 'a':
- case 'A':
- $s .= 'AM';
- break;
- case 'w':
- $s .= 'D';
- break;
- case 'l':
- $s .= 'DAY';
- break;
- case 'W':
- $s .= 'WW';
- break;
- default:
- // handle escape characters...
- if ($ch == '\\') {
- $i++;
- $ch = substr($fmt,$i,1);
- }
- if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
- else $s .= '"'.$ch.'"';
- }
- }
- return $s. "')";
- }
- /*
- * Load a Large Object from a file
- * - the procedure stores the object id in the table and imports the object using
- * postgres proprietary blob handling routines
- *
- * contributed by Mattia Rossi mattia@technologist.com
- * modified for safe mode by juraj chlebec
- */
- function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB')
- {
- pg_query($this->_connectionID, 'begin');
- $fd = fopen($path,'r');
- $contents = fread($fd,filesize($path));
- fclose($fd);
- $oid = pg_lo_create($this->_connectionID);
- $handle = pg_lo_open($this->_connectionID, $oid, 'w');
- pg_lo_write($handle, $contents);
- pg_lo_close($handle);
- // $oid = pg_lo_import ($path);
- pg_query($this->_connectionID, 'commit');
- $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype);
- $rez = !empty($rs);
- return $rez;
- }
- /*
- * Deletes/Unlinks a Blob from the database, otherwise it
- * will be left behind
- *
- * Returns TRUE on success or FALSE on failure.
- *
- * contributed by Todd Rogers todd#windfox.net
- */
- function BlobDelete( $blob )
- {
- pg_query($this->_connectionID, 'begin');
- $result = @pg_lo_unlink($blob);
- pg_query($this->_connectionID, 'commit');
- return( $result );
- }
- /*
- Hueristic - not guaranteed to work.
- */
- function GuessOID($oid)
- {
- if (strlen($oid)>16) return false;
- return is_numeric($oid);
- }
- /*
- * If an OID is detected, then we use pg_lo_* to open the oid file and read the
- * real blob from the db using the oid supplied as a parameter. If you are storing
- * blobs using bytea, we autodetect and process it so this function is not needed.
- *
- * contributed by Mattia Rossi mattia@technologist.com
- *
- * see http://www.postgresql.org/idocs/index.php?largeobjects.html
- *
- * Since adodb 4.54, this returns the blob, instead of sending it to stdout. Also
- * added maxsize parameter, which defaults to $db->maxblobsize if not defined.
- */
- function BlobDecode($blob,$maxsize=false,$hastrans=true)
- {
- if (!$this->GuessOID($blob)) return $blob;
- if ($hastrans) pg_query($this->_connectionID,'begin');
- $fd = @pg_lo_open($this->_connectionID,$blob,'r');
- if ($fd === false) {
- if ($hastrans) pg_query($this->_connectionID,'commit');
- return $blob;
- }
- if (!$maxsize) $maxsize = $this->maxblobsize;
- $realblob = @pg_lo_read($fd,$maxsize);
- @pg_loclose($fd);
- if ($hastrans) pg_query($this->_connectionID,'commit');
- return $realblob;
- }
- /*
- See http://www.postgresql.org/idocs/index.php?datatype-binary.html
- NOTE: SQL string literals (input strings) must be preceded with two backslashes
- due to the fact that they must pass through two parsers in the PostgreSQL
- backend.
- */
- function BlobEncode($blob)
- {
- if (ADODB_PHPVER >= 0x5200) return pg_escape_bytea($this->_connectionID, $blob);
- if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob);
- /*92=backslash, 0=null, 39=single-quote*/
- $badch = array(chr(92),chr(0),chr(39)); # \ null '
- $fixch = array('\\\\134','\\\\000','\\\\047');
- return adodb_str_replace($badch,$fixch,$blob);
- // note that there is a pg_escape_bytea function only for php 4.2.0 or later
- }
- // assumes bytea for blob, and varchar for clob
- function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
- {
- if ($blobtype == 'CLOB') {
- return $this->Execute("UPDATE $table SET $column=" . $this->qstr($val) . " WHERE $where");
- }
- // do not use bind params which uses qstr(), as blobencode() already quotes data
- return $this->Execute("UPDATE $table SET $column='".$this->BlobEncode($val)."'::bytea WHERE $where");
- }
- function OffsetDate($dayFraction,$date=false)
- {
- if (!$date) $date = $this->sysDate;
- else if (strncmp($date,"'",1) == 0) {
- $len = strlen($date);
- if (10 <= $len && $len <= 12) $date = 'date '.$date;
- else $date = 'timestamp '.$date;
- }
- return "($date+interval'".($dayFraction * 1440)." minutes')";
- #return "($date+interval'$dayFraction days')";
- }
- /**
- * Generate the SQL to retrieve MetaColumns data
- * @param string $table Table name
- * @param string $schema Schema name (can be blank)
- * @return string SQL statement to execute
- */
- protected function _generateMetaColumnsSQL($table, $schema)
- {
- if ($schema) {
- return sprintf($this->metaColumnsSQL1, $table, $table, $schema);
- }
- else {
- return sprintf($this->metaColumnsSQL, $table, $table, $schema);
- }
- }
- // for schema support, pass in the $table param "$schema.$tabname".
- // converts field names to lowercase, $upper is ignored
- // see http://phplens.com/lens/lensforum/msgs.php?id=14018 for more info
- function MetaColumns($table,$normalize=true)
- {
- global $ADODB_FETCH_MODE;
- $schema = false;
- $false = false;
- $this->_findschema($table,$schema);
- if ($normalize) $table = strtolower($table);
- $save = $ADODB_FETCH_MODE;
- $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
- if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
- $rs = $this->Execute($this->_generateMetaColumnsSQL($table, $schema));
- if (isset($savem)) $this->SetFetchMode($savem);
- $ADODB_FETCH_MODE = $save;
- if ($rs === false) {
- return $false;
- }
- if (!empty($this->metaKeySQL)) {
- // If we want the primary keys, we have to issue a separate query
- // Of course, a modified version of the metaColumnsSQL query using a
- // LEFT JOIN would have been much more elegant, but postgres does
- // not support OUTER JOINS. So here is the clumsy way.
- $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
- $rskey = $this->Execute(sprintf($this->metaKeySQL,($table)));
- // fetch all result in once for performance.
- $keys = $rskey->GetArray();
- if (isset($savem)) $this->SetFetchMode($savem);
- $ADODB_FETCH_MODE = $save;
- $rskey->Close();
- unset($rskey);
- }
- $rsdefa = array();
- if (!empty($this->metaDefaultsSQL)) {
- $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
- $sql = sprintf($this->metaDefaultsSQL, ($table));
- $rsdef = $this->Execute($sql);
- if (isset($savem)) $this->SetFetchMode($savem);
- $ADODB_FETCH_MODE = $save;
- if ($rsdef) {
- while (!$rsdef->EOF) {
- $num = $rsdef->fields['num'];
- $s = $rsdef->fields['def'];
- if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */
- $s = substr($s, 1);
- $s = substr($s, 0, strlen($s) - 1);
- }
- $rsdefa[$num] = $s;
- $rsdef->MoveNext();
- }
- } else {
- ADOConnection::outp( "==> SQL => " . $sql);
- }
- unset($rsdef);
- }
- $retarr = array();
- while (!$rs->EOF) {
- $fld = new ADOFieldObject();
- $fld->name = $rs->fields[0];
- $fld->type = $rs->fields[1];
- $fld->max_length = $rs->fields[2];
- $fld->attnum = $rs->fields[6];
- if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4;
- if ($fld->max_length <= 0) $fld->max_length = -1;
- if ($fld->type == 'numeric') {
- $fld->scale = $fld->max_length & 0xFFFF;
- $fld->max_length >>= 16;
- }
- // dannym
- // 5 hasdefault; 6 num-of-column
- $fld->has_default = ($rs->fields[5] == 't');
- if ($fld->has_default) {
- $fld->default_value = $rsdefa[$rs->fields[6]];
- }
- //Freek
- $fld->not_null = $rs->fields[4] == 't';
- // Freek
- if (is_array($keys)) {
- foreach($keys as $key) {
- if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't')
- $fld->primary_key = true;
- if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't')
- $fld->unique = true; // What name is more compatible?
- }
- }
- if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
- else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld;
- $rs->MoveNext();
- }
- $rs->Close();
- if (empty($retarr))
- return $false;
- else
- return $retarr;
- }
- function Param($name,$type='C')
- {
- if ($name) {
- $this->_pnum += 1;
- } else {
- // Reset param num if $name is false
- $this->_pnum = 1;
- }
- return '$'.$this->_pnum;
- }
- function MetaIndexes ($table, $primary = FALSE, $owner = false)
- {
- global $ADODB_FETCH_MODE;
- $schema = false;
- $this->_findschema($table,$schema);
- if ($schema) { // requires pgsql 7.3+ - pg_namespace used.
- $sql = '
- SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
- FROM pg_catalog.pg_class c
- JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
- JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
- ,pg_namespace n
- WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))
- and c.relnamespace=c2.relnamespace
- and c.relnamespace=n.oid
- and n.nspname=\'%s\'';
- } else {
- $sql = '
- SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns"
- FROM pg_catalog.pg_class c
- JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid
- JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid
- WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))';
- }
- if ($primary == FALSE) {
- $sql .= ' AND i.indisprimary=false;';
- }
- $save = $ADODB_FETCH_MODE;
- $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
- if ($this->fetchMode !== FALSE) {
- $savem = $this->SetFetchMode(FALSE);
- }
- $rs = $this->Execute(sprintf($sql,$table,$table,$schema));
- if (isset($savem)) {
- $this->SetFetchMode($savem);
- }
- $ADODB_FETCH_MODE = $save;
- if (!is_object($rs)) {
- $false = false;
- return $false;
- }
- $col_names = $this->MetaColumnNames($table,true,true);
- // 3rd param is use attnum,
- // see https://sourceforge.net/p/adodb/bugs/45/
- $indexes = array();
- while ($row = $rs->FetchRow()) {
- $columns = array();
- foreach (explode(' ', $row[2]) as $col) {
- $columns[] = $col_names[$col];
- }
- $indexes[$row[0]] = array(
- 'unique' => ($row[1] == 't'),
- 'columns' => $columns
- );
- }
- return $indexes;
- }
- // returns true or false
- //
- // examples:
- // $db->Connect("host=host1 user=user1 password=secret port=4341");
- // $db->Connect('host1','user1','secret');
- function _connect($str,$user='',$pwd='',$db='',$ctype=0)
- {
- if (!function_exists('pg_connect')) return null;
- $this->_errorMsg = false;
- if ($user || $pwd || $db) {
- $user = adodb_addslashes($user);
- $pwd = adodb_addslashes($pwd);
- if (strlen($db) == 0) $db = 'template1';
- $db = adodb_addslashes($db);
- if ($str) {
- $host = explode(":", $str);
- if ($host[0]) $str = "host=".adodb_addslashes($host[0]);
- else $str = '';
- if (isset($host[1])) $str .= " port=$host[1]";
- else if (!empty($this->port)) $str .= " port=".$this->port;
- }
- if ($user) $str .= " user=".$user;
- if ($pwd) $str .= " password=".$pwd;
- if ($db) $str .= " dbname=".$db;
- }
- //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432";
- if ($ctype === 1) { // persistent
- $this->_connectionID = pg_pconnect($str);
- } else {
- if ($ctype === -1) { // nconnect, we trick pgsql ext by changing the connection str
- static $ncnt;
- if (empty($ncnt)) $ncnt = 1;
- else $ncnt += 1;
- $str .= str_repeat(' ',$ncnt);
- }
- $this->_connectionID = pg_connect($str);
- }
- if ($this->_connectionID === false) return false;
- $this->Execute("set datestyle='ISO'");
- $info = $this->ServerInfo();
- $this->pgVersion = (float) substr($info['version'],0,3);
- if ($this->pgVersion >= 7.1) { // good till version 999
- $this->_nestedSQL = true;
- }
- # PostgreSQL 9.0 changed the default output for bytea from 'escape' to 'hex'
- # PHP does not handle 'hex' properly ('x74657374' is returned as 't657374')
- # https://bugs.php.net/bug.php?id=59831 states this is in fact not a bug,
- # so we manually set bytea_output
- if ( !empty($this->connection->noBlobs) && version_compare($info['version'], '9.0', '>=')) {
- $this->Execute('set bytea_output=escape');
- }
- return true;
- }
- function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName)
- {
- return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName,-1);
- }
- // returns true or false
- //
- // examples:
- // $db->PConnect("host=host1 user=user1 password=secret port=4341");
- // $db->PConnect('host1','user1','secret');
- function _pconnect($str,$user='',$pwd='',$db='')
- {
- return $this->_connect($str,$user,$pwd,$db,1);
- }
- // returns queryID or false
- function _query($sql,$inputarr=false)
- {
- $this->_pnum = 0;
- $this->_errorMsg = false;
- if ($inputarr) {
- /*
- It appears that PREPARE/EXECUTE is slower for many queries.
- For query executed 1000 times:
- "select id,firstname,lastname from adoxyz
- where firstname not like ? and lastname not like ? and id = ?"
- with plan = 1.51861286163 secs
- no plan = 1.26903700829 secs
- */
- $plan = 'P'.md5($sql);
- $execp = '';
- foreach($inputarr as $v) {
- if ($execp) $execp .= ',';
- if (is_string($v)) {
- if (strncmp($v,"'",1) !== 0) $execp .= $this->qstr($v);
- } else {
- $execp .= $v;
- }
- }
- if ($execp) $exsql = "EXECUTE $plan ($execp)";
- else $exsql = "EXECUTE $plan";
- $rez = @pg_execute($this->_connectionID,$exsql);
- if (!$rez) {
- # Perhaps plan does not exist? Prepare/compile plan.
- $params = '';
- foreach($inputarr as $v) {
- if ($params) $params .= ',';
- if (is_string($v)) {
- $params .= 'VARCHAR';
- } else if (is_integer($v)) {
- $params .= 'INTEGER';
- } else {
- $params .= "REAL";
- }
- }
- $sqlarr = explode('?',$sql);
- //print_r($sqlarr);
- $sql = '';
- $i = 1;
- foreach($sqlarr as $v) {
- $sql .= $v.' $'.$i;
- $i++;
- }
- $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2);
- //adodb_pr($s);
- $rez = pg_execute($this->_connectionID,$s);
- //echo $this->ErrorMsg();
- }
- if ($rez)
- $rez = pg_execute($this->_connectionID,$exsql);
- } else {
- //adodb_backtrace();
- $rez = pg_query($this->_connectionID,$sql);
- }
- // check if no data returned, then no need to create real recordset
- if ($rez && pg_num_fields($rez) <= 0) {
- if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') {
- pg_free_result($this->_resultid);
- }
- $this->_resultid = $rez;
- return true;
- }
- return $rez;
- }
- function _errconnect()
- {
- if (defined('DB_ERROR_CONNECT_FAILED')) return DB_ERROR_CONNECT_FAILED;
- else return 'Database connection failed';
- }
- /* Returns: the last error message from previous database operation */
- function ErrorMsg()
- {
- if ($this->_errorMsg !== false) return $this->_errorMsg;
- if (ADODB_PHPVER >= 0x4300) {
- if (!empty($this->_resultid)) {
- $this->_errorMsg = @pg_result_error($this->_resultid);
- if ($this->_errorMsg) return $this->_errorMsg;
- }
- if (!empty($this->_connectionID)) {
- $this->_errorMsg = @pg_last_error($this->_connectionID);
- } else $this->_errorMsg = $this->_errconnect();
- } else {
- if (empty($this->_connectionID)) $this->_errconnect();
- else $this->_errorMsg = @pg_errormessage($this->_connectionID);
- }
- return $this->_errorMsg;
- }
- function ErrorNo()
- {
- $e = $this->ErrorMsg();
- if (strlen($e)) {
- return ADOConnection::MetaError($e);
- }
- return 0;
- }
- // returns true or false
- function _close()
- {
- if ($this->transCnt) $this->RollbackTrans();
- if ($this->_resultid) {
- @pg_free_result($this->_resultid);
- $this->_resultid = false;
- }
- @pg_close($this->_connectionID);
- $this->_connectionID = false;
- return true;
- }
- /*
- * Maximum size of C field
- */
- function CharMax()
- {
- return 1000000000; // should be 1 Gb?
- }
- /*
- * Maximum size of X field
- */
- function TextMax()
- {
- return 1000000000; // should be 1 Gb?
- }
- }
- /*--------------------------------------------------------------------------------------
- Class Name: Recordset
- --------------------------------------------------------------------------------------*/
- class ADORecordSet_postgres64 extends ADORecordSet{
- var $_blobArr;
- var $databaseType = "postgres64";
- var $canSeek = true;
- function __construct($queryID, $mode=false)
- {
- if ($mode === false) {
- global $ADODB_FETCH_MODE;
- $mode = $ADODB_FETCH_MODE;
- }
- switch ($mode)
- {
- case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break;
- case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break;
- case ADODB_FETCH_DEFAULT:
- case ADODB_FETCH_BOTH:
- default: $this->fetchMode = PGSQL_BOTH; break;
- }
- $this->adodbFetchMode = $mode;
- // Parent's constructor
- parent::__construct($queryID);
- }
- function GetRowAssoc($upper = ADODB_ASSOC_CASE)
- {
- if ($this->fetchMode == PGSQL_ASSOC && $upper == ADODB_ASSOC_CASE_LOWER) {
- return $this->fields;
- }
- $row = ADORecordSet::GetRowAssoc($upper);
- return $row;
- }
- function _initrs()
- {
- global $ADODB_COUNTRECS;
- $qid = $this->_queryID;
- $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_num_rows($qid):-1;
- $this->_numOfFields = @pg_num_fields($qid);
- // cache types for blob decode check
- // apparently pg_field_type actually performs an sql query on the database to get the type.
- if (empty($this->connection->noBlobs))
- for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) {
- if (pg_field_type($qid,$i) == 'bytea') {
- $this->_blobArr[$i] = pg_field_name($qid,$i);
- }
- }
- }
- /* Use associative array to get fields array */
- function Fields($colname)
- {
- if ($this->fetchMode != PGSQL_NUM) return @$this->fields[$colname];
- if (!$this->bind) {
- $this->bind = array();
- for ($i=0; $i < $this->_numOfFields; $i++) {
- $o = $this->FetchField($i);
- $this->bind[strtoupper($o->name)] = $i;
- }
- }
- return $this->fields[$this->bind[strtoupper($colname)]];
- }
- function FetchField($off = 0)
- {
- // offsets begin at 0
- $o= new ADOFieldObject();
- $o->name = @pg_field_name($this->_queryID,$off);
- $o->type = @pg_field_type($this->_queryID,$off);
- $o->max_length = @pg_fieldsize($this->_queryID,$off);
- return $o;
- }
- function _seek($row)
- {
- return @pg_fetch_row($this->_queryID,$row);
- }
- function _decode($blob)
- {
- if ($blob === NULL) return NULL;
- // eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";');
- return pg_unescape_bytea($blob);
- }
- function _fixblobs()
- {
- if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) {
- foreach($this->_blobArr as $k => $v) {
- $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]);
- }
- }
- if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) {
- foreach($this->_blobArr as $k => $v) {
- $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]);
- }
- }
- }
- // 10% speedup to move MoveNext to child class
- function MoveNext()
- {
- if (!$this->EOF) {
- $this->_currentRow++;
- if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) {
- $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
- if (is_array($this->fields) && $this->fields) {
- if (isset($this->_blobArr)) $this->_fixblobs();
- return true;
- }
- }
- $this->fields = false;
- $this->EOF = true;
- }
- return false;
- }
- function _fetch()
- {
- if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0)
- return false;
- $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode);
- if ($this->fields && isset($this->_blobArr)) $this->_fixblobs();
- return (is_array($this->fields));
- }
- function _close()
- {
- return @pg_free_result($this->_queryID);
- }
- function MetaType($t,$len=-1,$fieldobj=false)
- {
- if (is_object($t)) {
- $fieldobj = $t;
- $t = $fieldobj->type;
- $len = $fieldobj->max_length;
- }
- switch (strtoupper($t)) {
- case 'MONEY': // stupid, postgres expects money to be a string
- case 'INTERVAL':
- case 'CHAR':
- case 'CHARACTER':
- case 'VARCHAR':
- case 'NAME':
- case 'BPCHAR':
- case '_VARCHAR':
- case 'INET':
- case 'MACADDR':
- if ($len <= $this->blobSize) return 'C';
- case 'TEXT':
- return 'X';
- case 'IMAGE': // user defined type
- case 'BLOB': // user defined type
- case 'BIT': // This is a bit string, not a single bit, so don't return 'L'
- case 'VARBIT':
- case 'BYTEA':
- return 'B';
- case 'BOOL':
- case 'BOOLEAN':
- return 'L';
- case 'DATE':
- return 'D';
- case 'TIMESTAMP WITHOUT TIME ZONE':
- case 'TIME':
- case 'DATETIME':
- case 'TIMESTAMP':
- case 'TIMESTAMPTZ':
- return 'T';
- case 'SMALLINT':
- case 'BIGINT':
- case 'INTEGER':
- case 'INT8':
- case 'INT4':
- case 'INT2':
- if (isset($fieldobj) &&
- empty($fieldobj->primary_key) && (!$this->connection->uniqueIisR || empty($fieldobj->unique))) return 'I';
- case 'OID':
- case 'SERIAL':
- return 'R';
- default:
- return 'N';
- }
- }
- }
|