Revert r108603, which was itself a revert of r107376, r107994. Before considering...
authorMax Semenik <maxsem@users.mediawiki.org>
Wed, 11 Jan 2012 20:19:55 +0000 (20:19 +0000)
committerMax Semenik <maxsem@users.mediawiki.org>
Wed, 11 Jan 2012 20:19:55 +0000 (20:19 +0000)
includes/db/Database.php
includes/db/DatabaseMysql.php
includes/db/DatabasePostgres.php
tests/phpunit/data/db/mysql/functions.sql [new file with mode: 0644]
tests/phpunit/data/db/postgres/functions.sql [new file with mode: 0644]
tests/phpunit/includes/db/DatabaseTest.php

index 9adac3b..5c186df 100644 (file)
@@ -228,6 +228,8 @@ abstract class DatabaseBase implements DatabaseType {
 
        protected $htmlErrors;
 
+       protected $delimiter = ';';
+
 # ------------------------------------------------------------------------------
 # Accessors
 # ------------------------------------------------------------------------------
@@ -3154,19 +3156,17 @@ abstract class DatabaseBase implements DatabaseType {
        function sourceStream( $fp, $lineCallback = false, $resultCallback = false,
                $fname = 'DatabaseBase::sourceStream' )
        {
-               $cmd = "";
+               $cmd = '';
                $done = false;
-               $dollarquote = false;
 
-               while ( ! feof( $fp ) ) {
+               while ( !feof( $fp ) ) {
                        if ( $lineCallback ) {
                                call_user_func( $lineCallback );
                        }
 
                        $line = trim( fgets( $fp ) );
-                       $sl = strlen( $line ) - 1;
 
-                       if ( $sl < 0 ) {
+                       if ( $line == '' ) {
                                continue;
                        }
 
@@ -3174,31 +3174,15 @@ abstract class DatabaseBase implements DatabaseType {
                                continue;
                        }
 
-                       # # Allow dollar quoting for function declarations
-                       if ( substr( $line, 0, 4 ) == '$mw$' ) {
-                               if ( $dollarquote ) {
-                                       $dollarquote = false;
-                                       $done = true;
-                               }
-                               else {
-                                       $dollarquote = true;
-                               }
-                       }
-                       elseif ( !$dollarquote ) {
-                               if ( ';' == $line[$sl] && ( $sl < 2 || ';' != $line[$sl - 1] ) ) {
-                                       $done = true;
-                                       $line = substr( $line, 0, $sl );
-                               }
-                       }
-
                        if ( $cmd != '' ) {
                                $cmd .= ' ';
                        }
 
+                       $done = $this->streamStatementEnd( $cmd, $line );
+
                        $cmd .= "$line\n";
 
-                       if ( $done ) {
-                               $cmd = str_replace( ';;', ";", $cmd );
+                       if ( $done || feof( $fp ) ) {
                                $cmd = $this->replaceVars( $cmd );
                                $res = $this->query( $cmd, $fname );
 
@@ -3219,6 +3203,24 @@ abstract class DatabaseBase implements DatabaseType {
                return true;
        }
 
+       /**
+        * Called by sourceStream() to check if we've reached a statement end
+        *
+        * @param $sql String: SQL assembled so far
+        * @param $newLine String: New line about to be added to $sql
+        * @returns Bool: Whether $newLine contains end of the statement
+        */
+       protected function streamStatementEnd( &$sql, &$newLine ) {
+               if ( $this->delimiter ) {
+                       $prev = $newLine;
+                       $newLine = preg_replace( '/' . preg_quote( $this->delimiter, '/' ) . '$/', '', $newLine );
+                       if ( $newLine != $prev ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
+
        /**
         * Database independent variable replacement. Replaces a set of variables
         * in an SQL statement with their contents as given by $this->getSchemaVars().
index 7054c8b..95d3968 100644 (file)
@@ -670,6 +670,15 @@ class DatabaseMysql extends DatabaseBase {
                }
        }
 
+       protected function streamStatementEnd( &$sql, &$newLine ) {
+               if ( strtoupper( substr( $newLine, 0, 9 ) ) == 'DELIMITER' ) {
+                       preg_match( '/^DELIMITER\s+(\S+)/' , $newLine, $m );
+                       $this->delimiter = $m[1];
+                       $newLine = '';
+               }
+               return parent::streamStatementEnd( $sql, $newLine );
+       }
+
        /**
         * @param $lockName string
         * @param $method string
index bb8ff7d..38950c3 100644 (file)
@@ -1045,4 +1045,17 @@ SQL;
        public function getSearchEngine() {
                return 'SearchPostgres';
        }
+
+       protected 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
diff --git a/tests/phpunit/data/db/mysql/functions.sql b/tests/phpunit/data/db/mysql/functions.sql
new file mode 100644 (file)
index 0000000..9e5e470
--- /dev/null
@@ -0,0 +1,12 @@
+-- MySQL test file for DatabaseTest::testStoredFunctions()
+
+DELIMITER //
+
+CREATE FUNCTION mw_test_function()
+RETURNS int DETERMINISTIC
+BEGIN
+       SET @foo = 21;
+       RETURN @foo * 2;
+END//
+
+DELIMITER //
diff --git a/tests/phpunit/data/db/postgres/functions.sql b/tests/phpunit/data/db/postgres/functions.sql
new file mode 100644 (file)
index 0000000..3086d4d
--- /dev/null
@@ -0,0 +1,12 @@
+-- Postgres test file for DatabaseTest::testStoredFunctions()
+
+CREATE FUNCTION mw_test_function()
+RETURNS INTEGER
+LANGUAGE plpgsql AS
+$mw$
+DECLARE foo INTEGER;
+BEGIN
+       foo := 21;
+       RETURN foo * 2;
+END
+$mw$;
index d480ac6..672e664 100644 (file)
@@ -2,12 +2,20 @@
 
 /**
  * @group Database
+ * @group DatabaseBase
  */
 class DatabaseTest extends MediaWikiTestCase {
-       var $db;
+       var $db, $functionTest = false;
 
        function setUp() {
-               $this->db = wfGetDB( DB_SLAVE );
+               $this->db = wfGetDB( DB_MASTER );
+       }
+
+       function tearDown() {
+               if ( $this->functionTest ) {
+                       $this->dropFunctions();
+                       $this->functionTest = false;
+               }
        }
 
        function testAddQuotesNull() {
@@ -90,6 +98,26 @@ class DatabaseTest extends MediaWikiTestCase {
                        $sql );
        }
 
+       /**
+        * @group Broken
+        */
+       function testStoredFunctions() {
+               if ( !in_array( wfGetDB( DB_MASTER )->getType(), array( 'mysql', 'postgres' ) ) ) {
+                       $this->markTestSkipped( 'MySQL or Postgres required' );
+               }
+               global $IP;
+               $this->dropFunctions();
+               $this->functionTest = true;
+               $this->assertTrue( $this->db->sourceFile( "$IP/tests/phpunit/data/db/{$this->db->getType()}/functions.sql" ) );
+               $res = $this->db->query( 'SELECT mw_test_function() AS test', __METHOD__ );
+               $this->assertEquals( 42, $res->fetchObject()->test );
+       }
+
+       private function dropFunctions() {
+               $this->db->query( 'DROP FUNCTION IF EXISTS mw_test_function'
+                       . ( $this->db->getType() == 'postgres'  ? '()' : '' )
+               );
+       }
 }