PostgreSQL: Improve SQL error handling
[lhc/web/wiklou.git] / includes / db / DatabasePostgres.php
index 38950c3..1f1d7ed 100644 (file)
@@ -145,6 +145,7 @@ class DatabasePostgres extends DatabaseBase {
 
        /**
         * Usually aborts on failure
+        * @return DatabaseBase|null
         */
        function open( $server, $user, $password, $dbName ) {
                # Test for Postgres support, to avoid suppressed fatal error
@@ -158,7 +159,6 @@ class DatabasePostgres extends DatabaseBase {
                        return;
                }
 
-               $this->close();
                $this->mServer = $server;
                $port = $wgDBport;
                $this->mUser = $user;
@@ -176,10 +176,14 @@ class DatabasePostgres extends DatabaseBase {
                if ( $port != false && $port != '' ) {
                        $connectVars['port'] = $port;
                }
-               $connectString = $this->makeConnectionString( $connectVars, PGSQL_CONNECT_FORCE_NEW );
+               $this->connectString = $this->makeConnectionString( $connectVars, PGSQL_CONNECT_FORCE_NEW );
+               $this->reOpen();
+       }
 
+       function reOpen() {
+               $this->close();
                $this->installErrorHandler();
-               $this->mConn = pg_connect( $connectString );
+               $this->mConn = pg_connect( $this->connectString );
                $phpError = $this->restoreErrorHandler();
 
                if ( !$this->mConn ) {
@@ -237,14 +241,10 @@ class DatabasePostgres extends DatabaseBase {
        /**
         * Closes a database connection, if it is open
         * Returns success, true if already closed
+        * @return bool
         */
-       function close() {
-               $this->mOpened = false;
-               if ( $this->mConn ) {
-                       return pg_close( $this->mConn );
-               } else {
-                       return true;
-               }
+       protected function closeConnection() {
+               return pg_close( $this->mConn );
        }
 
        protected function doQuery( $sql ) {
@@ -256,6 +256,13 @@ class DatabasePostgres extends DatabaseBase {
                return $this->mLastResult;
        }
 
+       function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
+               $this->rollback( __METHOD__ );
+               $this->reOpen();
+               parent::reportQueryError( $error, $errno, $sql, $fname, $tempIgnore );
+       }
+
+
        function queryIgnore( $sql, $fname = 'DatabasePostgres::queryIgnore' ) {
                return $this->query( $sql, $fname, true );
        }
@@ -331,6 +338,7 @@ class DatabasePostgres extends DatabaseBase {
 
        /**
         * This must be called after nextSequenceVal
+        * @return null
         */
        function insertId() {
                return $this->mInsertId;
@@ -371,6 +379,7 @@ class DatabasePostgres extends DatabaseBase {
         * This is not necessarily an accurate estimate, so use sparingly
         * Returns -1 if count cannot be found
         * Takes same arguments as Database::select()
+        * @return int
         */
        function estimateRowCount( $table, $vars = '*', $conds='', $fname = 'DatabasePostgres::estimateRowCount', $options = array() ) {
                $options['EXPLAIN'] = true;
@@ -389,6 +398,7 @@ class DatabasePostgres extends DatabaseBase {
        /**
         * Returns information about an index
         * If errors are explicitly ignored, returns NULL on failure
+        * @return bool|null
         */
        function indexInfo( $table, $index, $fname = 'DatabasePostgres::indexInfo' ) {
                $sql = "SELECT indexname FROM pg_indexes WHERE tablename='$table'";
@@ -461,7 +471,7 @@ class DatabasePostgres extends DatabaseBase {
                $didbegin = 0;
                if ( $ignore ) {
                        if ( !$this->mTrxLevel ) {
-                               $this->begin();
+                               $this->begin( __METHOD__ );
                                $didbegin = 1;
                        }
                        $olde = error_reporting( 0 );
@@ -535,7 +545,7 @@ class DatabasePostgres extends DatabaseBase {
                if ( $ignore ) {
                        $olde = error_reporting( $olde );
                        if ( $didbegin ) {
-                               $this->commit();
+                               $this->commit( __METHOD__ );
                        }
 
                        // Set the affected row count for the whole operation
@@ -555,6 +565,7 @@ class DatabasePostgres extends DatabaseBase {
         * $conds may be "*" to copy the whole table
         * srcTable may be an array of tables.
         * @todo FIXME: Implement this a little better (seperate select/insert)?
+        * @return bool
         */
        function insertSelect( $destTable, $srcTable, $varMap, $conds, $fname = 'DatabasePostgres::insertSelect',
                $insertOptions = array(), $selectOptions = array() )
@@ -581,7 +592,7 @@ class DatabasePostgres extends DatabaseBase {
                $didbegin = 0;
                if ( $ignore ) {
                        if( !$this->mTrxLevel ) {
-                               $this->begin();
+                               $this->begin( __METHOD__ );
                                $didbegin = 1;
                        }
                        $olde = error_reporting( 0 );
@@ -610,7 +621,7 @@ class DatabasePostgres extends DatabaseBase {
                        }
                        $olde = error_reporting( $olde );
                        if( $didbegin ) {
-                               $this->commit();
+                               $this->commit( __METHOD__ );
                        }
 
                        // Set the affected row count for the whole operation
@@ -624,63 +635,20 @@ class DatabasePostgres extends DatabaseBase {
        }
 
        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 ) {
+               # Replace reserved words with better ones
+               switch( $name ) {
                        case 'user':
-                               $table = "{$schema}\"mwuser\"";
-                               break;
+                               return 'mwuser';
                        case 'text':
-                               $table = "{$schema}\"pagecontent\"";
-                               break;
+                               return 'pagecontent';
                        default:
-                               $table = "{$schema}\"$table\"";
-                               break;
+                               return parent::tableName( $name, $format );
                }
-               return $table;
        }
 
        /**
         * Return the next in a sequence, save the value for retrieval via insertId()
+        * @return null
         */
        function nextSequenceValue( $seqName ) {
                $safeseq = str_replace( "'", "''", $seqName );
@@ -692,6 +660,7 @@ class DatabasePostgres extends DatabaseBase {
 
        /**
         * Return the current value of a sequence. Assumes it has been nextval'ed in this session.
+        * @return
         */
        function currentSequenceValue( $seqName ) {
                $safeseq = str_replace( "'", "''", $seqName );
@@ -791,6 +760,7 @@ class DatabasePostgres extends DatabaseBase {
        /**
         * Query whether a given relation exists (in the given schema, or the
         * default mw one if not given)
+        * @return bool
         */
        function relationExists( $table, $types, $schema = false ) {
                global $wgDBmwschema;
@@ -814,6 +784,7 @@ class DatabasePostgres extends DatabaseBase {
        /**
         * For backward compatibility, this function checks both tables and
         * views.
+        * @return bool
         */
        function tableExists( $table, $fname = __METHOD__, $schema = false ) {
                return $this->relationExists( $table, array( 'r', 'v' ), $schema );
@@ -877,6 +848,7 @@ SQL;
 
        /**
         * Query whether a given schema exists. Returns true if it does, false if it doesn't.
+        * @return bool
         */
        function schemaExists( $schema ) {
                $exists = $this->selectField( '"pg_catalog"."pg_namespace"', 1,
@@ -886,6 +858,7 @@ SQL;
 
        /**
         * Returns true if a given role (i.e. user) exists, false otherwise.
+        * @return bool
         */
        function roleExists( $roleName ) {
                $exists = $this->selectField( '"pg_catalog"."pg_roles"', 1,
@@ -899,6 +872,7 @@ SQL;
 
        /**
         * pg_field_type() wrapper
+        * @return string
         */
        function fieldType( $res, $index ) {
                if ( $res instanceof ResultWrapper ) {
@@ -1046,7 +1020,7 @@ SQL;
                return 'SearchPostgres';
        }
 
-       protected function streamStatementEnd( &$sql, &$newLine ) {
+       public function streamStatementEnd( &$sql, &$newLine ) {
                # Allow dollar quoting for function declarations
                if ( substr( $newLine, 0, 4 ) == '$mw$' ) {
                        if ( $this->delimiter ) {