Index: trunk/phase3/tests/phpunit/includes/db/DatabaseTest.php |
— | — | @@ -2,14 +2,22 @@ |
3 | 3 | |
4 | 4 | /** |
5 | 5 | * @group Database |
| 6 | + * @group DatabaseBase |
6 | 7 | */ |
7 | 8 | class DatabaseTest extends MediaWikiTestCase { |
8 | | - var $db; |
| 9 | + var $db, $functionTest = false; |
9 | 10 | |
10 | 11 | function setUp() { |
11 | | - $this->db = wfGetDB( DB_SLAVE ); |
| 12 | + $this->db = wfGetDB( DB_MASTER ); |
12 | 13 | } |
13 | 14 | |
| 15 | + function tearDown() { |
| 16 | + if ( $this->functionTest ) { |
| 17 | + $this->dropFunctions(); |
| 18 | + $this->functionTest = false; |
| 19 | + } |
| 20 | + } |
| 21 | + |
14 | 22 | function testAddQuotesNull() { |
15 | 23 | $check = "NULL"; |
16 | 24 | if ( $this->db->getType() === 'sqlite' || $this->db->getType() === 'oracle' ) { |
— | — | @@ -90,6 +98,23 @@ |
91 | 99 | $sql ); |
92 | 100 | } |
93 | 101 | |
| 102 | + function testStoredFunctions() { |
| 103 | + if ( !in_array( wfGetDB( DB_MASTER )->getType(), array( 'mysql', 'postgres' ) ) ) { |
| 104 | + $this->markTestSkipped( 'MySQL or Postgres required' ); |
| 105 | + } |
| 106 | + global $IP; |
| 107 | + $this->dropFunctions(); |
| 108 | + $this->functionTest = true; |
| 109 | + $this->assertTrue( $this->db->sourceFile( "$IP/tests/phpunit/data/db/{$this->db->getType()}/functions.sql" ) ); |
| 110 | + $res = $this->db->query( 'SELECT mw_test_function() AS test', __METHOD__ ); |
| 111 | + $this->assertEquals( 42, $res->fetchObject()->test ); |
| 112 | + } |
| 113 | + |
| 114 | + private function dropFunctions() { |
| 115 | + $this->db->query( 'DROP FUNCTION IF EXISTS mw_test_function' |
| 116 | + . ( $this->db->getType() == 'postgres' ? '()' : '' ) |
| 117 | + ); |
| 118 | + } |
94 | 119 | } |
95 | 120 | |
96 | 121 | |
Index: trunk/phase3/tests/phpunit/data/db/mysql/functions.sql |
— | — | @@ -0,0 +1,12 @@ |
| 2 | +-- MySQL test file for DatabaseTest::testStoredFunctions() |
| 3 | + |
| 4 | +DELIMITER // |
| 5 | + |
| 6 | +CREATE FUNCTION mw_test_function() |
| 7 | +RETURNS int DETERMINISTIC |
| 8 | +BEGIN |
| 9 | + SET @foo = 21; |
| 10 | + RETURN @foo * 2; |
| 11 | +END// |
| 12 | + |
| 13 | +DELIMITER // |
Property changes on: trunk/phase3/tests/phpunit/data/db/mysql/functions.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 14 | + native |
Index: trunk/phase3/tests/phpunit/data/db/postgres/functions.sql |
— | — | @@ -0,0 +1,12 @@ |
| 2 | +-- Postgres test file for DatabaseTest::testStoredFunctions() |
| 3 | + |
| 4 | +CREATE FUNCTION mw_test_function() |
| 5 | +RETURNS INTEGER |
| 6 | +LANGUAGE plpgsql AS |
| 7 | +$mw$ |
| 8 | +DECLARE foo INTEGER; |
| 9 | +BEGIN |
| 10 | + foo := 21; |
| 11 | + RETURN foo * 2; |
| 12 | +END |
| 13 | +$mw$; |
Property changes on: trunk/phase3/tests/phpunit/data/db/postgres/functions.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 14 | + native |
Index: trunk/phase3/includes/db/DatabaseMysql.php |
— | — | @@ -670,6 +670,15 @@ |
671 | 671 | } |
672 | 672 | } |
673 | 673 | |
| 674 | + protected function streamStatementEnd( &$sql, &$newLine ) { |
| 675 | + if ( strtoupper( substr( $newLine, 0, 9 ) ) == 'DELIMITER' ) { |
| 676 | + preg_match( '/^DELIMITER\s+(\S+)/' , $newLine, $m ); |
| 677 | + $this->delimiter = $m[1]; |
| 678 | + $newLine = ''; |
| 679 | + } |
| 680 | + return parent::streamStatementEnd( $sql, $newLine ); |
| 681 | + } |
| 682 | + |
674 | 683 | /** |
675 | 684 | * @param $lockName string |
676 | 685 | * @param $method string |
Index: trunk/phase3/includes/db/DatabasePostgres.php |
— | — | @@ -1050,4 +1050,17 @@ |
1051 | 1051 | public function getSearchEngine() { |
1052 | 1052 | return 'SearchPostgres'; |
1053 | 1053 | } |
| 1054 | + |
| 1055 | + protected function streamStatementEnd( &$sql, &$newLine ) { |
| 1056 | + # Allow dollar quoting for function declarations |
| 1057 | + if ( substr( $newLine, 0, 4 ) == '$mw$' ) { |
| 1058 | + if ( $this->delimiter ) { |
| 1059 | + $this->delimiter = false; |
| 1060 | + } |
| 1061 | + else { |
| 1062 | + $this->delimiter = ';'; |
| 1063 | + } |
| 1064 | + } |
| 1065 | + return parent::streamStatementEnd( $sql, $newLine ); |
| 1066 | + } |
1054 | 1067 | } // end DatabasePostgres class |
Index: trunk/phase3/includes/db/Database.php |
— | — | @@ -228,6 +228,8 @@ |
229 | 229 | |
230 | 230 | protected $htmlErrors; |
231 | 231 | |
| 232 | + protected $delimiter = ';'; |
| 233 | + |
232 | 234 | # ------------------------------------------------------------------------------ |
233 | 235 | # Accessors |
234 | 236 | # ------------------------------------------------------------------------------ |
— | — | @@ -3156,19 +3158,17 @@ |
3157 | 3159 | function sourceStream( $fp, $lineCallback = false, $resultCallback = false, |
3158 | 3160 | $fname = 'DatabaseBase::sourceStream' ) |
3159 | 3161 | { |
3160 | | - $cmd = ""; |
| 3162 | + $cmd = ''; |
3161 | 3163 | $done = false; |
3162 | | - $dollarquote = false; |
3163 | 3164 | |
3164 | | - while ( ! feof( $fp ) ) { |
| 3165 | + while ( !feof( $fp ) ) { |
3165 | 3166 | if ( $lineCallback ) { |
3166 | 3167 | call_user_func( $lineCallback ); |
3167 | 3168 | } |
3168 | 3169 | |
3169 | 3170 | $line = trim( fgets( $fp ) ); |
3170 | | - $sl = strlen( $line ) - 1; |
3171 | 3171 | |
3172 | | - if ( $sl < 0 ) { |
| 3172 | + if ( $line == '' ) { |
3173 | 3173 | continue; |
3174 | 3174 | } |
3175 | 3175 | |
— | — | @@ -3176,31 +3176,15 @@ |
3177 | 3177 | continue; |
3178 | 3178 | } |
3179 | 3179 | |
3180 | | - # # Allow dollar quoting for function declarations |
3181 | | - if ( substr( $line, 0, 4 ) == '$mw$' ) { |
3182 | | - if ( $dollarquote ) { |
3183 | | - $dollarquote = false; |
3184 | | - $done = true; |
3185 | | - } |
3186 | | - else { |
3187 | | - $dollarquote = true; |
3188 | | - } |
3189 | | - } |
3190 | | - elseif ( !$dollarquote ) { |
3191 | | - if ( ';' == $line[$sl] && ( $sl < 2 || ';' != $line[$sl - 1] ) ) { |
3192 | | - $done = true; |
3193 | | - $line = substr( $line, 0, $sl ); |
3194 | | - } |
3195 | | - } |
3196 | | - |
3197 | 3180 | if ( $cmd != '' ) { |
3198 | 3181 | $cmd .= ' '; |
3199 | 3182 | } |
3200 | 3183 | |
| 3184 | + $done = $this->streamStatementEnd( $cmd, $line ); |
| 3185 | + |
3201 | 3186 | $cmd .= "$line\n"; |
3202 | 3187 | |
3203 | | - if ( $done ) { |
3204 | | - $cmd = str_replace( ';;', ";", $cmd ); |
| 3188 | + if ( $done || feof( $fp ) ) { |
3205 | 3189 | $cmd = $this->replaceVars( $cmd ); |
3206 | 3190 | $res = $this->query( $cmd, $fname ); |
3207 | 3191 | |
— | — | @@ -3222,6 +3206,24 @@ |
3223 | 3207 | } |
3224 | 3208 | |
3225 | 3209 | /** |
| 3210 | + * Called by sourceStream() to check if we've reached a statement end |
| 3211 | + * |
| 3212 | + * @param $sql String: SQL assembled so far |
| 3213 | + * @param $newLine String: New line about to be added to $sql |
| 3214 | + * @returns Bool: Whether $newLine contains end of the statement |
| 3215 | + */ |
| 3216 | + protected function streamStatementEnd( &$sql, &$newLine ) { |
| 3217 | + if ( $this->delimiter ) { |
| 3218 | + $prev = $newLine; |
| 3219 | + $newLine = preg_replace( '/' . preg_quote( $this->delimiter, '/' ) . '$/', '', $newLine ); |
| 3220 | + if ( $newLine != $prev ) { |
| 3221 | + return true; |
| 3222 | + } |
| 3223 | + } |
| 3224 | + return false; |
| 3225 | + } |
| 3226 | + |
| 3227 | + /** |
3226 | 3228 | * Database independent variable replacement. Replaces a set of variables |
3227 | 3229 | * in an SQL statement with their contents as given by $this->getSchemaVars(). |
3228 | 3230 | * |