rv r87948 "fix backtrace on SQL error"
[lhc/web/wiklou.git] / includes / db / DatabasePostgres.php
index 74316cf..2f0cdb4 100644 (file)
@@ -39,7 +39,7 @@ AND relname=%s
 AND attname=%s;
 SQL;
 
-               $table = $db->tableName( $table, false );
+               $table = $db->tableName( $table, 'raw' );
                $res = $db->query(
                        sprintf( $q,
                                $db->addQuotes( $wgDBmwschema ),
@@ -200,6 +200,7 @@ class DatabasePostgres extends DatabaseBase {
                $this->query( "SET client_encoding='UTF8'", __METHOD__ );
                $this->query( "SET datestyle = 'ISO, YMD'", __METHOD__ );
                $this->query( "SET timezone = 'GMT'", __METHOD__ );
+               $this->query( "SET standard_conforming_strings = on", __METHOD__ );
 
                global $wgDBmwschema;
                if ( $this->schemaExists( $wgDBmwschema ) ) {
@@ -263,7 +264,10 @@ class DatabasePostgres extends DatabaseBase {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
                }
-               if ( !@pg_free_result( $res ) ) {
+               wfSuppressWarnings();
+               $ok = pg_free_result( $res );
+               wfRestoreWarnings();
+               if ( !$ok ) {
                        throw new DBUnexpectedError( $this, "Unable to free Postgres result\n" );
                }
        }
@@ -272,7 +276,9 @@ class DatabasePostgres extends DatabaseBase {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
                }
-               @$row = pg_fetch_object( $res );
+               wfSuppressWarnings();
+               $row = pg_fetch_object( $res );
+               wfRestoreWarnings();
                # @todo FIXME: HACK HACK HACK HACK debug
 
                # @todo hashar: not sure if the following test really trigger if the object
@@ -287,7 +293,9 @@ class DatabasePostgres extends DatabaseBase {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
                }
-               @$row = pg_fetch_array( $res );
+               wfSuppressWarnings();
+               $row = pg_fetch_array( $res );
+               wfRestoreWarnings();
                if( pg_last_error( $this->mConn ) ) {
                        throw new DBUnexpectedError( $this, 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) ) );
                }
@@ -298,7 +306,9 @@ class DatabasePostgres extends DatabaseBase {
                if ( $res instanceof ResultWrapper ) {
                        $res = $res->result;
                }
-               @$n = pg_num_rows( $res );
+               wfSuppressWarnings();
+               $n = pg_num_rows( $res );
+               wfRestoreWarnings();
                if( pg_last_error( $this->mConn ) ) {
                        throw new DBUnexpectedError( $this, 'SQL error: ' . htmlspecialchars( pg_last_error( $this->mConn ) ) );
                }
@@ -555,7 +565,7 @@ class DatabasePostgres extends DatabaseBase {
                $ignore = in_array( 'IGNORE', $insertOptions ) ? 'mw' : '';
 
                if( is_array( $insertOptions ) ) {
-                       $insertOptions = implode( ' ', $insertOptions );
+                       $insertOptions = implode( ' ', $insertOptions ); // FIXME: This is unused
                }
                if( !is_array( $selectOptions ) ) {
                        $selectOptions = array( $selectOptions );
@@ -613,16 +623,60 @@ class DatabasePostgres extends DatabaseBase {
                return $res;
        }
 
-       function tableName( $name, $quoted = true ) {
-               # Replace reserved words with better ones
-               switch( $name ) {
+       function tableName( $name, $format = 'quoted' ) {
+               global $wgSharedDB, $wgSharedTables;
+               # Skip quoted tablenames.
+               if ( $this->isQuotedIdentifier( $name ) ) {
+                       return $name;
+               }
+               # Lets test for any bits of text that should never show up in a table
+               # name. Basically anything like JOIN or ON which are actually part of
+               # SQL queries, but may end up inside of the table value to combine
+               # sql. Such as how the API is doing.
+               # Note that we use a whitespace test rather than a \b test to avoid
+               # any remote case where a word like on may be inside of a table name
+               # surrounded by symbols which may be considered word breaks.
+               if ( preg_match( '/(^|\s)(DISTINCT|JOIN|ON|AS)(\s|$)/i', $name ) !== 0 ) {
+                       return $name;
+               }
+               # Extract the database prefix, if any and quote it
+               $dbDetails = explode( '.', $name, 2 );
+               if ( isset( $dbDetails[1] ) ) {
+                       $schema = '"' . $dbDetails[0] . '".';
+                       $table  = $dbDetails[1];
+               } else {
+                       $schema = ""; # do NOT force the schema (due to temporary tables)
+                       $table = $dbDetails[0];
+               }
+               if ( $format != 'quoted' ) {
+                       switch( $name ) {
+                               case 'user':
+                                       return 'mwuser';
+                               case 'text':
+                                       return 'pagecontent';
+                               default:
+                                       return $table;
+                       }
+               }
+
+               if ( isset( $wgSharedDB ) # We have a shared database (=> schema)
+                 && isset( $wgSharedTables )
+                 && is_array( $wgSharedTables )
+                 && in_array( $table, $wgSharedTables ) ) { # A shared table is selected
+                       $schema = "\"{$wgSharedDB}\".";
+               }
+               switch ( $table ) {
                        case 'user':
-                               return 'mwuser';
+                               $table = "{$schema}\"mwuser\"";
+                               break;
                        case 'text':
-                               return 'pagecontent';
+                               $table = "{$schema}\"pagecontent\"";
+                               break;
                        default:
-                               return parent::tableName( $name, $quoted );
+                               $table = "{$schema}\"$table\"";
+                               break;
                }
+               return $table;
        }
 
        /**
@@ -678,6 +732,24 @@ class DatabasePostgres extends DatabaseBase {
                return $this->query( 'CREATE ' . ( $temporary ? 'TEMPORARY ' : '' ) . " TABLE $newName (LIKE $oldName INCLUDING DEFAULTS)", $fname );
        }
 
+       function listTables( $prefix = null, $fname = 'DatabasePostgres::listTables' ) {
+               global $wgDBmwschema;
+               $eschema = $this->addQuotes( $wgDBmwschema );
+               $result = $this->query( "SELECT tablename FROM pg_tables WHERE schemaname = $eschema", $fname );
+
+               $endArray = array();
+
+               foreach( $result as $table ) {
+                       $vars = get_object_vars($table);
+                       $table = array_pop( $vars );
+                       if( !$prefix || strpos( $table, $prefix ) === 0 ) {
+                               $endArray[] = $table;
+                       }
+               }
+
+               return $endArray;
+       }
+
        function timestamp( $ts = 0 ) {
                return wfTimestamp( TS_POSTGRES, $ts );
        }
@@ -728,7 +800,7 @@ class DatabasePostgres extends DatabaseBase {
                if ( !$schema ) {
                        $schema = $wgDBmwschema;
                }
-               $table = $this->tableName( $table, false );
+               $table = $this->tableName( $table, 'raw' );
                $etable = $this->addQuotes( $table );
                $eschema = $this->addQuotes( $schema );
                $SQL = "SELECT 1 FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n "
@@ -743,7 +815,7 @@ class DatabasePostgres extends DatabaseBase {
         * For backward compatibility, this function checks both tables and
         * views.
         */
-       function tableExists( $table, $schema = false ) {
+       function tableExists( $table, $fname = __METHOD__, $schema = false ) {
                return $this->relationExists( $table, array( 'r', 'v' ), $schema );
        }
 
@@ -757,8 +829,8 @@ class DatabasePostgres extends DatabaseBase {
                $q = <<<SQL
        SELECT 1 FROM pg_class, pg_namespace, pg_trigger
                WHERE relnamespace=pg_namespace.oid AND relkind='r'
-                     AND tgrelid=pg_class.oid
-                     AND nspname=%s AND relname=%s AND tgname=%s
+                         AND tgrelid=pg_class.oid
+                         AND nspname=%s AND relname=%s AND tgname=%s
 SQL;
                $res = $this->query(
                        sprintf(
@@ -973,4 +1045,17 @@ SQL;
        public function getSearchEngine() {
                return 'SearchPostgres';
        }
+
+       public function streamStatementEnd( &$sql, &$newLine ) {
+               # Allow dollar quoting for function declarations
+               if ( substr( $newLine, 0, 4 ) == '$mw$' ) {
+                       if ( $this->delimiter ) {
+                               $this->delimiter = false;
+                       }
+                       else {
+                               $this->delimiter = ';';
+                       }
+               }
+               return parent::streamStatementEnd( $sql, $newLine );
+       }
 } // end DatabasePostgres class