Documentation TYPO3 par Ameos |
00001 <?php 00002 /* 00003 V4.93 10 Oct 2006 (c) 2000-2006 John Lim (jlim#natsoft.com.my). All rights reserved. 00004 Released under both BSD license and Lesser GPL library license. 00005 Whenever there is any discrepancy between the two licenses, 00006 the BSD license will take precedence. 00007 Set tabs to 8. 00008 00009 Original version derived from Alberto Cerezal (acerezalp@dbnet.es) - DBNet Informatica & Comunicaciones. 00010 08 Nov 2000 jlim - Minor corrections, removing mysql stuff 00011 09 Nov 2000 jlim - added insertid support suggested by "Christopher Kings-Lynne" <chriskl@familyhealth.com.au> 00012 jlim - changed concat operator to || and data types to MetaType to match documented pgsql types 00013 see http://www.postgresql.org/devel-corner/docs/postgres/datatype.htm 00014 22 Nov 2000 jlim - added changes to FetchField() and MetaTables() contributed by "raser" <raser@mail.zen.com.tw> 00015 27 Nov 2000 jlim - added changes to _connect/_pconnect from ideas by "Lennie" <leen@wirehub.nl> 00016 15 Dec 2000 jlim - added changes suggested by Additional code changes by "Eric G. Werk" egw@netguide.dk. 00017 31 Jan 2002 jlim - finally installed postgresql. testing 00018 01 Mar 2001 jlim - Freek Dijkstra changes, also support for text type 00019 00020 See http://www.varlena.com/varlena/GeneralBits/47.php 00021 00022 -- What indexes are on my table? 00023 select * from pg_indexes where tablename = 'tablename'; 00024 00025 -- What triggers are on my table? 00026 select c.relname as "Table", t.tgname as "Trigger Name", 00027 t.tgconstrname as "Constraint Name", t.tgenabled as "Enabled", 00028 t.tgisconstraint as "Is Constraint", cc.relname as "Referenced Table", 00029 p.proname as "Function Name" 00030 from pg_trigger t, pg_class c, pg_class cc, pg_proc p 00031 where t.tgfoid = p.oid and t.tgrelid = c.oid 00032 and t.tgconstrrelid = cc.oid 00033 and c.relname = 'tablename'; 00034 00035 -- What constraints are on my table? 00036 select r.relname as "Table", c.conname as "Constraint Name", 00037 contype as "Constraint Type", conkey as "Key Columns", 00038 confkey as "Foreign Columns", consrc as "Source" 00039 from pg_class r, pg_constraint c 00040 where r.oid = c.conrelid 00041 and relname = 'tablename'; 00042 00043 */ 00044 00045 // security - hide paths 00046 if (!defined('ADODB_DIR')) die(); 00047 00048 function adodb_addslashes($s) 00049 { 00050 $len = strlen($s); 00051 if ($len == 0) return "''"; 00052 if (strncmp($s,"'",1) === 0 && substr($s,$len-1) == "'") return $s; // already quoted 00053 00054 return "'".addslashes($s)."'"; 00055 } 00056 00057 class ADODB_postgres64 extends ADOConnection{ 00058 var $databaseType = 'postgres64'; 00059 var $dataProvider = 'postgres'; 00060 var $hasInsertID = true; 00061 var $_resultid = false; 00062 var $concat_operator='||'; 00063 var $metaDatabasesSQL = "select datname from pg_database where datname not in ('template0','template1') order by 1"; 00064 var $metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' 00065 and tablename not in ('sql_features', 'sql_implementation_info', 'sql_languages', 00066 'sql_packages', 'sql_sizing', 'sql_sizing_profiles') 00067 union 00068 select viewname,'V' from pg_views where viewname not like 'pg\_%'"; 00069 //"select tablename from pg_tables where tablename not like 'pg_%' order by 1"; 00070 var $isoDates = true; // accepts dates in ISO format 00071 var $sysDate = "CURRENT_DATE"; 00072 var $sysTimeStamp = "CURRENT_TIMESTAMP"; 00073 var $blobEncodeType = 'C'; 00074 var $metaColumnsSQL = "SELECT a.attname,t.typname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,a.attnum 00075 FROM pg_class c, pg_attribute a,pg_type t 00076 WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) and a.attname not like '....%%' 00077 AND a.attnum > 0 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; 00078 00079 // used when schema defined 00080 var $metaColumnsSQL1 = "SELECT a.attname, t.typname, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum 00081 FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n 00082 WHERE relkind in ('r','v') AND (c.relname='%s' or c.relname = lower('%s')) 00083 and c.relnamespace=n.oid and n.nspname='%s' 00084 and a.attname not like '....%%' AND a.attnum > 0 00085 AND a.atttypid = t.oid AND a.attrelid = c.oid ORDER BY a.attnum"; 00086 00087 // get primary key etc -- from Freek Dijkstra 00088 var $metaKeySQL = "SELECT ic.relname AS index_name, a.attname AS column_name,i.indisunique AS unique_key, i.indisprimary AS primary_key 00089 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'"; 00090 00091 var $hasAffectedRows = true; 00092 var $hasLimit = false; // set to true for pgsql 7 only. support pgsql/mysql SELECT * FROM TABLE LIMIT 10 00093 // below suggested by Freek Dijkstra 00094 var $true = 'TRUE'; // string that represents TRUE for a database 00095 var $false = 'FALSE'; // string that represents FALSE for a database 00096 var $fmtDate = "'Y-m-d'"; // used by DBDate() as the default date format used by the database 00097 var $fmtTimeStamp = "'Y-m-d H:i:s'"; // used by DBTimeStamp as the default timestamp fmt. 00098 var $hasMoveFirst = true; 00099 var $hasGenID = true; 00100 var $_genIDSQL = "SELECT NEXTVAL('%s')"; 00101 var $_genSeqSQL = "CREATE SEQUENCE %s START %s"; 00102 var $_dropSeqSQL = "DROP SEQUENCE %s"; 00103 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"; 00104 var $random = 'random()'; 00105 var $autoRollback = true; // apparently pgsql does not autorollback properly before php 4.3.4 00106 // http://bugs.php.net/bug.php?id=25404 00107 00108 var $_bindInputArray = false; // requires postgresql 7.3+ and ability to modify database 00109 var $disableBlobs = false; // set to true to disable blob checking, resulting in 2-5% improvement in performance. 00110 00111 // The last (fmtTimeStamp is not entirely correct: 00112 // PostgreSQL also has support for time zones, 00113 // and writes these time in this format: "2001-03-01 18:59:26+02". 00114 // There is no code for the "+02" time zone information, so I just left that out. 00115 // I'm not familiar enough with both ADODB as well as Postgres 00116 // to know what the concequences are. The other values are correct (wheren't in 0.94) 00117 // -- Freek Dijkstra 00118 00119 function ADODB_postgres64() 00120 { 00121 // changes the metaColumnsSQL, adds columns: attnum[6] 00122 } 00123 00124 function ServerInfo() 00125 { 00126 if (isset($this->version)) return $this->version; 00127 00128 $arr['description'] = $this->GetOne("select version()"); 00129 $arr['version'] = ADOConnection::_findvers($arr['description']); 00130 $this->version = $arr; 00131 return $arr; 00132 } 00133 00134 function IfNull( $field, $ifNull ) 00135 { 00136 return " coalesce($field, $ifNull) "; 00137 } 00138 00139 // get the last id - never tested 00140 function pg_insert_id($tablename,$fieldname) 00141 { 00142 $result=pg_exec($this->_connectionID, "SELECT last_value FROM ${tablename}_${fieldname}_seq"); 00143 if ($result) { 00144 $arr = @pg_fetch_row($result,0); 00145 pg_freeresult($result); 00146 if (isset($arr[0])) return $arr[0]; 00147 } 00148 return false; 00149 } 00150 00151 /* Warning from http://www.php.net/manual/function.pg-getlastoid.php: 00152 Using a OID as a unique identifier is not generally wise. 00153 Unless you are very careful, you might end up with a tuple having 00154 a different OID if a database must be reloaded. */ 00155 function _insertid($table,$column) 00156 { 00157 if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false; 00158 $oid = pg_getlastoid($this->_resultid); 00159 // to really return the id, we need the table and column-name, else we can only return the oid != id 00160 return empty($table) || empty($column) ? $oid : $this->GetOne("SELECT $column FROM $table WHERE oid=".(int)$oid); 00161 } 00162 00163 // I get this error with PHP before 4.0.6 - jlim 00164 // Warning: This compilation does not support pg_cmdtuples() in adodb-postgres.inc.php on line 44 00165 function _affectedrows() 00166 { 00167 if (!is_resource($this->_resultid) || get_resource_type($this->_resultid) !== 'pgsql result') return false; 00168 return pg_cmdtuples($this->_resultid); 00169 } 00170 00171 00172 // returns true/false 00173 function BeginTrans() 00174 { 00175 if ($this->transOff) return true; 00176 $this->transCnt += 1; 00177 return @pg_Exec($this->_connectionID, "begin ".$this->_transmode); 00178 } 00179 00180 function RowLock($tables,$where,$flds='1 as ignore') 00181 { 00182 if (!$this->transCnt) $this->BeginTrans(); 00183 return $this->GetOne("select $flds from $tables where $where for update"); 00184 } 00185 00186 // returns true/false. 00187 function CommitTrans($ok=true) 00188 { 00189 if ($this->transOff) return true; 00190 if (!$ok) return $this->RollbackTrans(); 00191 00192 $this->transCnt -= 1; 00193 return @pg_Exec($this->_connectionID, "commit"); 00194 } 00195 00196 // returns true/false 00197 function RollbackTrans() 00198 { 00199 if ($this->transOff) return true; 00200 $this->transCnt -= 1; 00201 return @pg_Exec($this->_connectionID, "rollback"); 00202 } 00203 00204 function &MetaTables($ttype=false,$showSchema=false,$mask=false) 00205 { 00206 $info = $this->ServerInfo(); 00207 if ($info['version'] >= 7.3) { 00208 $this->metaTablesSQL = "select tablename,'T' from pg_tables where tablename not like 'pg\_%' 00209 and schemaname not in ( 'pg_catalog','information_schema') 00210 union 00211 select viewname,'V' from pg_views where viewname not like 'pg\_%' and schemaname not in ( 'pg_catalog','information_schema') "; 00212 } 00213 if ($mask) { 00214 $save = $this->metaTablesSQL; 00215 $mask = $this->qstr(strtolower($mask)); 00216 if ($info['version']>=7.3) 00217 $this->metaTablesSQL = " 00218 select tablename,'T' from pg_tables where tablename like $mask and schemaname not in ( 'pg_catalog','information_schema') 00219 union 00220 select viewname,'V' from pg_views where viewname like $mask and schemaname not in ( 'pg_catalog','information_schema') "; 00221 else 00222 $this->metaTablesSQL = " 00223 select tablename,'T' from pg_tables where tablename like $mask 00224 union 00225 select viewname,'V' from pg_views where viewname like $mask"; 00226 } 00227 $ret =& ADOConnection::MetaTables($ttype,$showSchema); 00228 00229 if ($mask) { 00230 $this->metaTablesSQL = $save; 00231 } 00232 return $ret; 00233 } 00234 00235 00236 // if magic quotes disabled, use pg_escape_string() 00237 function qstr($s,$magic_quotes=false) 00238 { 00239 if (!$magic_quotes) { 00240 if (ADODB_PHPVER >= 0x5200) { 00241 return "'".pg_escape_string($this->_connectionID,$s)."'"; 00242 } 00243 if (ADODB_PHPVER >= 0x4200) { 00244 return "'".pg_escape_string($s)."'"; 00245 } 00246 if ($this->replaceQuote[0] == '\\'){ 00247 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\\000"),$s); 00248 } 00249 return "'".str_replace("'",$this->replaceQuote,$s)."'"; 00250 } 00251 00252 // undo magic quotes for " 00253 $s = str_replace('\\"','"',$s); 00254 return "'$s'"; 00255 } 00256 00257 00258 00259 // Format date column in sql string given an input format that understands Y M D 00260 function SQLDate($fmt, $col=false) 00261 { 00262 if (!$col) $col = $this->sysTimeStamp; 00263 $s = 'TO_CHAR('.$col.",'"; 00264 00265 $len = strlen($fmt); 00266 for ($i=0; $i < $len; $i++) { 00267 $ch = $fmt[$i]; 00268 switch($ch) { 00269 case 'Y': 00270 case 'y': 00271 $s .= 'YYYY'; 00272 break; 00273 case 'Q': 00274 case 'q': 00275 $s .= 'Q'; 00276 break; 00277 00278 case 'M': 00279 $s .= 'Mon'; 00280 break; 00281 00282 case 'm': 00283 $s .= 'MM'; 00284 break; 00285 case 'D': 00286 case 'd': 00287 $s .= 'DD'; 00288 break; 00289 00290 case 'H': 00291 $s.= 'HH24'; 00292 break; 00293 00294 case 'h': 00295 $s .= 'HH'; 00296 break; 00297 00298 case 'i': 00299 $s .= 'MI'; 00300 break; 00301 00302 case 's': 00303 $s .= 'SS'; 00304 break; 00305 00306 case 'a': 00307 case 'A': 00308 $s .= 'AM'; 00309 break; 00310 00311 case 'w': 00312 $s .= 'D'; 00313 break; 00314 00315 case 'l': 00316 $s .= 'DAY'; 00317 break; 00318 00319 case 'W': 00320 $s .= 'WW'; 00321 break; 00322 00323 default: 00324 // handle escape characters... 00325 if ($ch == '\\') { 00326 $i++; 00327 $ch = substr($fmt,$i,1); 00328 } 00329 if (strpos('-/.:;, ',$ch) !== false) $s .= $ch; 00330 else $s .= '"'.$ch.'"'; 00331 00332 } 00333 } 00334 return $s. "')"; 00335 } 00336 00337 00338 00339 /* 00340 * Load a Large Object from a file 00341 * - the procedure stores the object id in the table and imports the object using 00342 * postgres proprietary blob handling routines 00343 * 00344 * contributed by Mattia Rossi mattia@technologist.com 00345 * modified for safe mode by juraj chlebec 00346 */ 00347 function UpdateBlobFile($table,$column,$path,$where,$blobtype='BLOB') 00348 { 00349 pg_exec ($this->_connectionID, "begin"); 00350 00351 $fd = fopen($path,'r'); 00352 $contents = fread($fd,filesize($path)); 00353 fclose($fd); 00354 00355 $oid = pg_lo_create($this->_connectionID); 00356 $handle = pg_lo_open($this->_connectionID, $oid, 'w'); 00357 pg_lo_write($handle, $contents); 00358 pg_lo_close($handle); 00359 00360 // $oid = pg_lo_import ($path); 00361 pg_exec($this->_connectionID, "commit"); 00362 $rs = ADOConnection::UpdateBlob($table,$column,$oid,$where,$blobtype); 00363 $rez = !empty($rs); 00364 return $rez; 00365 } 00366 00367 /* 00368 * Deletes/Unlinks a Blob from the database, otherwise it 00369 * will be left behind 00370 * 00371 * Returns TRUE on success or FALSE on failure. 00372 * 00373 * contributed by Todd Rogers todd#windfox.net 00374 */ 00375 function BlobDelete( $blob ) 00376 { 00377 pg_exec ($this->_connectionID, "begin"); 00378 $result = @pg_lo_unlink($blob); 00379 pg_exec ($this->_connectionID, "commit"); 00380 return( $result ); 00381 } 00382 00383 /* 00384 Hueristic - not guaranteed to work. 00385 */ 00386 function GuessOID($oid) 00387 { 00388 if (strlen($oid)>16) return false; 00389 return is_numeric($oid); 00390 } 00391 00392 /* 00393 * If an OID is detected, then we use pg_lo_* to open the oid file and read the 00394 * real blob from the db using the oid supplied as a parameter. If you are storing 00395 * blobs using bytea, we autodetect and process it so this function is not needed. 00396 * 00397 * contributed by Mattia Rossi mattia@technologist.com 00398 * 00399 * see http://www.postgresql.org/idocs/index.php?largeobjects.html 00400 * 00401 * Since adodb 4.54, this returns the blob, instead of sending it to stdout. Also 00402 * added maxsize parameter, which defaults to $db->maxblobsize if not defined. 00403 */ 00404 function BlobDecode($blob,$maxsize=false,$hastrans=true) 00405 { 00406 if (!$this->GuessOID($blob)) return $blob; 00407 00408 if ($hastrans) @pg_exec($this->_connectionID,"begin"); 00409 $fd = @pg_lo_open($this->_connectionID,$blob,"r"); 00410 if ($fd === false) { 00411 if ($hastrans) @pg_exec($this->_connectionID,"commit"); 00412 return $blob; 00413 } 00414 if (!$maxsize) $maxsize = $this->maxblobsize; 00415 $realblob = @pg_loread($fd,$maxsize); 00416 @pg_loclose($fd); 00417 if ($hastrans) @pg_exec($this->_connectionID,"commit"); 00418 return $realblob; 00419 } 00420 00421 /* 00422 See http://www.postgresql.org/idocs/index.php?datatype-binary.html 00423 00424 NOTE: SQL string literals (input strings) must be preceded with two backslashes 00425 due to the fact that they must pass through two parsers in the PostgreSQL 00426 backend. 00427 */ 00428 function BlobEncode($blob) 00429 { 00430 if (ADODB_PHPVER >= 0x5200) return pg_escape_bytea($this->_connectionID, $blob); 00431 if (ADODB_PHPVER >= 0x4200) return pg_escape_bytea($blob); 00432 00433 /*92=backslash, 0=null, 39=single-quote*/ 00434 $badch = array(chr(92),chr(0),chr(39)); # \ null ' 00435 $fixch = array('\\\\134','\\\\000','\\\\047'); 00436 return adodb_str_replace($badch,$fixch,$blob); 00437 00438 // note that there is a pg_escape_bytea function only for php 4.2.0 or later 00439 } 00440 00441 // assumes bytea for blob, and varchar for clob 00442 function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB') 00443 { 00444 00445 if ($blobtype == 'CLOB') { 00446 return $this->Execute("UPDATE $table SET $column=" . $this->qstr($val) . " WHERE $where"); 00447 } 00448 // do not use bind params which uses qstr(), as blobencode() already quotes data 00449 return $this->Execute("UPDATE $table SET $column='".$this->BlobEncode($val)."'::bytea WHERE $where"); 00450 } 00451 00452 function OffsetDate($dayFraction,$date=false) 00453 { 00454 if (!$date) $date = $this->sysDate; 00455 else if (strncmp($date,"'",1) == 0) { 00456 $len = strlen($date); 00457 if (10 <= $len && $len <= 12) $date = 'date '.$date; 00458 else $date = 'timestamp '.$date; 00459 } 00460 return "($date+interval'$dayFraction days')"; 00461 } 00462 00463 00464 // for schema support, pass in the $table param "$schema.$tabname". 00465 // converts field names to lowercase, $upper is ignored 00466 // see http://phplens.com/lens/lensforum/msgs.php?id=14018 for more info 00467 function &MetaColumns($table,$normalize=true) 00468 { 00469 global $ADODB_FETCH_MODE; 00470 00471 $schema = false; 00472 $false = false; 00473 $this->_findschema($table,$schema); 00474 00475 if ($normalize) $table = strtolower($table); 00476 00477 $save = $ADODB_FETCH_MODE; 00478 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 00479 if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false); 00480 00481 if ($schema) $rs =& $this->Execute(sprintf($this->metaColumnsSQL1,$table,$table,$schema)); 00482 else $rs =& $this->Execute(sprintf($this->metaColumnsSQL,$table,$table)); 00483 if (isset($savem)) $this->SetFetchMode($savem); 00484 $ADODB_FETCH_MODE = $save; 00485 00486 if ($rs === false) { 00487 return $false; 00488 } 00489 if (!empty($this->metaKeySQL)) { 00490 // If we want the primary keys, we have to issue a separate query 00491 // Of course, a modified version of the metaColumnsSQL query using a 00492 // LEFT JOIN would have been much more elegant, but postgres does 00493 // not support OUTER JOINS. So here is the clumsy way. 00494 00495 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; 00496 00497 $rskey = $this->Execute(sprintf($this->metaKeySQL,($table))); 00498 // fetch all result in once for performance. 00499 $keys =& $rskey->GetArray(); 00500 if (isset($savem)) $this->SetFetchMode($savem); 00501 $ADODB_FETCH_MODE = $save; 00502 00503 $rskey->Close(); 00504 unset($rskey); 00505 } 00506 00507 $rsdefa = array(); 00508 if (!empty($this->metaDefaultsSQL)) { 00509 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; 00510 $sql = sprintf($this->metaDefaultsSQL, ($table)); 00511 $rsdef = $this->Execute($sql); 00512 if (isset($savem)) $this->SetFetchMode($savem); 00513 $ADODB_FETCH_MODE = $save; 00514 00515 if ($rsdef) { 00516 while (!$rsdef->EOF) { 00517 $num = $rsdef->fields['num']; 00518 $s = $rsdef->fields['def']; 00519 if (strpos($s,'::')===false && substr($s, 0, 1) == "'") { /* quoted strings hack... for now... fixme */ 00520 $s = substr($s, 1); 00521 $s = substr($s, 0, strlen($s) - 1); 00522 } 00523 00524 $rsdefa[$num] = $s; 00525 $rsdef->MoveNext(); 00526 } 00527 } else { 00528 ADOConnection::outp( "==> SQL => " . $sql); 00529 } 00530 unset($rsdef); 00531 } 00532 00533 $retarr = array(); 00534 while (!$rs->EOF) { 00535 $fld = new ADOFieldObject(); 00536 $fld->name = $rs->fields[0]; 00537 $fld->type = $rs->fields[1]; 00538 $fld->max_length = $rs->fields[2]; 00539 $fld->attnum = $rs->fields[6]; 00540 00541 if ($fld->max_length <= 0) $fld->max_length = $rs->fields[3]-4; 00542 if ($fld->max_length <= 0) $fld->max_length = -1; 00543 if ($fld->type == 'numeric') { 00544 $fld->scale = $fld->max_length & 0xFFFF; 00545 $fld->max_length >>= 16; 00546 } 00547 // dannym 00548 // 5 hasdefault; 6 num-of-column 00549 $fld->has_default = ($rs->fields[5] == 't'); 00550 if ($fld->has_default) { 00551 $fld->default_value = $rsdefa[$rs->fields[6]]; 00552 } 00553 00554 //Freek 00555 $fld->not_null = $rs->fields[4] == 't'; 00556 00557 00558 // Freek 00559 if (is_array($keys)) { 00560 foreach($keys as $key) { 00561 if ($fld->name == $key['column_name'] AND $key['primary_key'] == 't') 00562 $fld->primary_key = true; 00563 if ($fld->name == $key['column_name'] AND $key['unique_key'] == 't') 00564 $fld->unique = true; // What name is more compatible? 00565 } 00566 } 00567 00568 if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld; 00569 else $retarr[($normalize) ? strtoupper($fld->name) : $fld->name] = $fld; 00570 00571 $rs->MoveNext(); 00572 } 00573 $rs->Close(); 00574 if (empty($retarr)) 00575 return $false; 00576 else 00577 return $retarr; 00578 00579 } 00580 00581 function &MetaIndexes ($table, $primary = FALSE) 00582 { 00583 global $ADODB_FETCH_MODE; 00584 00585 $schema = false; 00586 $this->_findschema($table,$schema); 00587 00588 if ($schema) { // requires pgsql 7.3+ - pg_namespace used. 00589 $sql = ' 00590 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" 00591 FROM pg_catalog.pg_class c 00592 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid 00593 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid 00594 ,pg_namespace n 00595 WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\')) and c.relnamespace=c2.relnamespace and c.relnamespace=n.oid and n.nspname=\'%s\''; 00596 } else { 00597 $sql = ' 00598 SELECT c.relname as "Name", i.indisunique as "Unique", i.indkey as "Columns" 00599 FROM pg_catalog.pg_class c 00600 JOIN pg_catalog.pg_index i ON i.indexrelid=c.oid 00601 JOIN pg_catalog.pg_class c2 ON c2.oid=i.indrelid 00602 WHERE (c2.relname=\'%s\' or c2.relname=lower(\'%s\'))'; 00603 } 00604 00605 if ($primary == FALSE) { 00606 $sql .= ' AND i.indisprimary=false;'; 00607 } 00608 00609 $save = $ADODB_FETCH_MODE; 00610 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 00611 if ($this->fetchMode !== FALSE) { 00612 $savem = $this->SetFetchMode(FALSE); 00613 } 00614 00615 $rs = $this->Execute(sprintf($sql,$table,$table,$schema)); 00616 if (isset($savem)) { 00617 $this->SetFetchMode($savem); 00618 } 00619 $ADODB_FETCH_MODE = $save; 00620 00621 if (!is_object($rs)) { 00622 $false = false; 00623 return $false; 00624 } 00625 00626 $col_names = $this->MetaColumnNames($table,true,true); 00627 //3rd param is use attnum, 00628 // see http://sourceforge.net/tracker/index.php?func=detail&aid=1451245&group_id=42718&atid=433976 00629 $indexes = array(); 00630 while ($row = $rs->FetchRow()) { 00631 $columns = array(); 00632 foreach (explode(' ', $row[2]) as $col) { 00633 $columns[] = $col_names[$col]; 00634 } 00635 00636 $indexes[$row[0]] = array( 00637 'unique' => ($row[1] == 't'), 00638 'columns' => $columns 00639 ); 00640 } 00641 return $indexes; 00642 } 00643 00644 // returns true or false 00645 // 00646 // examples: 00647 // $db->Connect("host=host1 user=user1 password=secret port=4341"); 00648 // $db->Connect('host1','user1','secret'); 00649 function _connect($str,$user='',$pwd='',$db='',$ctype=0) 00650 { 00651 00652 if (!function_exists('pg_connect')) return null; 00653 00654 $this->_errorMsg = false; 00655 00656 if ($user || $pwd || $db) { 00657 $user = adodb_addslashes($user); 00658 $pwd = adodb_addslashes($pwd); 00659 if (strlen($db) == 0) $db = 'template1'; 00660 $db = adodb_addslashes($db); 00661 if ($str) { 00662 $host = split(":", $str); 00663 if ($host[0]) $str = "host=".adodb_addslashes($host[0]); 00664 else $str = ''; 00665 if (isset($host[1])) $str .= " port=$host[1]"; 00666 else if (!empty($this->port)) $str .= " port=".$this->port; 00667 } 00668 if ($user) $str .= " user=".$user; 00669 if ($pwd) $str .= " password=".$pwd; 00670 if ($db) $str .= " dbname=".$db; 00671 } 00672 00673 //if ($user) $linea = "user=$user host=$linea password=$pwd dbname=$db port=5432"; 00674 00675 if ($ctype === 1) { // persistent 00676 $this->_connectionID = pg_pconnect($str); 00677 } else { 00678 if ($ctype === -1) { // nconnect, we trick pgsql ext by changing the connection str 00679 static $ncnt; 00680 00681 if (empty($ncnt)) $ncnt = 1; 00682 else $ncnt += 1; 00683 00684 $str .= str_repeat(' ',$ncnt); 00685 } 00686 $this->_connectionID = pg_connect($str); 00687 } 00688 if ($this->_connectionID === false) return false; 00689 $this->Execute("set datestyle='ISO'"); 00690 00691 $info = $this->ServerInfo(); 00692 $this->pgVersion = (float) substr($info['version'],0,3); 00693 if ($this->pgVersion >= 7.1) { // good till version 999 00694 $this->_nestedSQL = true; 00695 } 00696 return true; 00697 } 00698 00699 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabaseName) 00700 { 00701 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabaseName,-1); 00702 } 00703 00704 // returns true or false 00705 // 00706 // examples: 00707 // $db->PConnect("host=host1 user=user1 password=secret port=4341"); 00708 // $db->PConnect('host1','user1','secret'); 00709 function _pconnect($str,$user='',$pwd='',$db='') 00710 { 00711 return $this->_connect($str,$user,$pwd,$db,1); 00712 } 00713 00714 00715 // returns queryID or false 00716 function _query($sql,$inputarr) 00717 { 00718 $this->_errorMsg = false; 00719 if ($inputarr) { 00720 /* 00721 It appears that PREPARE/EXECUTE is slower for many queries. 00722 00723 For query executed 1000 times: 00724 "select id,firstname,lastname from adoxyz 00725 where firstname not like ? and lastname not like ? and id = ?" 00726 00727 with plan = 1.51861286163 secs 00728 no plan = 1.26903700829 secs 00729 00730 00731 00732 */ 00733 $plan = 'P'.md5($sql); 00734 00735 $execp = ''; 00736 foreach($inputarr as $v) { 00737 if ($execp) $execp .= ','; 00738 if (is_string($v)) { 00739 if (strncmp($v,"'",1) !== 0) $execp .= $this->qstr($v); 00740 } else { 00741 $execp .= $v; 00742 } 00743 } 00744 00745 if ($execp) $exsql = "EXECUTE $plan ($execp)"; 00746 else $exsql = "EXECUTE $plan"; 00747 00748 00749 $rez = @pg_exec($this->_connectionID,$exsql); 00750 if (!$rez) { 00751 # Perhaps plan does not exist? Prepare/compile plan. 00752 $params = ''; 00753 foreach($inputarr as $v) { 00754 if ($params) $params .= ','; 00755 if (is_string($v)) { 00756 $params .= 'VARCHAR'; 00757 } else if (is_integer($v)) { 00758 $params .= 'INTEGER'; 00759 } else { 00760 $params .= "REAL"; 00761 } 00762 } 00763 $sqlarr = explode('?',$sql); 00764 //print_r($sqlarr); 00765 $sql = ''; 00766 $i = 1; 00767 foreach($sqlarr as $v) { 00768 $sql .= $v.' $'.$i; 00769 $i++; 00770 } 00771 $s = "PREPARE $plan ($params) AS ".substr($sql,0,strlen($sql)-2); 00772 //adodb_pr($s); 00773 pg_exec($this->_connectionID,$s); 00774 //echo $this->ErrorMsg(); 00775 } 00776 00777 $rez = pg_exec($this->_connectionID,$exsql); 00778 } else { 00779 //adodb_backtrace(); 00780 $rez = pg_exec($this->_connectionID,$sql); 00781 } 00782 // check if no data returned, then no need to create real recordset 00783 if ($rez && pg_numfields($rez) <= 0) { 00784 if (is_resource($this->_resultid) && get_resource_type($this->_resultid) === 'pgsql result') { 00785 pg_freeresult($this->_resultid); 00786 } 00787 $this->_resultid = $rez; 00788 return true; 00789 } 00790 00791 return $rez; 00792 } 00793 00794 function _errconnect() 00795 { 00796 if (defined('DB_ERROR_CONNECT_FAILED')) return DB_ERROR_CONNECT_FAILED; 00797 else return 'Database connection failed'; 00798 } 00799 00800 /* Returns: the last error message from previous database operation */ 00801 function ErrorMsg() 00802 { 00803 if ($this->_errorMsg !== false) return $this->_errorMsg; 00804 if (ADODB_PHPVER >= 0x4300) { 00805 if (!empty($this->_resultid)) { 00806 $this->_errorMsg = @pg_result_error($this->_resultid); 00807 if ($this->_errorMsg) return $this->_errorMsg; 00808 } 00809 00810 if (!empty($this->_connectionID)) { 00811 $this->_errorMsg = @pg_last_error($this->_connectionID); 00812 } else $this->_errorMsg = $this->_errconnect(); 00813 } else { 00814 if (empty($this->_connectionID)) $this->_errconnect(); 00815 else $this->_errorMsg = @pg_errormessage($this->_connectionID); 00816 } 00817 return $this->_errorMsg; 00818 } 00819 00820 function ErrorNo() 00821 { 00822 $e = $this->ErrorMsg(); 00823 if (strlen($e)) { 00824 return ADOConnection::MetaError($e); 00825 } 00826 return 0; 00827 } 00828 00829 // returns true or false 00830 function _close() 00831 { 00832 if ($this->transCnt) $this->RollbackTrans(); 00833 if ($this->_resultid) { 00834 @pg_freeresult($this->_resultid); 00835 $this->_resultid = false; 00836 } 00837 @pg_close($this->_connectionID); 00838 $this->_connectionID = false; 00839 return true; 00840 } 00841 00842 00843 /* 00844 * Maximum size of C field 00845 */ 00846 function CharMax() 00847 { 00848 return 1000000000; // should be 1 Gb? 00849 } 00850 00851 /* 00852 * Maximum size of X field 00853 */ 00854 function TextMax() 00855 { 00856 return 1000000000; // should be 1 Gb? 00857 } 00858 00859 00860 } 00861 00862 /*-------------------------------------------------------------------------------------- 00863 Class Name: Recordset 00864 --------------------------------------------------------------------------------------*/ 00865 00866 class ADORecordSet_postgres64 extends ADORecordSet{ 00867 var $_blobArr; 00868 var $databaseType = "postgres64"; 00869 var $canSeek = true; 00870 function ADORecordSet_postgres64($queryID,$mode=false) 00871 { 00872 if ($mode === false) { 00873 global $ADODB_FETCH_MODE; 00874 $mode = $ADODB_FETCH_MODE; 00875 } 00876 switch ($mode) 00877 { 00878 case ADODB_FETCH_NUM: $this->fetchMode = PGSQL_NUM; break; 00879 case ADODB_FETCH_ASSOC:$this->fetchMode = PGSQL_ASSOC; break; 00880 00881 case ADODB_FETCH_DEFAULT: 00882 case ADODB_FETCH_BOTH: 00883 default: $this->fetchMode = PGSQL_BOTH; break; 00884 } 00885 $this->adodbFetchMode = $mode; 00886 $this->ADORecordSet($queryID); 00887 } 00888 00889 function &GetRowAssoc($upper=true) 00890 { 00891 if ($this->fetchMode == PGSQL_ASSOC && !$upper) return $this->fields; 00892 $row =& ADORecordSet::GetRowAssoc($upper); 00893 return $row; 00894 } 00895 00896 function _initrs() 00897 { 00898 global $ADODB_COUNTRECS; 00899 $qid = $this->_queryID; 00900 $this->_numOfRows = ($ADODB_COUNTRECS)? @pg_numrows($qid):-1; 00901 $this->_numOfFields = @pg_numfields($qid); 00902 00903 // cache types for blob decode check 00904 // apparently pg_fieldtype actually performs an sql query on the database to get the type. 00905 if (empty($this->connection->noBlobs)) 00906 for ($i=0, $max = $this->_numOfFields; $i < $max; $i++) { 00907 if (pg_fieldtype($qid,$i) == 'bytea') { 00908 $this->_blobArr[$i] = pg_fieldname($qid,$i); 00909 } 00910 } 00911 } 00912 00913 /* Use associative array to get fields array */ 00914 function Fields($colname) 00915 { 00916 if ($this->fetchMode != PGSQL_NUM) return @$this->fields[$colname]; 00917 00918 if (!$this->bind) { 00919 $this->bind = array(); 00920 for ($i=0; $i < $this->_numOfFields; $i++) { 00921 $o = $this->FetchField($i); 00922 $this->bind[strtoupper($o->name)] = $i; 00923 } 00924 } 00925 return $this->fields[$this->bind[strtoupper($colname)]]; 00926 } 00927 00928 function &FetchField($off = 0) 00929 { 00930 // offsets begin at 0 00931 00932 $o= new ADOFieldObject(); 00933 $o->name = @pg_fieldname($this->_queryID,$off); 00934 $o->type = @pg_fieldtype($this->_queryID,$off); 00935 $o->max_length = @pg_fieldsize($this->_queryID,$off); 00936 return $o; 00937 } 00938 00939 function _seek($row) 00940 { 00941 return @pg_fetch_row($this->_queryID,$row); 00942 } 00943 00944 function _decode($blob) 00945 { 00946 eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";'); 00947 return $realblob; 00948 } 00949 00950 function _fixblobs() 00951 { 00952 if ($this->fetchMode == PGSQL_NUM || $this->fetchMode == PGSQL_BOTH) { 00953 foreach($this->_blobArr as $k => $v) { 00954 $this->fields[$k] = ADORecordSet_postgres64::_decode($this->fields[$k]); 00955 } 00956 } 00957 if ($this->fetchMode == PGSQL_ASSOC || $this->fetchMode == PGSQL_BOTH) { 00958 foreach($this->_blobArr as $k => $v) { 00959 $this->fields[$v] = ADORecordSet_postgres64::_decode($this->fields[$v]); 00960 } 00961 } 00962 } 00963 00964 // 10% speedup to move MoveNext to child class 00965 function MoveNext() 00966 { 00967 if (!$this->EOF) { 00968 $this->_currentRow++; 00969 if ($this->_numOfRows < 0 || $this->_numOfRows > $this->_currentRow) { 00970 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); 00971 if (is_array($this->fields) && $this->fields) { 00972 if (isset($this->_blobArr)) $this->_fixblobs(); 00973 return true; 00974 } 00975 } 00976 $this->fields = false; 00977 $this->EOF = true; 00978 } 00979 return false; 00980 } 00981 00982 function _fetch() 00983 { 00984 00985 if ($this->_currentRow >= $this->_numOfRows && $this->_numOfRows >= 0) 00986 return false; 00987 00988 $this->fields = @pg_fetch_array($this->_queryID,$this->_currentRow,$this->fetchMode); 00989 00990 if ($this->fields && isset($this->_blobArr)) $this->_fixblobs(); 00991 00992 return (is_array($this->fields)); 00993 } 00994 00995 function _close() 00996 { 00997 return @pg_freeresult($this->_queryID); 00998 } 00999 01000 function MetaType($t,$len=-1,$fieldobj=false) 01001 { 01002 if (is_object($t)) { 01003 $fieldobj = $t; 01004 $t = $fieldobj->type; 01005 $len = $fieldobj->max_length; 01006 } 01007 switch (strtoupper($t)) { 01008 case 'MONEY': // stupid, postgres expects money to be a string 01009 case 'INTERVAL': 01010 case 'CHAR': 01011 case 'CHARACTER': 01012 case 'VARCHAR': 01013 case 'NAME': 01014 case 'BPCHAR': 01015 case '_VARCHAR': 01016 case 'INET': 01017 case 'MACADDR': 01018 if ($len <= $this->blobSize) return 'C'; 01019 01020 case 'TEXT': 01021 return 'X'; 01022 01023 case 'IMAGE': // user defined type 01024 case 'BLOB': // user defined type 01025 case 'BIT': // This is a bit string, not a single bit, so don't return 'L' 01026 case 'VARBIT': 01027 case 'BYTEA': 01028 return 'B'; 01029 01030 case 'BOOL': 01031 case 'BOOLEAN': 01032 return 'L'; 01033 01034 case 'DATE': 01035 return 'D'; 01036 01037 01038 case 'TIMESTAMP WITHOUT TIME ZONE': 01039 case 'TIME': 01040 case 'DATETIME': 01041 case 'TIMESTAMP': 01042 case 'TIMESTAMPTZ': 01043 return 'T'; 01044 01045 case 'SMALLINT': 01046 case 'BIGINT': 01047 case 'INTEGER': 01048 case 'INT8': 01049 case 'INT4': 01050 case 'INT2': 01051 if (isset($fieldobj) && 01052 empty($fieldobj->primary_key) && empty($fieldobj->unique)) return 'I'; 01053 01054 case 'OID': 01055 case 'SERIAL': 01056 return 'R'; 01057 01058 default: 01059 return 'N'; 01060 } 01061 } 01062 01063 } 01064 ?>