00001 <?php
00008 class PostgresField {
00009 private $name, $tablename, $type, $nullable, $max_length;
00010
00011 static function fromText($db, $table, $field) {
00012 global $wgDBmwschema;
00013
00014 $q = <<<SQL
00015 SELECT
00016 CASE WHEN typname = 'int2' THEN 'smallint'
00017 WHEN typname = 'int4' THEN 'integer'
00018 WHEN typname = 'int8' THEN 'bigint'
00019 WHEN typname = 'bpchar' THEN 'char'
00020 ELSE typname END AS typname,
00021 attnotnull, attlen
00022 FROM pg_class, pg_namespace, pg_attribute, pg_type
00023 WHERE relnamespace=pg_namespace.oid
00024 AND relkind='r'
00025 AND attrelid=pg_class.oid
00026 AND atttypid=pg_type.oid
00027 AND nspname=%s
00028 AND relname=%s
00029 AND attname=%s;
00030 SQL;
00031 $res = $db->query(sprintf($q,
00032 $db->addQuotes($wgDBmwschema),
00033 $db->addQuotes($table),
00034 $db->addQuotes($field)));
00035 $row = $db->fetchObject($res);
00036 if (!$row)
00037 return null;
00038 $n = new PostgresField;
00039 $n->type = $row->typname;
00040 $n->nullable = ($row->attnotnull == 'f');
00041 $n->name = $field;
00042 $n->tablename = $table;
00043 $n->max_length = $row->attlen;
00044 return $n;
00045 }
00046
00047 function name() {
00048 return $this->name;
00049 }
00050
00051 function tableName() {
00052 return $this->tablename;
00053 }
00054
00055 function type() {
00056 return $this->type;
00057 }
00058
00059 function nullable() {
00060 return $this->nullable;
00061 }
00062
00063 function maxLength() {
00064 return $this->max_length;
00065 }
00066 }
00067
00071 class DatabasePostgres extends DatabaseBase {
00072 var $mInsertId = null;
00073 var $mLastResult = null;
00074 var $numeric_version = null;
00075 var $mAffectedRows = null;
00076
00077 function DatabasePostgres($server = false, $user = false, $password = false, $dbName = false,
00078 $failFunction = false, $flags = 0 )
00079 {
00080
00081 $this->mFailFunction = $failFunction;
00082 $this->mFlags = $flags;
00083 $this->open( $server, $user, $password, $dbName);
00084
00085 }
00086
00087 function getType() {
00088 return 'postgres';
00089 }
00090
00091 function cascadingDeletes() {
00092 return true;
00093 }
00094 function cleanupTriggers() {
00095 return true;
00096 }
00097 function strictIPs() {
00098 return true;
00099 }
00100 function realTimestamps() {
00101 return true;
00102 }
00103 function implicitGroupby() {
00104 return false;
00105 }
00106 function implicitOrderby() {
00107 return false;
00108 }
00109 function searchableIPs() {
00110 return true;
00111 }
00112 function functionalIndexes() {
00113 return true;
00114 }
00115
00116 function hasConstraint( $name ) {
00117 global $wgDBmwschema;
00118 $SQL = "SELECT 1 FROM pg_catalog.pg_constraint c, pg_catalog.pg_namespace n WHERE c.connamespace = n.oid AND conname = '" . pg_escape_string( $name ) . "' AND n.nspname = '" . pg_escape_string($wgDBmwschema) ."'";
00119 return $this->numRows($res = $this->doQuery($SQL));
00120 }
00121
00122 static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0)
00123 {
00124 return new DatabasePostgres( $server, $user, $password, $dbName, $failFunction, $flags );
00125 }
00126
00131 function open( $server, $user, $password, $dbName ) {
00132 # Test for Postgres support, to avoid suppressed fatal error
00133 if ( !function_exists( 'pg_connect' ) ) {
00134 throw new DBConnectionError( $this, "Postgres functions missing, have you compiled PHP with the --with-pgsql option?\n (Note: if you recently installed PHP, you may need to restart your webserver and database)\n" );
00135 }
00136
00137 global $wgDBport;
00138
00139 if (!strlen($user)) { ## e.g. the class is being loaded
00140 return;
00141 }
00142 $this->close();
00143 $this->mServer = $server;
00144 $this->mPort = $port = $wgDBport;
00145 $this->mUser = $user;
00146 $this->mPassword = $password;
00147 $this->mDBname = $dbName;
00148
00149 $connectVars = array(
00150 'dbname' => $dbName,
00151 'user' => $user,
00152 'password' => $password );
00153 if ($server!=false && $server!="") {
00154 $connectVars['host'] = $server;
00155 }
00156 if ($port!=false && $port!="") {
00157 $connectVars['port'] = $port;
00158 }
00159 $connectString = $this->makeConnectionString( $connectVars, PGSQL_CONNECT_FORCE_NEW );
00160
00161 $this->installErrorHandler();
00162 $this->mConn = pg_connect( $connectString );
00163 $phpError = $this->restoreErrorHandler();
00164
00165 if ( $this->mConn == false ) {
00166 wfDebug( "DB connection error\n" );
00167 wfDebug( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" );
00168 wfDebug( $this->lastError()."\n" );
00169 if ( !$this->mFailFunction ) {
00170 throw new DBConnectionError( $this, $phpError );
00171 } else {
00172 return false;
00173 }
00174 }
00175
00176 $this->mOpened = true;
00177
00178 global $wgCommandLineMode;
00179 ## If called from the command-line (e.g. importDump), only show errors
00180 if ($wgCommandLineMode) {
00181 $this->doQuery( "SET client_min_messages = 'ERROR'" );
00182 }
00183
00184 $this->doQuery( "SET client_encoding='UTF8'" );
00185
00186 global $wgDBmwschema, $wgDBts2schema;
00187 if (isset( $wgDBmwschema ) && isset( $wgDBts2schema )
00188 && $wgDBmwschema !== 'mediawiki'
00189 && preg_match( '/^\w+$/', $wgDBmwschema )
00190 && preg_match( '/^\w+$/', $wgDBts2schema )
00191 ) {
00192 $safeschema = $this->quote_ident($wgDBmwschema);
00193 $safeschema2 = $this->quote_ident($wgDBts2schema);
00194 $this->doQuery( "SET search_path = $safeschema, $wgDBts2schema, public" );
00195 }
00196
00197 return $this->mConn;
00198 }
00199
00200 function makeConnectionString( $vars ) {
00201 $s = '';
00202 foreach ( $vars as $name => $value ) {
00203 $s .= "$name='" . str_replace( "'", "\\'", $value ) . "' ";
00204 }
00205 return $s;
00206 }
00207
00208
00209 function initial_setup($password, $dbName) {
00210
00211 global $wgDBname, $wgDBuser, $wgDBpassword, $wgDBsuperuser, $wgDBmwschema, $wgDBts2schema;
00212
00213 print "<li>Checking the version of Postgres...";
00214 $version = $this->getServerVersion();
00215 $PGMINVER = '8.1';
00216 if ($version < $PGMINVER) {
00217 print "<b>FAILED</b>. Required version is $PGMINVER. You have " . htmlspecialchars( $version ) . "</li>\n";
00218 dieout("</ul>");
00219 }
00220 print "version " . htmlspecialchars( $this->numeric_version ) . " is OK.</li>\n";
00221
00222 $safeuser = $this->quote_ident($wgDBuser);
00223
00224 if ($wgDBsuperuser) {
00225
00226 $SQL = "SELECT
00227 CASE WHEN usesuper IS TRUE THEN
00228 CASE WHEN usecreatedb IS TRUE THEN 3 ELSE 1 END
00229 ELSE CASE WHEN usecreatedb IS TRUE THEN 2 ELSE 0 END
00230 END AS rights
00231 FROM pg_catalog.pg_user WHERE usename = " . $this->addQuotes($wgDBsuperuser);
00232 $rows = $this->numRows($res = $this->doQuery($SQL));
00233 if (!$rows) {
00234 print "<li>ERROR: Could not read permissions for user \"" . htmlspecialchars( $wgDBsuperuser ) . "\"</li>\n";
00235 dieout('</ul>');
00236 }
00237 $perms = pg_fetch_result($res, 0, 0);
00238
00239 $SQL = "SELECT 1 FROM pg_catalog.pg_user WHERE usename = " . $this->addQuotes($wgDBuser);
00240 $rows = $this->numRows($this->doQuery($SQL));
00241 if ($rows) {
00242 print "<li>User \"" . htmlspecialchars( $wgDBuser ) . "\" already exists, skipping account creation.</li>";
00243 }
00244 else {
00245 if ($perms != 1 and $perms != 3) {
00246 print "<li>ERROR: the user \"" . htmlspecialchars( $wgDBsuperuser ) . "\" cannot create other users. ";
00247 print 'Please use a different Postgres user.</li>';
00248 dieout('</ul>');
00249 }
00250 print "<li>Creating user <b>" . htmlspecialchars( $wgDBuser ) . "</b>...";
00251 $safepass = $this->addQuotes($wgDBpassword);
00252 $SQL = "CREATE USER $safeuser NOCREATEDB PASSWORD $safepass";
00253 $this->doQuery($SQL);
00254 print "OK</li>\n";
00255 }
00256
00257 if ($dbName != $wgDBname) {
00258 $SQL = "SELECT 1 FROM pg_catalog.pg_database WHERE datname = " . $this->addQuotes($wgDBname);
00259 $rows = $this->numRows($this->doQuery($SQL));
00260 if ($rows) {
00261 print "<li>Database \"" . htmlspecialchars( $wgDBname ) . "\" already exists, skipping database creation.</li>";
00262 }
00263 else {
00264 if ($perms < 1) {
00265 print "<li>ERROR: the user \"" . htmlspecialchars( $wgDBsuperuser ) . "\" cannot create databases. ";
00266 print 'Please use a different Postgres user.</li>';
00267 dieout('</ul>');
00268 }
00269 print "<li>Creating database <b>" . htmlspecialchars( $wgDBname ) . "</b>...";
00270 $safename = $this->quote_ident($wgDBname);
00271 $SQL = "CREATE DATABASE $safename OWNER $safeuser ";
00272 $this->doQuery($SQL);
00273 print "OK</li>\n";
00274
00275 }
00276
00277
00278 print "<li>Connecting to \"" . htmlspecialchars( $wgDBname ) . "\" as superuser \"" .
00279 htmlspecialchars( $wgDBsuperuser ) . "\" to check rights...";
00280
00281 $connectVars = array();
00282 if ($this->mServer!=false && $this->mServer!="") {
00283 $connectVars['host'] = $this->mServer;
00284 }
00285 if ($this->mPort!=false && $this->mPort!="") {
00286 $connectVars['port'] = $this->mPort;
00287 }
00288 $connectVars['dbname'] = $wgDBname;
00289 $connectVars['user'] = $wgDBsuperuser;
00290 $connectVars['password'] = $password;
00291
00292 @$this->mConn = pg_connect( $this->makeConnectionString( $connectVars ) );
00293 if ( $this->mConn == false ) {
00294 print "<b>FAILED TO CONNECT!</b></li>";
00295 dieout("</ul>");
00296 }
00297 print "OK</li>\n";
00298 }
00299
00300 if ($this->numeric_version < 8.3) {
00301
00302 print "<li>Checking that tsearch2 is installed in the database \"" .
00303 htmlspecialchars( $wgDBname ) . "\"...";
00304 if (! $this->tableExists("pg_ts_cfg", $wgDBts2schema)) {
00305 print "<b>FAILED</b>. tsearch2 must be installed in the database \"" .
00306 htmlspecialchars( $wgDBname ) . "\".";
00307 print "Please see <a href='http://www.devx.com/opensource/Article/21674/0/page/2'>this article</a>";
00308 print " for instructions or ask on #postgresql on irc.freenode.net</li>\n";
00309 dieout("</ul>");
00310 }
00311 print "OK</li>\n";
00312 print "<li>Ensuring that user \"" . htmlspecialchars( $wgDBuser ) .
00313 "\" has select rights on the tsearch2 tables...";
00314 foreach (array('cfg','cfgmap','dict','parser') as $table) {
00315 $SQL = "GRANT SELECT ON pg_ts_$table TO $safeuser";
00316 $this->doQuery($SQL);
00317 }
00318 print "OK</li>\n";
00319 }
00320
00321
00322 $result = $this->schemaExists($wgDBmwschema);
00323 $safeschema = $this->quote_ident($wgDBmwschema);
00324 if (!$result) {
00325 print "<li>Creating schema <b>" . htmlspecialchars( $wgDBmwschema ) . "</b> ...";
00326 $result = $this->doQuery("CREATE SCHEMA $safeschema AUTHORIZATION $safeuser");
00327 if (!$result) {
00328 print "<b>FAILED</b>.</li>\n";
00329 dieout("</ul>");
00330 }
00331 print "OK</li>\n";
00332 }
00333 else {
00334 print "<li>Schema already exists, explicitly granting rights...\n";
00335 $safeschema2 = $this->addQuotes($wgDBmwschema);
00336 $SQL = "SELECT 'GRANT ALL ON '||pg_catalog.quote_ident(relname)||' TO $safeuser;'\n".
00337 "FROM pg_catalog.pg_class p, pg_catalog.pg_namespace n\n".
00338 "WHERE relnamespace = n.oid AND n.nspname = $safeschema2\n".
00339 "AND p.relkind IN ('r','S','v')\n";
00340 $SQL .= "UNION\n";
00341 $SQL .= "SELECT 'GRANT ALL ON FUNCTION '||pg_catalog.quote_ident(proname)||'('||\n".
00342 "pg_catalog.oidvectortypes(p.proargtypes)||') TO $safeuser;'\n".
00343 "FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n\n".
00344 "WHERE p.pronamespace = n.oid AND n.nspname = $safeschema2";
00345 $res = $this->doQuery($SQL);
00346 if (!$res) {
00347 print "<b>FAILED</b>. Could not set rights for the user.</li>\n";
00348 dieout("</ul>");
00349 }
00350 $this->doQuery("SET search_path = $safeschema");
00351 $rows = $this->numRows($res);
00352 while ($rows) {
00353 $rows--;
00354 $this->doQuery(pg_fetch_result($res, $rows, 0));
00355 }
00356 print "OK</li>";
00357 }
00358
00359
00360 $this->setup_plpgsql();
00361
00362 $wgDBsuperuser = '';
00363 return true;
00364
00365 }
00366
00367 if (!defined('POSTGRES_SEARCHPATH')) {
00368
00369 if ($this->numeric_version < 8.3) {
00370
00371 print "<li>Checking for tsearch2 in the schema \"" . htmlspecialchars( $wgDBts2schema ) . "\"...";
00372 if (! $this->tableExists("pg_ts_dict", $wgDBts2schema)) {
00373 print "<b>FAILED</b>. Make sure tsearch2 is installed. See <a href=";
00374 print "'http://www.devx.com/opensource/Article/21674/0/page/2'>this article</a>";
00375 print " for instructions.</li>\n";
00376 dieout("</ul>");
00377 }
00378 print "OK</li>\n";
00379
00380
00381 $ctype = pg_fetch_result($this->doQuery("SHOW lc_ctype"),0,0);
00382 print "<li>Checking tsearch2 permissions...";
00383
00384 error_reporting( 0 );
00385 $ts2tables = array('cfg','cfgmap','dict','parser');
00386 $safetsschema = $this->quote_ident($wgDBts2schema);
00387 foreach ( $ts2tables AS $tname ) {
00388 $SQL = "SELECT count(*) FROM $safetsschema.pg_ts_$tname";
00389 $res = $this->doQuery($SQL);
00390 if (!$res) {
00391 print "<b>FAILED</b> to access " . htmlspecialchars( "pg_ts_$tname" ) .
00392 ". Make sure that the user \"". htmlspecialchars( $wgDBuser ) .
00393 "\" has SELECT access to all four tsearch2 tables</li>\n";
00394 dieout("</ul>");
00395 }
00396 }
00397 $SQL = "SELECT ts_name FROM $safetsschema.pg_ts_cfg WHERE locale = " . $this->addQuotes( $ctype ) ;
00398 $SQL .= " ORDER BY CASE WHEN ts_name <> 'default' THEN 1 ELSE 0 END";
00399 $res = $this->doQuery($SQL);
00400 error_reporting( E_ALL );
00401 if (!$res) {
00402 print "<b>FAILED</b>. Could not determine the tsearch2 locale information</li>\n";
00403 dieout("</ul>");
00404 }
00405 print "OK</li>";
00406
00407
00408 print "<li>Verifying tsearch2 locale with " . htmlspecialchars( $ctype ) . "...";
00409 $rows = $this->numRows($res);
00410 $resetlocale = 0;
00411 if (!$rows) {
00412 print "<b>not found</b></li>\n";
00413 print "<li>Attempting to set default tsearch2 locale to \"" . htmlspecialchars( $ctype ) . "\"...";
00414 $resetlocale = 1;
00415 }
00416 else {
00417 $tsname = pg_fetch_result($res, 0, 0);
00418 if ($tsname != 'default') {
00419 print "<b>not set to default (" . htmlspecialchars( $tsname ) . ")</b>";
00420 print "<li>Attempting to change tsearch2 default locale to \"" .
00421 htmlspecialchars( $ctype ) . "\"...";
00422 $resetlocale = 1;
00423 }
00424 }
00425 if ($resetlocale) {
00426 $SQL = "UPDATE $safetsschema.pg_ts_cfg SET locale = " . $this->addQuotes( $ctype ) . " WHERE ts_name = 'default'";
00427 $res = $this->doQuery($SQL);
00428 if (!$res) {
00429 print "<b>FAILED</b>. ";
00430 print "Please make sure that the locale in pg_ts_cfg for \"default\" is set to \"" .
00431 htmlspecialchars( $ctype ) . "\"</li>\n";
00432 dieout("</ul>");
00433 }
00434 print "OK</li>";
00435 }
00436
00437
00438 $SQL = "SELECT $safetsschema.to_tsvector('default','MediaWiki tsearch2 testing')";
00439 $res = $this->doQuery($SQL);
00440 if (!$res) {
00441 print "<b>FAILED</b>. Specifically, \"" . htmlspecialchars( $SQL ) . "\" did not work.</li>";
00442 dieout("</ul>");
00443 }
00444 print "OK</li>";
00445 }
00446
00447
00448 $this->setup_plpgsql();
00449
00450
00451 $result = $this->schemaExists($wgDBmwschema);
00452 if (!$result) {
00453 print "<li>Creating schema <b>" . htmlspecialchars( $wgDBmwschema ) . "</b> ...";
00454 error_reporting( 0 );
00455 $safeschema = $this->quote_ident($wgDBmwschema);
00456 $result = $this->doQuery("CREATE SCHEMA $safeschema");
00457 error_reporting( E_ALL );
00458 if (!$result) {
00459 print "<b>FAILED</b>. The user \"" . htmlspecialchars( $wgDBuser ) .
00460 "\" must be able to access the schema. ".
00461 "You can try making them the owner of the database, or try creating the schema with a ".
00462 "different user, and then grant access to the \"" .
00463 htmlspecialchars( $wgDBuser ) . "\" user.</li>\n";
00464 dieout("</ul>");
00465 }
00466 print "OK</li>\n";
00467 }
00468 else if ($result != $wgDBuser) {
00469 print "<li>Schema \"" . htmlspecialchars( $wgDBmwschema ) . "\" exists but is not owned by \"" .
00470 htmlspecialchars( $wgDBuser ) . "\". Not ideal.</li>\n";
00471 }
00472 else {
00473 print "<li>Schema \"" . htmlspecialchars( $wgDBmwschema ) . "\" exists and is owned by \"" .
00474 htmlspecialchars( $wgDBuser ) . "\". Excellent.</li>\n";
00475 }
00476
00477
00478 print "<li>Setting the timezone to GMT for user \"" . htmlspecialchars( $wgDBuser ) . "\" ...";
00479 $SQL = "ALTER USER $safeuser SET timezone = 'GMT'";
00480 $result = pg_query($this->mConn, $SQL);
00481 if (!$result) {
00482 print "<b>FAILED</b>.</li>\n";
00483 dieout("</ul>");
00484 }
00485 print "OK</li>\n";
00486
00487 $SQL = "SET timezone = 'GMT'";
00488 $result = pg_query($this->mConn, $SQL);
00489 if (!$result) {
00490 print "<li>Failed to set timezone</li>\n";
00491 dieout("</ul>");
00492 }
00493
00494 print "<li>Setting the datestyle to ISO, YMD for user \"" . htmlspecialchars( $wgDBuser ) . "\" ...";
00495 $SQL = "ALTER USER $safeuser SET datestyle = 'ISO, YMD'";
00496 $result = pg_query($this->mConn, $SQL);
00497 if (!$result) {
00498 print "<b>FAILED</b>.</li>\n";
00499 dieout("</ul>");
00500 }
00501 print "OK</li>\n";
00502
00503 $SQL = "SET datestyle = 'ISO, YMD'";
00504 $result = pg_query($this->mConn, $SQL);
00505 if (!$result) {
00506 print "<li>Failed to set datestyle</li>\n";
00507 dieout("</ul>");
00508 }
00509
00510
00511 print "<li>Setting the search path for user \"" . htmlspecialchars( $wgDBuser ) . "\" ...";
00512 $path = $this->quote_ident($wgDBmwschema);
00513 if ($wgDBts2schema !== $wgDBmwschema)
00514 $path .= ", ". $this->quote_ident($wgDBts2schema);
00515 if ($wgDBmwschema !== 'public' and $wgDBts2schema !== 'public')
00516 $path .= ", public";
00517 $SQL = "ALTER USER $safeuser SET search_path = $path";
00518 $result = pg_query($this->mConn, $SQL);
00519 if (!$result) {
00520 print "<b>FAILED</b>.</li>\n";
00521 dieout("</ul>");
00522 }
00523 print "OK</li>\n";
00524
00525 $SQL = "SET search_path = $path";
00526 $result = pg_query($this->mConn, $SQL);
00527 if (!$result) {
00528 print "<li>Failed to set search_path</li>\n";
00529 dieout("</ul>");
00530 }
00531 define( "POSTGRES_SEARCHPATH", $path );
00532 }
00533 }
00534
00535
00536 function setup_plpgsql() {
00537 print "<li>Checking for Pl/Pgsql ...";
00538 $SQL = "SELECT 1 FROM pg_catalog.pg_language WHERE lanname = 'plpgsql'";
00539 $rows = $this->numRows($this->doQuery($SQL));
00540 if ($rows < 1) {
00541
00542 print "not installed. Attempting to install Pl/Pgsql ...";
00543 $SQL = "SELECT 1 FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) ".
00544 "WHERE relname = 'pg_pltemplate' AND nspname='pg_catalog'";
00545 $rows = $this->numRows($this->doQuery($SQL));
00546 if ($rows >= 1) {
00547 $olde = error_reporting(0);
00548 error_reporting($olde - E_WARNING);
00549 $result = $this->doQuery("CREATE LANGUAGE plpgsql");
00550 error_reporting($olde);
00551 if (!$result) {
00552 print "<b>FAILED</b>. You need to install the language plpgsql in the database <tt>" .
00553 htmlspecialchars( $wgDBname ) . "</tt></li>";
00554 dieout("</ul>");
00555 }
00556 }
00557 else {
00558 print "<b>FAILED</b>. You need to install the language plpgsql in the database <tt>" .
00559 htmlspecialchars( $wgDBname ) . "</tt></li>";
00560 dieout("</ul>");
00561 }
00562 }
00563 print "OK</li>\n";
00564 }
00565
00566
00571 function close() {
00572 $this->mOpened = false;
00573 if ( $this->mConn ) {
00574 return pg_close( $this->mConn );
00575 } else {
00576 return true;
00577 }
00578 }
00579
00580 function doQuery( $sql ) {
00581 if (function_exists('mb_convert_encoding')) {
00582 $sql = mb_convert_encoding($sql,'UTF-8');
00583 }
00584 $this->mLastResult = pg_query( $this->mConn, $sql);
00585 $this->mAffectedRows = null;
00586 return $this->mLastResult;
00587 }
00588
00589 function queryIgnore( $sql, $fname = '' ) {
00590 return $this->query( $sql, $fname, true );
00591 }
00592
00593 function freeResult( $res ) {
00594 if ( $res instanceof ResultWrapper ) {
00595 $res = $res->result;
00596 }
00597 if ( !@pg_free_result( $res ) ) {
00598 throw new DBUnexpectedError($this, "Unable to free Postgres result\n" );
00599 }
00600 }
00601
00602 function fetchObject( $res ) {
00603 if ( $res instanceof ResultWrapper ) {
00604 $res = $res->result;
00605 }
00606 @$row = pg_fetch_object( $res );
00607 # FIXME: HACK HACK HACK HACK debug
00608
00609 # TODO:
00610 # hashar : not sure if the following test really trigger if the object
00611 # fetching failed.
00612 if( pg_last_error($this->mConn) ) {
00613 throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
00614 }
00615 return $row;
00616 }
00617
00618 function fetchRow( $res ) {
00619 if ( $res instanceof ResultWrapper ) {
00620 $res = $res->result;
00621 }
00622 @$row = pg_fetch_array( $res );
00623 if( pg_last_error($this->mConn) ) {
00624 throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
00625 }
00626 return $row;
00627 }
00628
00629 function numRows( $res ) {
00630 if ( $res instanceof ResultWrapper ) {
00631 $res = $res->result;
00632 }
00633 @$n = pg_num_rows( $res );
00634 if( pg_last_error($this->mConn) ) {
00635 throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( pg_last_error($this->mConn) ) );
00636 }
00637 return $n;
00638 }
00639 function numFields( $res ) {
00640 if ( $res instanceof ResultWrapper ) {
00641 $res = $res->result;
00642 }
00643 return pg_num_fields( $res );
00644 }
00645 function fieldName( $res, $n ) {
00646 if ( $res instanceof ResultWrapper ) {
00647 $res = $res->result;
00648 }
00649 return pg_field_name( $res, $n );
00650 }
00651
00655 function insertId() {
00656 return $this->mInsertId;
00657 }
00658
00659 function dataSeek( $res, $row ) {
00660 if ( $res instanceof ResultWrapper ) {
00661 $res = $res->result;
00662 }
00663 return pg_result_seek( $res, $row );
00664 }
00665
00666 function lastError() {
00667 if ( $this->mConn ) {
00668 return pg_last_error();
00669 }
00670 else {
00671 return "No database connection";
00672 }
00673 }
00674 function lastErrno() {
00675 return pg_last_error() ? 1 : 0;
00676 }
00677
00678 function affectedRows() {
00679 if ( !is_null( $this->mAffectedRows ) ) {
00680
00681 return $this->mAffectedRows;
00682 }
00683 if( empty( $this->mLastResult ) )
00684 return 0;
00685 return pg_affected_rows( $this->mLastResult );
00686 }
00687
00696 function estimateRowCount( $table, $vars='*', $conds='', $fname = 'DatabasePostgres::estimateRowCount', $options = array() ) {
00697 $options['EXPLAIN'] = true;
00698 $res = $this->select( $table, $vars, $conds, $fname, $options );
00699 $rows = -1;
00700 if ( $res ) {
00701 $row = $this->fetchRow( $res );
00702 $count = array();
00703 if( preg_match( '/rows=(\d+)/', $row[0], $count ) ) {
00704 $rows = $count[1];
00705 }
00706 $this->freeResult($res);
00707 }
00708 return $rows;
00709 }
00710
00711
00716 function indexInfo( $table, $index, $fname = 'DatabasePostgres::indexInfo' ) {
00717 $sql = "SELECT indexname FROM pg_indexes WHERE tablename='$table'";
00718 $res = $this->query( $sql, $fname );
00719 if ( !$res ) {
00720 return null;
00721 }
00722 while ( $row = $this->fetchObject( $res ) ) {
00723 if ( $row->indexname == $this->indexName( $index ) ) {
00724 return $row;
00725 }
00726 }
00727 return false;
00728 }
00729
00730 function indexUnique ($table, $index, $fname = 'DatabasePostgres::indexUnique' ) {
00731 $sql = "SELECT indexname FROM pg_indexes WHERE tablename='{$table}'".
00732 " AND indexdef LIKE 'CREATE UNIQUE%(" .
00733 $this->strencode( $this->indexName( $index ) ) .
00734 ")'";
00735 $res = $this->query( $sql, $fname );
00736 if ( !$res )
00737 return null;
00738 while ($row = $this->fetchObject( $res ))
00739 return true;
00740 return false;
00741
00742 }
00743
00757 function insert( $table, $args, $fname = 'DatabasePostgres::insert', $options = array() ) {
00758 global $wgDBversion;
00759
00760 if ( !count( $args ) ) {
00761 return true;
00762 }
00763
00764 $table = $this->tableName( $table );
00765 if (! isset( $wgDBversion ) ) {
00766 $wgDBversion = $this->getServerVersion();
00767 }
00768
00769 if ( !is_array( $options ) )
00770 $options = array( $options );
00771
00772 if ( isset( $args[0] ) && is_array( $args[0] ) ) {
00773 $multi = true;
00774 $keys = array_keys( $args[0] );
00775 }
00776 else {
00777 $multi = false;
00778 $keys = array_keys( $args );
00779 }
00780
00781
00782 $ignore = in_array( 'IGNORE', $options ) ? 'mw' : '';
00783
00784
00785 $didbegin = 0;
00786 if ( $ignore ) {
00787 if (! $this->mTrxLevel) {
00788 $this->begin();
00789 $didbegin = 1;
00790 }
00791 $olde = error_reporting( 0 );
00792
00793
00794 $numrowsinserted = 0;
00795 }
00796
00797 $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ';
00798
00799 if ( $multi ) {
00800 if ( $wgDBversion >= 8.2 && !$ignore ) {
00801 $first = true;
00802 foreach ( $args as $row ) {
00803 if ( $first ) {
00804 $first = false;
00805 } else {
00806 $sql .= ',';
00807 }
00808 $sql .= '(' . $this->makeList( $row ) . ')';
00809 }
00810 $res = (bool)$this->query( $sql, $fname, $ignore );
00811 }
00812 else {
00813 $res = true;
00814 $origsql = $sql;
00815 foreach ( $args as $row ) {
00816 $tempsql = $origsql;
00817 $tempsql .= '(' . $this->makeList( $row ) . ')';
00818
00819 if ( $ignore ) {
00820 pg_query($this->mConn, "SAVEPOINT $ignore");
00821 }
00822
00823 $tempres = (bool)$this->query( $tempsql, $fname, $ignore );
00824
00825 if ( $ignore ) {
00826 $bar = pg_last_error();
00827 if ($bar != false) {
00828 pg_query( $this->mConn, "ROLLBACK TO $ignore" );
00829 }
00830 else {
00831 pg_query( $this->mConn, "RELEASE $ignore" );
00832 $numrowsinserted++;
00833 }
00834 }
00835
00836
00837
00838 if (! $tempres)
00839 $res = false;
00840 }
00841 }
00842 }
00843 else {
00844
00845 if ( $ignore ) {
00846 pg_query($this->mConn, "SAVEPOINT $ignore");
00847 }
00848
00849 $sql .= '(' . $this->makeList( $args ) . ')';
00850 $res = (bool)$this->query( $sql, $fname, $ignore );
00851 if ( $ignore ) {
00852 $bar = pg_last_error();
00853 if ($bar != false) {
00854 pg_query( $this->mConn, "ROLLBACK TO $ignore" );
00855 }
00856 else {
00857 pg_query( $this->mConn, "RELEASE $ignore" );
00858 $numrowsinserted++;
00859 }
00860 }
00861 }
00862 if ( $ignore ) {
00863 $olde = error_reporting( $olde );
00864 if ($didbegin) {
00865 $this->commit();
00866 }
00867
00868
00869 $this->mAffectedRows = $numrowsinserted;
00870
00871
00872 return true;
00873 }
00874
00875
00876 return $res;
00877
00878 }
00879
00888 function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = 'DatabasePostgres::insertSelect',
00889 $insertOptions = array(), $selectOptions = array() )
00890 {
00891 $destTable = $this->tableName( $destTable );
00892
00893
00894 $ignore = in_array( 'IGNORE', $insertOptions ) ? 'mw' : '';
00895
00896 if( is_array( $insertOptions ) ) {
00897 $insertOptions = implode( ' ', $insertOptions );
00898 }
00899 if( !is_array( $selectOptions ) ) {
00900 $selectOptions = array( $selectOptions );
00901 }
00902 list( $startOpts, $useIndex, $tailOpts ) = $this->makeSelectOptions( $selectOptions );
00903 if( is_array( $srcTable ) ) {
00904 $srcTable = implode( ',', array_map( array( &$this, 'tableName' ), $srcTable ) );
00905 } else {
00906 $srcTable = $this->tableName( $srcTable );
00907 }
00908
00909
00910 $didbegin = 0;
00911 if ( $ignore ) {
00912 if( !$this->mTrxLevel ) {
00913 $this->begin();
00914 $didbegin = 1;
00915 }
00916 $olde = error_reporting( 0 );
00917 $numrowsinserted = 0;
00918 pg_query( $this->mConn, "SAVEPOINT $ignore");
00919 }
00920
00921 $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' .
00922 " SELECT $startOpts " . implode( ',', $varMap ) .
00923 " FROM $srcTable $useIndex";
00924
00925 if ( $conds != '*') {
00926 $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
00927 }
00928
00929 $sql .= " $tailOpts";
00930
00931 $res = (bool)$this->query( $sql, $fname, $ignore );
00932 if( $ignore ) {
00933 $bar = pg_last_error();
00934 if( $bar != false ) {
00935 pg_query( $this->mConn, "ROLLBACK TO $ignore" );
00936 } else {
00937 pg_query( $this->mConn, "RELEASE $ignore" );
00938 $numrowsinserted++;
00939 }
00940 $olde = error_reporting( $olde );
00941 if( $didbegin ) {
00942 $this->commit();
00943 }
00944
00945
00946 $this->mAffectedRows = $numrowsinserted;
00947
00948
00949 return true;
00950 }
00951
00952 return $res;
00953 }
00954
00955 function tableName( $name ) {
00956 # Replace reserved words with better ones
00957 switch( $name ) {
00958 case 'user':
00959 return 'mwuser';
00960 case 'text':
00961 return 'pagecontent';
00962 default:
00963 return $name;
00964 }
00965 }
00966
00970 function nextSequenceValue( $seqName ) {
00971 $safeseq = preg_replace( "/'/", "''", $seqName );
00972 $res = $this->query( "SELECT nextval('$safeseq')" );
00973 $row = $this->fetchRow( $res );
00974 $this->mInsertId = $row[0];
00975 $this->freeResult( $res );
00976 return $this->mInsertId;
00977 }
00978
00982 function currentSequenceValue( $seqName ) {
00983 $safeseq = preg_replace( "/'/", "''", $seqName );
00984 $res = $this->query( "SELECT currval('$safeseq')" );
00985 $row = $this->fetchRow( $res );
00986 $currval = $row[0];
00987 $this->freeResult( $res );
00988 return $currval;
00989 }
00990
00991 # REPLACE query wrapper
00992 # Postgres simulates this with a DELETE followed by INSERT
00993 # $row is the row to insert, an associative array
00994 # $uniqueIndexes is an array of indexes. Each element may be either a
00995 # field name or an array of field names
00996 #
00997 # It may be more efficient to leave off unique indexes which are unlikely to collide.
00998 # However if you do this, you run the risk of encountering errors which wouldn't have
00999 # occurred in MySQL
01000 function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabasePostgres::replace' ) {
01001 $table = $this->tableName( $table );
01002
01003 if (count($rows)==0) {
01004 return;
01005 }
01006
01007 # Single row case
01008 if ( !is_array( reset( $rows ) ) ) {
01009 $rows = array( $rows );
01010 }
01011
01012 foreach( $rows as $row ) {
01013 # Delete rows which collide
01014 if ( $uniqueIndexes ) {
01015 $sql = "DELETE FROM $table WHERE ";
01016 $first = true;
01017 foreach ( $uniqueIndexes as $index ) {
01018 if ( $first ) {
01019 $first = false;
01020 $sql .= "(";
01021 } else {
01022 $sql .= ') OR (';
01023 }
01024 if ( is_array( $index ) ) {
01025 $first2 = true;
01026 foreach ( $index as $col ) {
01027 if ( $first2 ) {
01028 $first2 = false;
01029 } else {
01030 $sql .= ' AND ';
01031 }
01032 $sql .= $col.'=' . $this->addQuotes( $row[$col] );
01033 }
01034 } else {
01035 $sql .= $index.'=' . $this->addQuotes( $row[$index] );
01036 }
01037 }
01038 $sql .= ')';
01039 $this->query( $sql, $fname );
01040 }
01041
01042 # Now insert the row
01043 $sql = "INSERT INTO $table (" . $this->makeList( array_keys( $row ), LIST_NAMES ) .') VALUES (' .
01044 $this->makeList( $row, LIST_COMMA ) . ')';
01045 $this->query( $sql, $fname );
01046 }
01047 }
01048
01049 # DELETE where the condition is a join
01050 function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = 'DatabasePostgres::deleteJoin' ) {
01051 if ( !$conds ) {
01052 throw new DBUnexpectedError($this, 'Database::deleteJoin() called with empty $conds' );
01053 }
01054
01055 $delTable = $this->tableName( $delTable );
01056 $joinTable = $this->tableName( $joinTable );
01057 $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
01058 if ( $conds != '*' ) {
01059 $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
01060 }
01061 $sql .= ')';
01062
01063 $this->query( $sql, $fname );
01064 }
01065
01066 # Returns the size of a text field, or -1 for "unlimited"
01067 function textFieldSize( $table, $field ) {
01068 $table = $this->tableName( $table );
01069 $sql = "SELECT t.typname as ftype,a.atttypmod as size
01070 FROM pg_class c, pg_attribute a, pg_type t
01071 WHERE relname='$table' AND a.attrelid=c.oid AND
01072 a.atttypid=t.oid and a.attname='$field'";
01073 $res =$this->query($sql);
01074 $row=$this->fetchObject($res);
01075 if ($row->ftype=="varchar") {
01076 $size=$row->size-4;
01077 } else {
01078 $size=$row->size;
01079 }
01080 $this->freeResult( $res );
01081 return $size;
01082 }
01083
01084 function limitResult($sql, $limit, $offset=false) {
01085 return "$sql LIMIT $limit ".(is_numeric($offset)?" OFFSET {$offset} ":"");
01086 }
01087
01088 function wasDeadlock() {
01089 return $this->lastErrno() == '40P01';
01090 }
01091
01092 function duplicateTableStructure( $oldName, $newName, $temporary = false, $fname = 'DatabasePostgres::duplicateTableStructure' ) {
01093 return $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newName (LIKE $oldName INCLUDING DEFAULTS)", $fname );
01094 }
01095
01096 function timestamp( $ts=0 ) {
01097 return wfTimestamp(TS_POSTGRES,$ts);
01098 }
01099
01103 function aggregateValue ($valuedata,$valuename='value') {
01104 return $valuedata;
01105 }
01106
01107
01108 function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
01109
01110 $ignore = $this->ignoreErrors( true );
01111 $this->mErrorCount++;
01112
01113 if ($ignore || $tempIgnore) {
01114 wfDebug("SQL ERROR (ignored): $error\n");
01115 $this->ignoreErrors( $ignore );
01116 }
01117 else {
01118 $message = "A database error has occurred\n" .
01119 "Query: $sql\n" .
01120 "Function: $fname\n" .
01121 "Error: $errno $error\n";
01122 throw new DBUnexpectedError($this, $message);
01123 }
01124 }
01125
01129 function getSoftwareLink() {
01130 return "[http://www.postgresql.org/ PostgreSQL]";
01131 }
01132
01136 function getServerVersion() {
01137 $versionInfo = pg_version( $this->mConn );
01138 if ( version_compare( $versionInfo['client'], '7.4.0', 'lt' ) ) {
01139
01140 $this->numeric_version = '7.3 or earlier';
01141 } elseif ( isset( $versionInfo['server'] ) ) {
01142
01143 $this->numeric_version = $versionInfo['server'];
01144 } else {
01145
01146 $this->numeric_version = pg_parameter_status( $this->mConn, 'server_version' );
01147 }
01148 return $this->numeric_version;
01149 }
01150
01155 function relationExists( $table, $types, $schema = false ) {
01156 global $wgDBmwschema;
01157 if ( !is_array( $types ) )
01158 $types = array( $types );
01159 if ( !$schema )
01160 $schema = $wgDBmwschema;
01161 $etable = $this->addQuotes( $table );
01162 $eschema = $this->addQuotes( $schema );
01163 $SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
01164 . "WHERE c.relnamespace = n.oid AND c.relname = $etable AND n.nspname = $eschema "
01165 . "AND c.relkind IN ('" . implode("','", $types) . "')";
01166 $res = $this->query( $SQL );
01167 $count = $res ? $res->numRows() : 0;
01168 if ($res)
01169 $this->freeResult( $res );
01170 return $count ? true : false;
01171 }
01172
01173
01174
01175
01176
01177 function tableExists( $table, $schema = false ) {
01178 return $this->relationExists( $table, array( 'r', 'v' ), $schema );
01179 }
01180
01181 function sequenceExists( $sequence, $schema = false ) {
01182 return $this->relationExists( $sequence, 'S', $schema );
01183 }
01184
01185 function triggerExists( $table, $trigger ) {
01186 global $wgDBmwschema;
01187
01188 $q = <<<SQL
01189 SELECT 1 FROM pg_class, pg_namespace, pg_trigger
01190 WHERE relnamespace=pg_namespace.oid AND relkind='r'
01191 AND tgrelid=pg_class.oid
01192 AND nspname=%s AND relname=%s AND tgname=%s
01193 SQL;
01194 $res = $this->query(sprintf($q,
01195 $this->addQuotes($wgDBmwschema),
01196 $this->addQuotes($table),
01197 $this->addQuotes($trigger)));
01198 if (!$res)
01199 return null;
01200 $rows = $res->numRows();
01201 $this->freeResult( $res );
01202 return $rows;
01203 }
01204
01205 function ruleExists( $table, $rule ) {
01206 global $wgDBmwschema;
01207 $exists = $this->selectField("pg_rules", "rulename",
01208 array( "rulename" => $rule,
01209 "tablename" => $table,
01210 "schemaname" => $wgDBmwschema ) );
01211 return $exists === $rule;
01212 }
01213
01214 function constraintExists( $table, $constraint ) {
01215 global $wgDBmwschema;
01216 $SQL = sprintf("SELECT 1 FROM information_schema.table_constraints ".
01217 "WHERE constraint_schema = %s AND table_name = %s AND constraint_name = %s",
01218 $this->addQuotes($wgDBmwschema),
01219 $this->addQuotes($table),
01220 $this->addQuotes($constraint));
01221 $res = $this->query($SQL);
01222 if (!$res)
01223 return null;
01224 $rows = $res->numRows();
01225 $this->freeResult($res);
01226 return $rows;
01227 }
01228
01232 function schemaExists( $schema ) {
01233 $eschema = preg_replace("/'/", "''", $schema);
01234 $SQL = "SELECT rolname FROM pg_catalog.pg_namespace n, pg_catalog.pg_roles r "
01235 ."WHERE n.nspowner=r.oid AND n.nspname = '$eschema'";
01236 $res = $this->query( $SQL );
01237 if ( $res && $res->numRows() ) {
01238 $row = $res->fetchObject();
01239 $owner = $row->rolname;
01240 } else {
01241 $owner = false;
01242 }
01243 if ($res)
01244 $this->freeResult($res);
01245 return $owner;
01246 }
01247
01251 function fieldExists( $table, $field, $fname = 'DatabasePostgres::fieldExists' ) {
01252 global $wgDBmwschema;
01253 $etable = preg_replace("/'/", "''", $table);
01254 $eschema = preg_replace("/'/", "''", $wgDBmwschema);
01255 $ecol = preg_replace("/'/", "''", $field);
01256 $SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n, pg_catalog.pg_attribute a "
01257 . "WHERE c.relnamespace = n.oid AND c.relname = '$etable' AND n.nspname = '$eschema' "
01258 . "AND a.attrelid = c.oid AND a.attname = '$ecol'";
01259 $res = $this->query( $SQL, $fname );
01260 $count = $res ? $res->numRows() : 0;
01261 if ($res)
01262 $this->freeResult( $res );
01263 return $count;
01264 }
01265
01266 function fieldInfo( $table, $field ) {
01267 return PostgresField::fromText($this, $table, $field);
01268 }
01269
01273 function fieldType( $res, $index ) {
01274 if ( $res instanceof ResultWrapper ) {
01275 $res = $res->result;
01276 }
01277 return pg_field_type( $res, $index );
01278 }
01279
01280 function begin( $fname = 'DatabasePostgres::begin' ) {
01281 $this->query( 'BEGIN', $fname );
01282 $this->mTrxLevel = 1;
01283 }
01284 function immediateCommit( $fname = 'DatabasePostgres::immediateCommit' ) {
01285 return true;
01286 }
01287 function commit( $fname = 'DatabasePostgres::commit' ) {
01288 $this->query( 'COMMIT', $fname );
01289 $this->mTrxLevel = 0;
01290 }
01291
01292
01293 function limitResultForUpdate( $sql, $num ) {
01294 return $sql;
01295 }
01296
01297 function setup_database() {
01298 global $wgVersion, $wgDBmwschema, $wgDBts2schema, $wgDBport, $wgDBuser;
01299
01300
01301
01302 $ctest = "mediawiki_test_table";
01303 $safeschema = $this->quote_ident($wgDBmwschema);
01304 if ($this->tableExists($ctest, $wgDBmwschema)) {
01305 $this->doQuery("DROP TABLE $safeschema.$ctest");
01306 }
01307 $SQL = "CREATE TABLE $safeschema.$ctest(a int)";
01308 $olde = error_reporting( 0 );
01309 $res = $this->doQuery($SQL);
01310 error_reporting( $olde );
01311 if (!$res) {
01312 print "<b>FAILED</b>. Make sure that the user \"" . htmlspecialchars( $wgDBuser ) .
01313 "\" can write to the schema \"" . htmlspecialchars( $wgDBmwschema ) . "\"</li>\n";
01314 dieout(""); # Will close the main list <ul> and finish the page.
01315 }
01316 $this->doQuery("DROP TABLE $safeschema.$ctest");
01317
01318 $res = $this->sourceFile( "../maintenance/postgres/tables.sql" );
01319 if ($res === true) {
01320 print " done.</li>\n";
01321 } else {
01322 print " <b>FAILED</b></li>\n";
01323 dieout( htmlspecialchars( $res ) );
01324 }
01325
01326 ## Update version information
01327 $mwv = $this->addQuotes($wgVersion);
01328 $pgv = $this->addQuotes($this->getServerVersion());
01329 $pgu = $this->addQuotes($this->mUser);
01330 $mws = $this->addQuotes($wgDBmwschema);
01331 $tss = $this->addQuotes($wgDBts2schema);
01332 $pgp = $this->addQuotes($wgDBport);
01333 $dbn = $this->addQuotes($this->mDBname);
01334 $ctype = $this->addQuotes( pg_fetch_result($this->doQuery("SHOW lc_ctype"),0,0) );
01335
01336 $SQL = "UPDATE mediawiki_version SET mw_version=$mwv, pg_version=$pgv, pg_user=$pgu, ".
01337 "mw_schema = $mws, ts2_schema = $tss, pg_port=$pgp, pg_dbname=$dbn, ".
01338 "ctype = $ctype ".
01339 "WHERE type = 'Creation'";
01340 $this->query($SQL);
01341
01342 echo "<li>Populating interwiki table... ";
01343
01344 ## Avoid the non-standard "REPLACE INTO" syntax
01345 $f = fopen( "../maintenance/interwiki.sql", 'r' );
01346 if ($f == false ) {
01347 print "<b>FAILED</b></li>";
01348 dieout( "Could not find the interwiki.sql file" );
01349 }
01350 ## We simply assume it is already empty as we have just created it
01351 $SQL = "INSERT INTO interwiki(iw_prefix,iw_url,iw_local) VALUES ";
01352 while ( ! feof( $f ) ) {
01353 $line = fgets($f,1024);
01354 $matches = array();
01355 if (!preg_match('/^\s*(\(.+?),(\d)\)/', $line, $matches)) {
01356 continue;
01357 }
01358 $this->query("$SQL $matches[1],$matches[2])");
01359 }
01360 print " successfully populated.</li>\n";
01361
01362 $this->doQuery("COMMIT");
01363 }
01364
01365 function encodeBlob( $b ) {
01366 return new Blob ( pg_escape_bytea( $b ) ) ;
01367 }
01368
01369 function decodeBlob( $b ) {
01370 if ($b instanceof Blob) {
01371 $b = $b->fetch();
01372 }
01373 return pg_unescape_bytea( $b );
01374 }
01375
01376 function strencode( $s ) { ## Should not be called by us
01377 return pg_escape_string( $s );
01378 }
01379
01380 function addQuotes( $s ) {
01381 if ( is_null( $s ) ) {
01382 return 'NULL';
01383 } else if ( is_bool( $s ) ) {
01384 return intval( $s );
01385 } else if ($s instanceof Blob) {
01386 return "'".$s->fetch($s)."'";
01387 }
01388 return "'" . pg_escape_string($s) . "'";
01389 }
01390
01391 function quote_ident( $s ) {
01392 return '"' . preg_replace( '/"/', '""', $s) . '"';
01393 }
01394
01405 protected function replaceVars( $ins ) {
01406
01407 $ins = parent::replaceVars( $ins );
01408
01409 if ($this->numeric_version >= 8.3 && ( defined('TEXTSEARCH_COMPAT') && TEXTSEARCH_COMPAT != true )) {
01410
01411 $ins = preg_replace( "/to_tsvector\s*\(\s*'default'\s*,/", 'to_tsvector(', $ins );
01412 }
01413
01414 if ($this->numeric_version <= 8.1) {
01415 $ins = str_replace( 'USING gin', 'USING gist', $ins );
01416 }
01417
01418 return $ins;
01419 }
01420
01430 function makeSelectOptions( $options ) {
01431 $preLimitTail = $postLimitTail = '';
01432 $startOpts = $useIndex = '';
01433
01434 $noKeyOptions = array();
01435 foreach ( $options as $key => $option ) {
01436 if ( is_numeric( $key ) ) {
01437 $noKeyOptions[$option] = true;
01438 }
01439 }
01440
01441 if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY " . $options['GROUP BY'];
01442 if ( isset( $options['HAVING'] ) ) $preLimitTail .= " HAVING {$options['HAVING']}";
01443 if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY " . $options['ORDER BY'];
01444
01445
01446
01447
01448
01449
01450
01451 if ( isset( $noKeyOptions['FOR UPDATE'] ) ) $postLimitTail .= ' FOR UPDATE';
01452 if ( isset( $noKeyOptions['LOCK IN SHARE MODE'] ) ) $postLimitTail .= ' LOCK IN SHARE MODE';
01453 if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
01454
01455 return array( $startOpts, $useIndex, $preLimitTail, $postLimitTail );
01456 }
01457
01462 public function getLag() {
01463 # Not implemented for PostgreSQL
01464 return false;
01465 }
01466
01467 function setFakeSlaveLag( $lag ) {}
01468 function setFakeMaster( $enabled = true ) {}
01469
01470 function getDBname() {
01471 return $this->mDBname;
01472 }
01473
01474 function getServer() {
01475 return $this->mServer;
01476 }
01477
01478 function buildConcat( $stringList ) {
01479 return implode( ' || ', $stringList );
01480 }
01481
01482 public function getSearchEngine() {
01483 return "SearchPostgres";
01484 }
01485 }