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