These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * This is the Oracle database abstraction layer. |
||
4 | * |
||
5 | * This program is free software; you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation; either version 2 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU General Public License along |
||
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
18 | * http://www.gnu.org/copyleft/gpl.html |
||
19 | * |
||
20 | * @file |
||
21 | * @ingroup Database |
||
22 | */ |
||
23 | |||
24 | /** |
||
25 | * The oci8 extension is fairly weak and doesn't support oci_num_rows, among |
||
26 | * other things. We use a wrapper class to handle that and other |
||
27 | * Oracle-specific bits, like converting column names back to lowercase. |
||
28 | * @ingroup Database |
||
29 | */ |
||
30 | class ORAResult { |
||
31 | private $rows; |
||
32 | private $cursor; |
||
33 | private $nrows; |
||
34 | |||
35 | private $columns = []; |
||
36 | |||
37 | private function array_unique_md( $array_in ) { |
||
38 | $array_out = []; |
||
39 | $array_hashes = []; |
||
40 | |||
41 | foreach ( $array_in as $item ) { |
||
42 | $hash = md5( serialize( $item ) ); |
||
43 | if ( !isset( $array_hashes[$hash] ) ) { |
||
44 | $array_hashes[$hash] = $hash; |
||
45 | $array_out[] = $item; |
||
46 | } |
||
47 | } |
||
48 | |||
49 | return $array_out; |
||
50 | } |
||
51 | |||
52 | /** |
||
53 | * @param IDatabase $db |
||
54 | * @param resource $stmt A valid OCI statement identifier |
||
55 | * @param bool $unique |
||
56 | */ |
||
57 | function __construct( &$db, $stmt, $unique = false ) { |
||
58 | $this->db =& $db; |
||
59 | |||
60 | $this->nrows = oci_fetch_all( $stmt, $this->rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW | OCI_NUM ); |
||
61 | if ( $this->nrows === false ) { |
||
62 | $e = oci_error( $stmt ); |
||
63 | $db->reportQueryError( $e['message'], $e['code'], '', __METHOD__ ); |
||
64 | $this->free(); |
||
65 | |||
66 | return; |
||
67 | } |
||
68 | |||
69 | if ( $unique ) { |
||
70 | $this->rows = $this->array_unique_md( $this->rows ); |
||
71 | $this->nrows = count( $this->rows ); |
||
72 | } |
||
73 | |||
74 | if ( $this->nrows > 0 ) { |
||
75 | foreach ( $this->rows[0] as $k => $v ) { |
||
76 | $this->columns[$k] = strtolower( oci_field_name( $stmt, $k + 1 ) ); |
||
77 | } |
||
78 | } |
||
79 | |||
80 | $this->cursor = 0; |
||
81 | oci_free_statement( $stmt ); |
||
82 | } |
||
83 | |||
84 | public function free() { |
||
85 | unset( $this->db ); |
||
86 | } |
||
87 | |||
88 | public function seek( $row ) { |
||
89 | $this->cursor = min( $row, $this->nrows ); |
||
90 | } |
||
91 | |||
92 | public function numRows() { |
||
93 | return $this->nrows; |
||
94 | } |
||
95 | |||
96 | public function numFields() { |
||
97 | return count( $this->columns ); |
||
98 | } |
||
99 | |||
100 | public function fetchObject() { |
||
101 | if ( $this->cursor >= $this->nrows ) { |
||
102 | return false; |
||
103 | } |
||
104 | $row = $this->rows[$this->cursor++]; |
||
105 | $ret = new stdClass(); |
||
106 | foreach ( $row as $k => $v ) { |
||
107 | $lc = $this->columns[$k]; |
||
108 | $ret->$lc = $v; |
||
109 | } |
||
110 | |||
111 | return $ret; |
||
112 | } |
||
113 | |||
114 | public function fetchRow() { |
||
115 | if ( $this->cursor >= $this->nrows ) { |
||
116 | return false; |
||
117 | } |
||
118 | |||
119 | $row = $this->rows[$this->cursor++]; |
||
120 | $ret = []; |
||
121 | foreach ( $row as $k => $v ) { |
||
122 | $lc = $this->columns[$k]; |
||
123 | $ret[$lc] = $v; |
||
124 | $ret[$k] = $v; |
||
125 | } |
||
126 | |||
127 | return $ret; |
||
128 | } |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * @ingroup Database |
||
133 | */ |
||
134 | class DatabaseOracle extends DatabaseBase { |
||
135 | /** @var resource */ |
||
136 | protected $mLastResult = null; |
||
137 | |||
138 | /** @var int The number of rows affected as an integer */ |
||
139 | protected $mAffectedRows; |
||
140 | |||
141 | /** @var int */ |
||
142 | private $mInsertId = null; |
||
143 | |||
144 | /** @var bool */ |
||
145 | private $ignoreDupValOnIndex = false; |
||
146 | |||
147 | /** @var bool|array */ |
||
148 | private $sequenceData = null; |
||
149 | |||
150 | /** @var string Character set for Oracle database */ |
||
151 | private $defaultCharset = 'AL32UTF8'; |
||
152 | |||
153 | /** @var array */ |
||
154 | private $mFieldInfoCache = []; |
||
155 | |||
156 | function __construct( array $p ) { |
||
157 | global $wgDBprefix; |
||
158 | |||
159 | if ( $p['tablePrefix'] == 'get from global' ) { |
||
160 | $p['tablePrefix'] = $wgDBprefix; |
||
161 | } |
||
162 | $p['tablePrefix'] = strtoupper( $p['tablePrefix'] ); |
||
163 | parent::__construct( $p ); |
||
164 | Hooks::run( 'DatabaseOraclePostInit', [ $this ] ); |
||
165 | } |
||
166 | |||
167 | function __destruct() { |
||
168 | if ( $this->mOpened ) { |
||
169 | MediaWiki\suppressWarnings(); |
||
170 | $this->close(); |
||
171 | MediaWiki\restoreWarnings(); |
||
172 | } |
||
173 | } |
||
174 | |||
175 | function getType() { |
||
176 | return 'oracle'; |
||
177 | } |
||
178 | |||
179 | function implicitGroupby() { |
||
180 | return false; |
||
181 | } |
||
182 | |||
183 | function implicitOrderby() { |
||
184 | return false; |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Usually aborts on failure |
||
189 | * @param string $server |
||
190 | * @param string $user |
||
191 | * @param string $password |
||
192 | * @param string $dbName |
||
193 | * @throws DBConnectionError |
||
194 | * @return resource|null |
||
195 | */ |
||
196 | function open( $server, $user, $password, $dbName ) { |
||
197 | global $wgDBOracleDRCP; |
||
198 | if ( !function_exists( 'oci_connect' ) ) { |
||
199 | throw new DBConnectionError( |
||
200 | $this, |
||
201 | "Oracle functions missing, have you compiled PHP with the --with-oci8 option?\n " . |
||
202 | "(Note: if you recently installed PHP, you may need to restart your webserver\n " . |
||
203 | "and database)\n" ); |
||
204 | } |
||
205 | |||
206 | $this->close(); |
||
207 | $this->mUser = $user; |
||
208 | $this->mPassword = $password; |
||
209 | // changed internal variables functions |
||
210 | // mServer now holds the TNS endpoint |
||
211 | // mDBname is schema name if different from username |
||
212 | if ( !$server ) { |
||
213 | // backward compatibillity (server used to be null and TNS was supplied in dbname) |
||
214 | $this->mServer = $dbName; |
||
215 | $this->mDBname = $user; |
||
216 | } else { |
||
217 | $this->mServer = $server; |
||
218 | if ( !$dbName ) { |
||
219 | $this->mDBname = $user; |
||
220 | } else { |
||
221 | $this->mDBname = $dbName; |
||
222 | } |
||
223 | } |
||
224 | |||
225 | if ( !strlen( $user ) ) { # e.g. the class is being loaded |
||
226 | return null; |
||
227 | } |
||
228 | |||
229 | if ( $wgDBOracleDRCP ) { |
||
230 | $this->setFlag( DBO_PERSISTENT ); |
||
231 | } |
||
232 | |||
233 | $session_mode = $this->mFlags & DBO_SYSDBA ? OCI_SYSDBA : OCI_DEFAULT; |
||
234 | |||
235 | MediaWiki\suppressWarnings(); |
||
236 | if ( $this->mFlags & DBO_PERSISTENT ) { |
||
237 | $this->mConn = oci_pconnect( |
||
238 | $this->mUser, |
||
239 | $this->mPassword, |
||
240 | $this->mServer, |
||
241 | $this->defaultCharset, |
||
242 | $session_mode |
||
243 | ); |
||
244 | } elseif ( $this->mFlags & DBO_DEFAULT ) { |
||
245 | $this->mConn = oci_new_connect( |
||
246 | $this->mUser, |
||
247 | $this->mPassword, |
||
248 | $this->mServer, |
||
249 | $this->defaultCharset, |
||
250 | $session_mode |
||
251 | ); |
||
252 | } else { |
||
253 | $this->mConn = oci_connect( |
||
254 | $this->mUser, |
||
255 | $this->mPassword, |
||
256 | $this->mServer, |
||
257 | $this->defaultCharset, |
||
258 | $session_mode |
||
259 | ); |
||
260 | } |
||
261 | MediaWiki\restoreWarnings(); |
||
262 | |||
263 | if ( $this->mUser != $this->mDBname ) { |
||
264 | // change current schema in session |
||
265 | $this->selectDB( $this->mDBname ); |
||
266 | } |
||
267 | |||
268 | if ( !$this->mConn ) { |
||
269 | throw new DBConnectionError( $this, $this->lastError() ); |
||
270 | } |
||
271 | |||
272 | $this->mOpened = true; |
||
273 | |||
274 | # removed putenv calls because they interfere with the system globaly |
||
275 | $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' ); |
||
276 | $this->doQuery( 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=\'DD-MM-YYYY HH24:MI:SS.FF6\'' ); |
||
277 | $this->doQuery( 'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=\'.,\'' ); |
||
278 | |||
279 | return $this->mConn; |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * Closes a database connection, if it is open |
||
284 | * Returns success, true if already closed |
||
285 | * @return bool |
||
286 | */ |
||
287 | protected function closeConnection() { |
||
288 | return oci_close( $this->mConn ); |
||
289 | } |
||
290 | |||
291 | function execFlags() { |
||
292 | return $this->mTrxLevel ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS; |
||
293 | } |
||
294 | |||
295 | protected function doQuery( $sql ) { |
||
296 | wfDebug( "SQL: [$sql]\n" ); |
||
297 | if ( !StringUtils::isUtf8( $sql ) ) { |
||
298 | throw new InvalidArgumentException( "SQL encoding is invalid\n$sql" ); |
||
299 | } |
||
300 | |||
301 | // handle some oracle specifics |
||
302 | // remove AS column/table/subquery namings |
||
303 | if ( !$this->getFlag( DBO_DDLMODE ) ) { |
||
304 | $sql = preg_replace( '/ as /i', ' ', $sql ); |
||
305 | } |
||
306 | |||
307 | // Oracle has issues with UNION clause if the statement includes LOB fields |
||
308 | // So we do a UNION ALL and then filter the results array with array_unique |
||
309 | $union_unique = ( preg_match( '/\/\* UNION_UNIQUE \*\/ /', $sql ) != 0 ); |
||
310 | // EXPLAIN syntax in Oracle is EXPLAIN PLAN FOR and it return nothing |
||
311 | // you have to select data from plan table after explain |
||
312 | $explain_id = MWTimestamp::getLocalInstance()->format( 'dmYHis' ); |
||
313 | |||
314 | $sql = preg_replace( |
||
315 | '/^EXPLAIN /', |
||
316 | 'EXPLAIN PLAN SET STATEMENT_ID = \'' . $explain_id . '\' FOR', |
||
317 | $sql, |
||
318 | 1, |
||
319 | $explain_count |
||
320 | ); |
||
321 | |||
322 | MediaWiki\suppressWarnings(); |
||
323 | |||
324 | $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ); |
||
325 | View Code Duplication | if ( $stmt === false ) { |
|
326 | $e = oci_error( $this->mConn ); |
||
327 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
328 | |||
329 | return false; |
||
330 | } |
||
331 | |||
332 | if ( !oci_execute( $stmt, $this->execFlags() ) ) { |
||
333 | $e = oci_error( $stmt ); |
||
334 | if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) { |
||
335 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
336 | |||
337 | return false; |
||
338 | } |
||
339 | } |
||
340 | |||
341 | MediaWiki\restoreWarnings(); |
||
342 | |||
343 | if ( $explain_count > 0 ) { |
||
344 | return $this->doQuery( 'SELECT id, cardinality "ROWS" FROM plan_table ' . |
||
345 | 'WHERE statement_id = \'' . $explain_id . '\'' ); |
||
346 | } elseif ( oci_statement_type( $stmt ) == 'SELECT' ) { |
||
347 | return new ORAResult( $this, $stmt, $union_unique ); |
||
348 | } else { |
||
349 | $this->mAffectedRows = oci_num_rows( $stmt ); |
||
350 | |||
351 | return true; |
||
352 | } |
||
353 | } |
||
354 | |||
355 | function queryIgnore( $sql, $fname = '' ) { |
||
356 | return $this->query( $sql, $fname, true ); |
||
357 | } |
||
358 | |||
359 | /** |
||
360 | * Frees resources associated with the LOB descriptor |
||
361 | * @param ResultWrapper|ORAResult $res |
||
362 | */ |
||
363 | function freeResult( $res ) { |
||
364 | if ( $res instanceof ResultWrapper ) { |
||
365 | $res = $res->result; |
||
366 | } |
||
367 | |||
368 | $res->free(); |
||
0 ignored issues
–
show
|
|||
369 | } |
||
370 | |||
371 | /** |
||
372 | * @param ResultWrapper|ORAResult $res |
||
373 | * @return mixed |
||
374 | */ |
||
375 | function fetchObject( $res ) { |
||
376 | if ( $res instanceof ResultWrapper ) { |
||
377 | $res = $res->result; |
||
378 | } |
||
379 | |||
380 | return $res->fetchObject(); |
||
0 ignored issues
–
show
It seems like
$res is not always an object, but can also be of type resource|array|null . Maybe add an additional type check?
If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe: function someFunction(A $objectMaybe = null)
{
if ($objectMaybe instanceof A) {
$objectMaybe->doSomething();
}
}
Loading history...
|
|||
381 | } |
||
382 | |||
383 | /** |
||
384 | * @param ResultWrapper|ORAResult $res |
||
385 | * @return mixed |
||
386 | */ |
||
387 | function fetchRow( $res ) { |
||
388 | if ( $res instanceof ResultWrapper ) { |
||
389 | $res = $res->result; |
||
390 | } |
||
391 | |||
392 | return $res->fetchRow(); |
||
0 ignored issues
–
show
It seems like
$res is not always an object, but can also be of type resource|array|null . Maybe add an additional type check?
If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe: function someFunction(A $objectMaybe = null)
{
if ($objectMaybe instanceof A) {
$objectMaybe->doSomething();
}
}
Loading history...
|
|||
393 | } |
||
394 | |||
395 | /** |
||
396 | * @param ResultWrapper|ORAResult $res |
||
397 | * @return int |
||
398 | */ |
||
399 | function numRows( $res ) { |
||
400 | if ( $res instanceof ResultWrapper ) { |
||
401 | $res = $res->result; |
||
402 | } |
||
403 | |||
404 | return $res->numRows(); |
||
0 ignored issues
–
show
It seems like
$res is not always an object, but can also be of type resource|array|null . Maybe add an additional type check?
If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe: function someFunction(A $objectMaybe = null)
{
if ($objectMaybe instanceof A) {
$objectMaybe->doSomething();
}
}
Loading history...
|
|||
405 | } |
||
406 | |||
407 | /** |
||
408 | * @param ResultWrapper|ORAResult $res |
||
409 | * @return int |
||
410 | */ |
||
411 | function numFields( $res ) { |
||
412 | if ( $res instanceof ResultWrapper ) { |
||
413 | $res = $res->result; |
||
414 | } |
||
415 | |||
416 | return $res->numFields(); |
||
0 ignored issues
–
show
It seems like
$res is not always an object, but can also be of type resource|array|null . Maybe add an additional type check?
If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe: function someFunction(A $objectMaybe = null)
{
if ($objectMaybe instanceof A) {
$objectMaybe->doSomething();
}
}
Loading history...
|
|||
417 | } |
||
418 | |||
419 | function fieldName( $stmt, $n ) { |
||
420 | return oci_field_name( $stmt, $n ); |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * This must be called after nextSequenceVal |
||
425 | * @return null|int |
||
426 | */ |
||
427 | function insertId() { |
||
428 | return $this->mInsertId; |
||
429 | } |
||
430 | |||
431 | /** |
||
432 | * @param mixed $res |
||
433 | * @param int $row |
||
434 | */ |
||
435 | function dataSeek( $res, $row ) { |
||
436 | if ( $res instanceof ORAResult ) { |
||
437 | $res->seek( $row ); |
||
438 | } else { |
||
439 | $res->result->seek( $row ); |
||
440 | } |
||
441 | } |
||
442 | |||
443 | View Code Duplication | function lastError() { |
|
444 | if ( $this->mConn === false ) { |
||
445 | $e = oci_error(); |
||
446 | } else { |
||
447 | $e = oci_error( $this->mConn ); |
||
448 | } |
||
449 | |||
450 | return $e['message']; |
||
451 | } |
||
452 | |||
453 | View Code Duplication | function lastErrno() { |
|
454 | if ( $this->mConn === false ) { |
||
455 | $e = oci_error(); |
||
456 | } else { |
||
457 | $e = oci_error( $this->mConn ); |
||
458 | } |
||
459 | |||
460 | return $e['code']; |
||
461 | } |
||
462 | |||
463 | function affectedRows() { |
||
464 | return $this->mAffectedRows; |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * Returns information about an index |
||
469 | * If errors are explicitly ignored, returns NULL on failure |
||
470 | * @param string $table |
||
471 | * @param string $index |
||
472 | * @param string $fname |
||
473 | * @return bool |
||
474 | */ |
||
475 | function indexInfo( $table, $index, $fname = __METHOD__ ) { |
||
476 | return false; |
||
477 | } |
||
478 | |||
479 | function indexUnique( $table, $index, $fname = __METHOD__ ) { |
||
480 | return false; |
||
481 | } |
||
482 | |||
483 | function insert( $table, $a, $fname = __METHOD__, $options = [] ) { |
||
484 | if ( !count( $a ) ) { |
||
485 | return true; |
||
486 | } |
||
487 | |||
488 | if ( !is_array( $options ) ) { |
||
489 | $options = [ $options ]; |
||
490 | } |
||
491 | |||
492 | if ( in_array( 'IGNORE', $options ) ) { |
||
493 | $this->ignoreDupValOnIndex = true; |
||
494 | } |
||
495 | |||
496 | if ( !is_array( reset( $a ) ) ) { |
||
497 | $a = [ $a ]; |
||
498 | } |
||
499 | |||
500 | foreach ( $a as &$row ) { |
||
501 | $this->insertOneRow( $table, $row, $fname ); |
||
502 | } |
||
503 | $retVal = true; |
||
504 | |||
505 | if ( in_array( 'IGNORE', $options ) ) { |
||
506 | $this->ignoreDupValOnIndex = false; |
||
507 | } |
||
508 | |||
509 | return $retVal; |
||
510 | } |
||
511 | |||
512 | private function fieldBindStatement( $table, $col, &$val, $includeCol = false ) { |
||
513 | $col_info = $this->fieldInfoMulti( $table, $col ); |
||
514 | $col_type = $col_info != false ? $col_info->type() : 'CONSTANT'; |
||
0 ignored issues
–
show
The method
type does only exist in ORAField , but not in ORAResult .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
515 | |||
516 | $bind = ''; |
||
517 | if ( is_numeric( $col ) ) { |
||
518 | $bind = $val; |
||
519 | $val = null; |
||
520 | |||
521 | return $bind; |
||
522 | } elseif ( $includeCol ) { |
||
523 | $bind = "$col = "; |
||
524 | } |
||
525 | |||
526 | if ( $val == '' && $val !== 0 && $col_type != 'BLOB' && $col_type != 'CLOB' ) { |
||
527 | $val = null; |
||
528 | } |
||
529 | |||
530 | if ( $val === 'NULL' ) { |
||
531 | $val = null; |
||
532 | } |
||
533 | |||
534 | if ( $val === null ) { |
||
535 | if ( $col_info != false && $col_info->isNullable() == 0 && $col_info->defaultValue() != null ) { |
||
0 ignored issues
–
show
The method
isNullable does only exist in ORAField , but not in ORAResult .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
The method
defaultValue does only exist in ORAField , but not in ORAResult .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
536 | $bind .= 'DEFAULT'; |
||
537 | } else { |
||
538 | $bind .= 'NULL'; |
||
539 | } |
||
540 | } else { |
||
541 | $bind .= ':' . $col; |
||
542 | } |
||
543 | |||
544 | return $bind; |
||
545 | } |
||
546 | |||
547 | /** |
||
548 | * @param string $table |
||
549 | * @param array $row |
||
550 | * @param string $fname |
||
551 | * @return bool |
||
552 | * @throws DBUnexpectedError |
||
553 | */ |
||
554 | private function insertOneRow( $table, $row, $fname ) { |
||
555 | global $wgContLang; |
||
556 | |||
557 | $table = $this->tableName( $table ); |
||
558 | // "INSERT INTO tables (a, b, c)" |
||
559 | $sql = "INSERT INTO " . $table . " (" . implode( ',', array_keys( $row ) ) . ')'; |
||
560 | $sql .= " VALUES ("; |
||
561 | |||
562 | // for each value, append ":key" |
||
563 | $first = true; |
||
564 | foreach ( $row as $col => &$val ) { |
||
565 | if ( !$first ) { |
||
566 | $sql .= ', '; |
||
567 | } else { |
||
568 | $first = false; |
||
569 | } |
||
570 | if ( $this->isQuotedIdentifier( $val ) ) { |
||
571 | $sql .= $this->removeIdentifierQuotes( $val ); |
||
572 | unset( $row[$col] ); |
||
573 | } else { |
||
574 | $sql .= $this->fieldBindStatement( $table, $col, $val ); |
||
575 | } |
||
576 | } |
||
577 | $sql .= ')'; |
||
578 | |||
579 | $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ); |
||
580 | View Code Duplication | if ( $stmt === false ) { |
|
581 | $e = oci_error( $this->mConn ); |
||
582 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
583 | |||
584 | return false; |
||
585 | } |
||
586 | foreach ( $row as $col => &$val ) { |
||
587 | $col_info = $this->fieldInfoMulti( $table, $col ); |
||
588 | $col_type = $col_info != false ? $col_info->type() : 'CONSTANT'; |
||
0 ignored issues
–
show
The method
type does only exist in ORAField , but not in ORAResult .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
589 | |||
590 | if ( $val === null ) { |
||
591 | // do nothing ... null was inserted in statement creation |
||
592 | } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) { |
||
593 | if ( is_object( $val ) ) { |
||
594 | $val = $val->fetch(); |
||
595 | } |
||
596 | |||
597 | // backward compatibility |
||
598 | if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) { |
||
599 | $val = $this->getInfinity(); |
||
600 | } |
||
601 | |||
602 | $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val; |
||
603 | if ( oci_bind_by_name( $stmt, ":$col", $val, -1, SQLT_CHR ) === false ) { |
||
604 | $e = oci_error( $stmt ); |
||
605 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
606 | |||
607 | return false; |
||
608 | } |
||
609 | View Code Duplication | } else { |
|
610 | /** @var OCI_Lob[] $lob */ |
||
611 | $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ); |
||
612 | if ( $lob[$col] === false ) { |
||
613 | $e = oci_error( $stmt ); |
||
614 | throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] ); |
||
615 | } |
||
616 | |||
617 | if ( is_object( $val ) ) { |
||
618 | $val = $val->fetch(); |
||
619 | } |
||
620 | |||
621 | if ( $col_type == 'BLOB' ) { |
||
622 | $lob[$col]->writeTemporary( $val, OCI_TEMP_BLOB ); |
||
623 | oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_BLOB ); |
||
624 | } else { |
||
625 | $lob[$col]->writeTemporary( $val, OCI_TEMP_CLOB ); |
||
626 | oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB ); |
||
627 | } |
||
628 | } |
||
629 | } |
||
630 | |||
631 | MediaWiki\suppressWarnings(); |
||
632 | |||
633 | View Code Duplication | if ( oci_execute( $stmt, $this->execFlags() ) === false ) { |
|
634 | $e = oci_error( $stmt ); |
||
635 | if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) { |
||
636 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
637 | |||
638 | return false; |
||
639 | } else { |
||
640 | $this->mAffectedRows = oci_num_rows( $stmt ); |
||
641 | } |
||
642 | } else { |
||
643 | $this->mAffectedRows = oci_num_rows( $stmt ); |
||
644 | } |
||
645 | |||
646 | MediaWiki\restoreWarnings(); |
||
647 | |||
648 | if ( isset( $lob ) ) { |
||
649 | foreach ( $lob as $lob_v ) { |
||
650 | $lob_v->free(); |
||
651 | } |
||
652 | } |
||
653 | |||
654 | if ( !$this->mTrxLevel ) { |
||
655 | oci_commit( $this->mConn ); |
||
656 | } |
||
657 | |||
658 | return oci_free_statement( $stmt ); |
||
659 | } |
||
660 | |||
661 | function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__, |
||
662 | $insertOptions = [], $selectOptions = [] |
||
663 | ) { |
||
664 | $destTable = $this->tableName( $destTable ); |
||
665 | if ( !is_array( $selectOptions ) ) { |
||
666 | $selectOptions = [ $selectOptions ]; |
||
667 | } |
||
668 | list( $startOpts, $useIndex, $tailOpts, $ignoreIndex ) = |
||
669 | $this->makeSelectOptions( $selectOptions ); |
||
670 | View Code Duplication | if ( is_array( $srcTable ) ) { |
|
671 | $srcTable = implode( ',', array_map( [ &$this, 'tableName' ], $srcTable ) ); |
||
672 | } else { |
||
673 | $srcTable = $this->tableName( $srcTable ); |
||
674 | } |
||
675 | |||
676 | $sequenceData = $this->getSequenceData( $destTable ); |
||
677 | View Code Duplication | if ( $sequenceData !== false && |
|
678 | !isset( $varMap[$sequenceData['column']] ) |
||
679 | ) { |
||
680 | $varMap[$sequenceData['column']] = 'GET_SEQUENCE_VALUE(\'' . $sequenceData['sequence'] . '\')'; |
||
681 | } |
||
682 | |||
683 | // count-alias subselect fields to avoid abigious definition errors |
||
684 | $i = 0; |
||
685 | foreach ( $varMap as &$val ) { |
||
686 | $val = $val . ' field' . ( $i++ ); |
||
687 | } |
||
688 | |||
689 | $sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ')' . |
||
690 | " SELECT $startOpts " . implode( ',', $varMap ) . |
||
691 | " FROM $srcTable $useIndex $ignoreIndex "; |
||
692 | if ( $conds != '*' ) { |
||
693 | $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); |
||
694 | } |
||
695 | $sql .= " $tailOpts"; |
||
696 | |||
697 | if ( in_array( 'IGNORE', $insertOptions ) ) { |
||
698 | $this->ignoreDupValOnIndex = true; |
||
699 | } |
||
700 | |||
701 | $retval = $this->query( $sql, $fname ); |
||
702 | |||
703 | if ( in_array( 'IGNORE', $insertOptions ) ) { |
||
704 | $this->ignoreDupValOnIndex = false; |
||
705 | } |
||
706 | |||
707 | return $retval; |
||
708 | } |
||
709 | |||
710 | public function upsert( $table, array $rows, array $uniqueIndexes, array $set, |
||
711 | $fname = __METHOD__ |
||
712 | ) { |
||
713 | if ( !count( $rows ) ) { |
||
714 | return true; // nothing to do |
||
715 | } |
||
716 | |||
717 | if ( !is_array( reset( $rows ) ) ) { |
||
718 | $rows = [ $rows ]; |
||
719 | } |
||
720 | |||
721 | $sequenceData = $this->getSequenceData( $table ); |
||
722 | View Code Duplication | if ( $sequenceData !== false ) { |
|
723 | // add sequence column to each list of columns, when not set |
||
724 | foreach ( $rows as &$row ) { |
||
725 | if ( !isset( $row[$sequenceData['column']] ) ) { |
||
726 | $row[$sequenceData['column']] = |
||
727 | $this->addIdentifierQuotes( 'GET_SEQUENCE_VALUE(\'' . |
||
728 | $sequenceData['sequence'] . '\')' ); |
||
729 | } |
||
730 | } |
||
731 | } |
||
732 | |||
733 | return parent::upsert( $table, $rows, $uniqueIndexes, $set, $fname ); |
||
734 | } |
||
735 | |||
736 | function tableName( $name, $format = 'quoted' ) { |
||
737 | /* |
||
738 | Replace reserved words with better ones |
||
739 | Using uppercase because that's the only way Oracle can handle |
||
740 | quoted tablenames |
||
741 | */ |
||
742 | switch ( $name ) { |
||
743 | case 'user': |
||
744 | $name = 'MWUSER'; |
||
745 | break; |
||
746 | case 'text': |
||
747 | $name = 'PAGECONTENT'; |
||
748 | break; |
||
749 | } |
||
750 | |||
751 | return strtoupper( parent::tableName( $name, $format ) ); |
||
752 | } |
||
753 | |||
754 | function tableNameInternal( $name ) { |
||
755 | $name = $this->tableName( $name ); |
||
756 | |||
757 | return preg_replace( '/.*\.(.*)/', '$1', $name ); |
||
758 | } |
||
759 | |||
760 | /** |
||
761 | * Return the next in a sequence, save the value for retrieval via insertId() |
||
762 | * |
||
763 | * @param string $seqName |
||
764 | * @return null|int |
||
765 | */ |
||
766 | function nextSequenceValue( $seqName ) { |
||
767 | $res = $this->query( "SELECT $seqName.nextval FROM dual" ); |
||
768 | $row = $this->fetchRow( $res ); |
||
769 | $this->mInsertId = $row[0]; |
||
770 | |||
771 | return $this->mInsertId; |
||
772 | } |
||
773 | |||
774 | /** |
||
775 | * Return sequence_name if table has a sequence |
||
776 | * |
||
777 | * @param string $table |
||
778 | * @return bool |
||
779 | */ |
||
780 | private function getSequenceData( $table ) { |
||
781 | if ( $this->sequenceData == null ) { |
||
782 | $result = $this->doQuery( "SELECT lower(asq.sequence_name), |
||
783 | lower(atc.table_name), |
||
784 | lower(atc.column_name) |
||
785 | FROM all_sequences asq, all_tab_columns atc |
||
786 | WHERE decode( |
||
787 | atc.table_name, |
||
788 | '{$this->mTablePrefix}MWUSER', |
||
789 | '{$this->mTablePrefix}USER', |
||
790 | atc.table_name |
||
791 | ) || '_' || |
||
792 | atc.column_name || '_SEQ' = '{$this->mTablePrefix}' || asq.sequence_name |
||
793 | AND asq.sequence_owner = upper('{$this->mDBname}') |
||
794 | AND atc.owner = upper('{$this->mDBname}')" ); |
||
795 | |||
796 | while ( ( $row = $result->fetchRow() ) !== false ) { |
||
797 | $this->sequenceData[$row[1]] = [ |
||
798 | 'sequence' => $row[0], |
||
799 | 'column' => $row[2] |
||
800 | ]; |
||
801 | } |
||
802 | } |
||
803 | $table = strtolower( $this->removeIdentifierQuotes( $this->tableName( $table ) ) ); |
||
804 | |||
805 | return ( isset( $this->sequenceData[$table] ) ) ? $this->sequenceData[$table] : false; |
||
806 | } |
||
807 | |||
808 | /** |
||
809 | * Returns the size of a text field, or -1 for "unlimited" |
||
810 | * |
||
811 | * @param string $table |
||
812 | * @param string $field |
||
813 | * @return mixed |
||
814 | */ |
||
815 | function textFieldSize( $table, $field ) { |
||
816 | $fieldInfoData = $this->fieldInfo( $table, $field ); |
||
817 | |||
818 | return $fieldInfoData->maxLength(); |
||
819 | } |
||
820 | |||
821 | function limitResult( $sql, $limit, $offset = false ) { |
||
822 | if ( $offset === false ) { |
||
823 | $offset = 0; |
||
824 | } |
||
825 | |||
826 | return "SELECT * FROM ($sql) WHERE rownum >= (1 + $offset) AND rownum < (1 + $limit + $offset)"; |
||
827 | } |
||
828 | |||
829 | function encodeBlob( $b ) { |
||
830 | return new Blob( $b ); |
||
831 | } |
||
832 | |||
833 | function decodeBlob( $b ) { |
||
834 | if ( $b instanceof Blob ) { |
||
835 | $b = $b->fetch(); |
||
836 | } |
||
837 | |||
838 | return $b; |
||
839 | } |
||
840 | |||
841 | function unionQueries( $sqls, $all ) { |
||
842 | $glue = ' UNION ALL '; |
||
843 | |||
844 | return 'SELECT * ' . ( $all ? '' : '/* UNION_UNIQUE */ ' ) . |
||
845 | 'FROM (' . implode( $glue, $sqls ) . ')'; |
||
846 | } |
||
847 | |||
848 | function wasDeadlock() { |
||
849 | return $this->lastErrno() == 'OCI-00060'; |
||
850 | } |
||
851 | |||
852 | function duplicateTableStructure( $oldName, $newName, $temporary = false, |
||
853 | $fname = __METHOD__ |
||
854 | ) { |
||
855 | $temporary = $temporary ? 'TRUE' : 'FALSE'; |
||
856 | |||
857 | $newName = strtoupper( $newName ); |
||
858 | $oldName = strtoupper( $oldName ); |
||
859 | |||
860 | $tabName = substr( $newName, strlen( $this->mTablePrefix ) ); |
||
861 | $oldPrefix = substr( $oldName, 0, strlen( $oldName ) - strlen( $tabName ) ); |
||
862 | $newPrefix = strtoupper( $this->mTablePrefix ); |
||
863 | |||
864 | return $this->doQuery( "BEGIN DUPLICATE_TABLE( '$tabName', " . |
||
865 | "'$oldPrefix', '$newPrefix', $temporary ); END;" ); |
||
866 | } |
||
867 | |||
868 | function listTables( $prefix = null, $fname = __METHOD__ ) { |
||
869 | $listWhere = ''; |
||
870 | if ( !empty( $prefix ) ) { |
||
871 | $listWhere = ' AND table_name LIKE \'' . strtoupper( $prefix ) . '%\''; |
||
872 | } |
||
873 | |||
874 | $owner = strtoupper( $this->mDBname ); |
||
875 | $result = $this->doQuery( "SELECT table_name FROM all_tables " . |
||
876 | "WHERE owner='$owner' AND table_name NOT LIKE '%!_IDX\$_' ESCAPE '!' $listWhere" ); |
||
877 | |||
878 | // dirty code ... i know |
||
879 | $endArray = []; |
||
880 | $endArray[] = strtoupper( $prefix . 'MWUSER' ); |
||
881 | $endArray[] = strtoupper( $prefix . 'PAGE' ); |
||
882 | $endArray[] = strtoupper( $prefix . 'IMAGE' ); |
||
883 | $fixedOrderTabs = $endArray; |
||
884 | while ( ( $row = $result->fetchRow() ) !== false ) { |
||
885 | if ( !in_array( $row['table_name'], $fixedOrderTabs ) ) { |
||
886 | $endArray[] = $row['table_name']; |
||
887 | } |
||
888 | } |
||
889 | |||
890 | return $endArray; |
||
891 | } |
||
892 | |||
893 | public function dropTable( $tableName, $fName = __METHOD__ ) { |
||
894 | $tableName = $this->tableName( $tableName ); |
||
895 | if ( !$this->tableExists( $tableName ) ) { |
||
896 | return false; |
||
897 | } |
||
898 | |||
899 | return $this->doQuery( "DROP TABLE $tableName CASCADE CONSTRAINTS PURGE" ); |
||
900 | } |
||
901 | |||
902 | function timestamp( $ts = 0 ) { |
||
903 | return wfTimestamp( TS_ORACLE, $ts ); |
||
904 | } |
||
905 | |||
906 | /** |
||
907 | * Return aggregated value function call |
||
908 | * |
||
909 | * @param array $valuedata |
||
910 | * @param string $valuename |
||
911 | * @return mixed |
||
912 | */ |
||
913 | public function aggregateValue( $valuedata, $valuename = 'value' ) { |
||
914 | return $valuedata; |
||
915 | } |
||
916 | |||
917 | /** |
||
918 | * @return string Wikitext of a link to the server software's web site |
||
919 | */ |
||
920 | public function getSoftwareLink() { |
||
921 | return '[{{int:version-db-oracle-url}} Oracle]'; |
||
922 | } |
||
923 | |||
924 | /** |
||
925 | * @return string Version information from the database |
||
926 | */ |
||
927 | function getServerVersion() { |
||
928 | // better version number, fallback on driver |
||
929 | $rset = $this->doQuery( |
||
930 | 'SELECT version FROM product_component_version ' . |
||
931 | 'WHERE UPPER(product) LIKE \'ORACLE DATABASE%\'' |
||
932 | ); |
||
933 | $row = $rset->fetchRow(); |
||
934 | if ( !$row ) { |
||
935 | return oci_server_version( $this->mConn ); |
||
936 | } |
||
937 | |||
938 | return $row['version']; |
||
939 | } |
||
940 | |||
941 | /** |
||
942 | * Query whether a given index exists |
||
943 | * @param string $table |
||
944 | * @param string $index |
||
945 | * @param string $fname |
||
946 | * @return bool |
||
947 | */ |
||
948 | function indexExists( $table, $index, $fname = __METHOD__ ) { |
||
949 | $table = $this->tableName( $table ); |
||
950 | $table = strtoupper( $this->removeIdentifierQuotes( $table ) ); |
||
951 | $index = strtoupper( $index ); |
||
952 | $owner = strtoupper( $this->mDBname ); |
||
953 | $sql = "SELECT 1 FROM all_indexes WHERE owner='$owner' AND index_name='{$table}_{$index}'"; |
||
954 | $res = $this->doQuery( $sql ); |
||
955 | if ( $res ) { |
||
956 | $count = $res->numRows(); |
||
957 | $res->free(); |
||
958 | } else { |
||
959 | $count = 0; |
||
960 | } |
||
961 | |||
962 | return $count != 0; |
||
963 | } |
||
964 | |||
965 | /** |
||
966 | * Query whether a given table exists (in the given schema, or the default mw one if not given) |
||
967 | * @param string $table |
||
968 | * @param string $fname |
||
969 | * @return bool |
||
970 | */ |
||
971 | function tableExists( $table, $fname = __METHOD__ ) { |
||
972 | $table = $this->tableName( $table ); |
||
973 | $table = $this->addQuotes( strtoupper( $this->removeIdentifierQuotes( $table ) ) ); |
||
974 | $owner = $this->addQuotes( strtoupper( $this->mDBname ) ); |
||
975 | $sql = "SELECT 1 FROM all_tables WHERE owner=$owner AND table_name=$table"; |
||
976 | $res = $this->doQuery( $sql ); |
||
977 | if ( $res && $res->numRows() > 0 ) { |
||
978 | $exists = true; |
||
979 | } else { |
||
980 | $exists = false; |
||
981 | } |
||
982 | |||
983 | $res->free(); |
||
984 | |||
985 | return $exists; |
||
986 | } |
||
987 | |||
988 | /** |
||
989 | * Function translates mysql_fetch_field() functionality on ORACLE. |
||
990 | * Caching is present for reducing query time. |
||
991 | * For internal calls. Use fieldInfo for normal usage. |
||
992 | * Returns false if the field doesn't exist |
||
993 | * |
||
994 | * @param array|string $table |
||
995 | * @param string $field |
||
996 | * @return ORAField|ORAResult |
||
997 | */ |
||
998 | private function fieldInfoMulti( $table, $field ) { |
||
999 | $field = strtoupper( $field ); |
||
1000 | if ( is_array( $table ) ) { |
||
1001 | $table = array_map( [ &$this, 'tableNameInternal' ], $table ); |
||
1002 | $tableWhere = 'IN ('; |
||
1003 | foreach ( $table as &$singleTable ) { |
||
1004 | $singleTable = $this->removeIdentifierQuotes( $singleTable ); |
||
1005 | if ( isset( $this->mFieldInfoCache["$singleTable.$field"] ) ) { |
||
1006 | return $this->mFieldInfoCache["$singleTable.$field"]; |
||
1007 | } |
||
1008 | $tableWhere .= '\'' . $singleTable . '\','; |
||
1009 | } |
||
1010 | $tableWhere = rtrim( $tableWhere, ',' ) . ')'; |
||
1011 | } else { |
||
1012 | $table = $this->removeIdentifierQuotes( $this->tableNameInternal( $table ) ); |
||
1013 | if ( isset( $this->mFieldInfoCache["$table.$field"] ) ) { |
||
1014 | return $this->mFieldInfoCache["$table.$field"]; |
||
1015 | } |
||
1016 | $tableWhere = '= \'' . $table . '\''; |
||
1017 | } |
||
1018 | |||
1019 | $fieldInfoStmt = oci_parse( |
||
1020 | $this->mConn, |
||
1021 | 'SELECT * FROM wiki_field_info_full WHERE table_name ' . |
||
1022 | $tableWhere . ' and column_name = \'' . $field . '\'' |
||
1023 | ); |
||
1024 | View Code Duplication | if ( oci_execute( $fieldInfoStmt, $this->execFlags() ) === false ) { |
|
1025 | $e = oci_error( $fieldInfoStmt ); |
||
1026 | $this->reportQueryError( $e['message'], $e['code'], 'fieldInfo QUERY', __METHOD__ ); |
||
1027 | |||
1028 | return false; |
||
1029 | } |
||
1030 | $res = new ORAResult( $this, $fieldInfoStmt ); |
||
1031 | if ( $res->numRows() == 0 ) { |
||
1032 | if ( is_array( $table ) ) { |
||
1033 | foreach ( $table as &$singleTable ) { |
||
1034 | $this->mFieldInfoCache["$singleTable.$field"] = false; |
||
1035 | } |
||
1036 | } else { |
||
1037 | $this->mFieldInfoCache["$table.$field"] = false; |
||
1038 | } |
||
1039 | $fieldInfoTemp = null; |
||
1040 | } else { |
||
1041 | $fieldInfoTemp = new ORAField( $res->fetchRow() ); |
||
1042 | $table = $fieldInfoTemp->tableName(); |
||
1043 | $this->mFieldInfoCache["$table.$field"] = $fieldInfoTemp; |
||
1044 | } |
||
1045 | $res->free(); |
||
1046 | |||
1047 | return $fieldInfoTemp; |
||
1048 | } |
||
1049 | |||
1050 | /** |
||
1051 | * @throws DBUnexpectedError |
||
1052 | * @param string $table |
||
1053 | * @param string $field |
||
1054 | * @return ORAField |
||
1055 | */ |
||
1056 | function fieldInfo( $table, $field ) { |
||
1057 | if ( is_array( $table ) ) { |
||
1058 | throw new DBUnexpectedError( $this, 'DatabaseOracle::fieldInfo called with table array!' ); |
||
1059 | } |
||
1060 | |||
1061 | return $this->fieldInfoMulti( $table, $field ); |
||
1062 | } |
||
1063 | |||
1064 | protected function doBegin( $fname = __METHOD__ ) { |
||
1065 | $this->mTrxLevel = 1; |
||
1066 | $this->doQuery( 'SET CONSTRAINTS ALL DEFERRED' ); |
||
1067 | } |
||
1068 | |||
1069 | protected function doCommit( $fname = __METHOD__ ) { |
||
1070 | if ( $this->mTrxLevel ) { |
||
1071 | $ret = oci_commit( $this->mConn ); |
||
1072 | if ( !$ret ) { |
||
1073 | throw new DBUnexpectedError( $this, $this->lastError() ); |
||
1074 | } |
||
1075 | $this->mTrxLevel = 0; |
||
1076 | $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' ); |
||
1077 | } |
||
1078 | } |
||
1079 | |||
1080 | protected function doRollback( $fname = __METHOD__ ) { |
||
1081 | if ( $this->mTrxLevel ) { |
||
1082 | oci_rollback( $this->mConn ); |
||
1083 | $this->mTrxLevel = 0; |
||
1084 | $this->doQuery( 'SET CONSTRAINTS ALL IMMEDIATE' ); |
||
1085 | } |
||
1086 | } |
||
1087 | |||
1088 | /** |
||
1089 | * defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}'; |
||
1090 | * |
||
1091 | * @param resource $fp |
||
1092 | * @param bool|string $lineCallback |
||
1093 | * @param bool|callable $resultCallback |
||
1094 | * @param string $fname |
||
1095 | * @param bool|callable $inputCallback |
||
1096 | * @return bool|string |
||
1097 | */ |
||
1098 | function sourceStream( $fp, $lineCallback = false, $resultCallback = false, |
||
1099 | $fname = __METHOD__, $inputCallback = false ) { |
||
1100 | $cmd = ''; |
||
1101 | $done = false; |
||
1102 | $dollarquote = false; |
||
1103 | |||
1104 | $replacements = []; |
||
1105 | |||
1106 | while ( !feof( $fp ) ) { |
||
1107 | if ( $lineCallback ) { |
||
1108 | call_user_func( $lineCallback ); |
||
1109 | } |
||
1110 | $line = trim( fgets( $fp, 1024 ) ); |
||
1111 | $sl = strlen( $line ) - 1; |
||
1112 | |||
1113 | if ( $sl < 0 ) { |
||
1114 | continue; |
||
1115 | } |
||
1116 | if ( '-' == $line[0] && '-' == $line[1] ) { |
||
1117 | continue; |
||
1118 | } |
||
1119 | |||
1120 | // Allow dollar quoting for function declarations |
||
1121 | if ( substr( $line, 0, 8 ) == '/*$mw$*/' ) { |
||
1122 | if ( $dollarquote ) { |
||
1123 | $dollarquote = false; |
||
1124 | $line = str_replace( '/*$mw$*/', '', $line ); // remove dollarquotes |
||
1125 | $done = true; |
||
1126 | } else { |
||
1127 | $dollarquote = true; |
||
1128 | } |
||
1129 | } elseif ( !$dollarquote ) { |
||
1130 | if ( ';' == $line[$sl] && ( $sl < 2 || ';' != $line[$sl - 1] ) ) { |
||
1131 | $done = true; |
||
1132 | $line = substr( $line, 0, $sl ); |
||
1133 | } |
||
1134 | } |
||
1135 | |||
1136 | if ( $cmd != '' ) { |
||
1137 | $cmd .= ' '; |
||
1138 | } |
||
1139 | $cmd .= "$line\n"; |
||
1140 | |||
1141 | if ( $done ) { |
||
1142 | $cmd = str_replace( ';;', ";", $cmd ); |
||
1143 | if ( strtolower( substr( $cmd, 0, 6 ) ) == 'define' ) { |
||
1144 | if ( preg_match( '/^define\s*([^\s=]*)\s*=\s*\'\{\$([^\}]*)\}\'/', $cmd, $defines ) ) { |
||
1145 | $replacements[$defines[2]] = $defines[1]; |
||
1146 | } |
||
1147 | } else { |
||
1148 | foreach ( $replacements as $mwVar => $scVar ) { |
||
1149 | $cmd = str_replace( '&' . $scVar . '.', '`{$' . $mwVar . '}`', $cmd ); |
||
1150 | } |
||
1151 | |||
1152 | $cmd = $this->replaceVars( $cmd ); |
||
1153 | if ( $inputCallback ) { |
||
1154 | call_user_func( $inputCallback, $cmd ); |
||
1155 | } |
||
1156 | $res = $this->doQuery( $cmd ); |
||
1157 | if ( $resultCallback ) { |
||
1158 | call_user_func( $resultCallback, $res, $this ); |
||
1159 | } |
||
1160 | |||
1161 | if ( false === $res ) { |
||
1162 | $err = $this->lastError(); |
||
1163 | |||
1164 | return "Query \"{$cmd}\" failed with error code \"$err\".\n"; |
||
1165 | } |
||
1166 | } |
||
1167 | |||
1168 | $cmd = ''; |
||
1169 | $done = false; |
||
1170 | } |
||
1171 | } |
||
1172 | |||
1173 | return true; |
||
1174 | } |
||
1175 | |||
1176 | function selectDB( $db ) { |
||
1177 | $this->mDBname = $db; |
||
1178 | if ( $db == null || $db == $this->mUser ) { |
||
1179 | return true; |
||
1180 | } |
||
1181 | $sql = 'ALTER SESSION SET CURRENT_SCHEMA=' . strtoupper( $db ); |
||
1182 | $stmt = oci_parse( $this->mConn, $sql ); |
||
1183 | MediaWiki\suppressWarnings(); |
||
1184 | $success = oci_execute( $stmt ); |
||
1185 | MediaWiki\restoreWarnings(); |
||
1186 | if ( !$success ) { |
||
1187 | $e = oci_error( $stmt ); |
||
1188 | if ( $e['code'] != '1435' ) { |
||
1189 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
1190 | } |
||
1191 | |||
1192 | return false; |
||
1193 | } |
||
1194 | |||
1195 | return true; |
||
1196 | } |
||
1197 | |||
1198 | function strencode( $s ) { |
||
1199 | return str_replace( "'", "''", $s ); |
||
1200 | } |
||
1201 | |||
1202 | function addQuotes( $s ) { |
||
1203 | global $wgContLang; |
||
1204 | if ( isset( $wgContLang->mLoaded ) && $wgContLang->mLoaded ) { |
||
1205 | $s = $wgContLang->checkTitleEncoding( $s ); |
||
1206 | } |
||
1207 | |||
1208 | return "'" . $this->strencode( $s ) . "'"; |
||
1209 | } |
||
1210 | |||
1211 | public function addIdentifierQuotes( $s ) { |
||
1212 | if ( !$this->getFlag( DBO_DDLMODE ) ) { |
||
1213 | $s = '/*Q*/' . $s; |
||
1214 | } |
||
1215 | |||
1216 | return $s; |
||
1217 | } |
||
1218 | |||
1219 | public function removeIdentifierQuotes( $s ) { |
||
1220 | return strpos( $s, '/*Q*/' ) === false ? $s : substr( $s, 5 ); |
||
1221 | } |
||
1222 | |||
1223 | public function isQuotedIdentifier( $s ) { |
||
1224 | return strpos( $s, '/*Q*/' ) !== false; |
||
1225 | } |
||
1226 | |||
1227 | private function wrapFieldForWhere( $table, &$col, &$val ) { |
||
1228 | global $wgContLang; |
||
1229 | |||
1230 | $col_info = $this->fieldInfoMulti( $table, $col ); |
||
1231 | $col_type = $col_info != false ? $col_info->type() : 'CONSTANT'; |
||
0 ignored issues
–
show
The method
type does only exist in ORAField , but not in ORAResult .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
1232 | if ( $col_type == 'CLOB' ) { |
||
1233 | $col = 'TO_CHAR(' . $col . ')'; |
||
1234 | $val = $wgContLang->checkTitleEncoding( $val ); |
||
1235 | } elseif ( $col_type == 'VARCHAR2' ) { |
||
1236 | $val = $wgContLang->checkTitleEncoding( $val ); |
||
1237 | } |
||
1238 | } |
||
1239 | |||
1240 | private function wrapConditionsForWhere( $table, $conds, $parentCol = null ) { |
||
1241 | $conds2 = []; |
||
1242 | foreach ( $conds as $col => $val ) { |
||
1243 | if ( is_array( $val ) ) { |
||
1244 | $conds2[$col] = $this->wrapConditionsForWhere( $table, $val, $col ); |
||
1245 | } else { |
||
1246 | if ( is_numeric( $col ) && $parentCol != null ) { |
||
1247 | $this->wrapFieldForWhere( $table, $parentCol, $val ); |
||
1248 | } else { |
||
1249 | $this->wrapFieldForWhere( $table, $col, $val ); |
||
1250 | } |
||
1251 | $conds2[$col] = $val; |
||
1252 | } |
||
1253 | } |
||
1254 | |||
1255 | return $conds2; |
||
1256 | } |
||
1257 | |||
1258 | function selectRow( $table, $vars, $conds, $fname = __METHOD__, |
||
1259 | $options = [], $join_conds = [] |
||
1260 | ) { |
||
1261 | if ( is_array( $conds ) ) { |
||
1262 | $conds = $this->wrapConditionsForWhere( $table, $conds ); |
||
1263 | } |
||
1264 | |||
1265 | return parent::selectRow( $table, $vars, $conds, $fname, $options, $join_conds ); |
||
1266 | } |
||
1267 | |||
1268 | /** |
||
1269 | * Returns an optional USE INDEX clause to go after the table, and a |
||
1270 | * string to go at the end of the query |
||
1271 | * |
||
1272 | * @param array $options An associative array of options to be turned into |
||
1273 | * an SQL query, valid keys are listed in the function. |
||
1274 | * @return array |
||
1275 | */ |
||
1276 | function makeSelectOptions( $options ) { |
||
1277 | $preLimitTail = $postLimitTail = ''; |
||
1278 | $startOpts = ''; |
||
1279 | |||
1280 | $noKeyOptions = []; |
||
1281 | foreach ( $options as $key => $option ) { |
||
1282 | if ( is_numeric( $key ) ) { |
||
1283 | $noKeyOptions[$option] = true; |
||
1284 | } |
||
1285 | } |
||
1286 | |||
1287 | $preLimitTail .= $this->makeGroupByWithHaving( $options ); |
||
1288 | |||
1289 | $preLimitTail .= $this->makeOrderBy( $options ); |
||
1290 | |||
1291 | if ( isset( $noKeyOptions['FOR UPDATE'] ) ) { |
||
1292 | $postLimitTail .= ' FOR UPDATE'; |
||
1293 | } |
||
1294 | |||
1295 | if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) { |
||
1296 | $startOpts .= 'DISTINCT'; |
||
1297 | } |
||
1298 | |||
1299 | View Code Duplication | if ( isset( $options['USE INDEX'] ) && !is_array( $options['USE INDEX'] ) ) { |
|
1300 | $useIndex = $this->useIndexClause( $options['USE INDEX'] ); |
||
1301 | } else { |
||
1302 | $useIndex = ''; |
||
1303 | } |
||
1304 | |||
1305 | View Code Duplication | if ( isset( $options['IGNORE INDEX'] ) && !is_array( $options['IGNORE INDEX'] ) ) { |
|
1306 | $ignoreIndex = $this->ignoreIndexClause( $options['IGNORE INDEX'] ); |
||
1307 | } else { |
||
1308 | $ignoreIndex = ''; |
||
1309 | } |
||
1310 | |||
1311 | return [ $startOpts, $useIndex, $preLimitTail, $postLimitTail, $ignoreIndex ]; |
||
1312 | } |
||
1313 | |||
1314 | public function delete( $table, $conds, $fname = __METHOD__ ) { |
||
1315 | if ( is_array( $conds ) ) { |
||
1316 | $conds = $this->wrapConditionsForWhere( $table, $conds ); |
||
1317 | } |
||
1318 | // a hack for deleting pages, users and images (which have non-nullable FKs) |
||
1319 | // all deletions on these tables have transactions so final failure rollbacks these updates |
||
1320 | $table = $this->tableName( $table ); |
||
1321 | if ( $table == $this->tableName( 'user' ) ) { |
||
1322 | $this->update( 'archive', [ 'ar_user' => 0 ], |
||
1323 | [ 'ar_user' => $conds['user_id'] ], $fname ); |
||
1324 | $this->update( 'ipblocks', [ 'ipb_user' => 0 ], |
||
1325 | [ 'ipb_user' => $conds['user_id'] ], $fname ); |
||
1326 | $this->update( 'image', [ 'img_user' => 0 ], |
||
1327 | [ 'img_user' => $conds['user_id'] ], $fname ); |
||
1328 | $this->update( 'oldimage', [ 'oi_user' => 0 ], |
||
1329 | [ 'oi_user' => $conds['user_id'] ], $fname ); |
||
1330 | $this->update( 'filearchive', [ 'fa_deleted_user' => 0 ], |
||
1331 | [ 'fa_deleted_user' => $conds['user_id'] ], $fname ); |
||
1332 | $this->update( 'filearchive', [ 'fa_user' => 0 ], |
||
1333 | [ 'fa_user' => $conds['user_id'] ], $fname ); |
||
1334 | $this->update( 'uploadstash', [ 'us_user' => 0 ], |
||
1335 | [ 'us_user' => $conds['user_id'] ], $fname ); |
||
1336 | $this->update( 'recentchanges', [ 'rc_user' => 0 ], |
||
1337 | [ 'rc_user' => $conds['user_id'] ], $fname ); |
||
1338 | $this->update( 'logging', [ 'log_user' => 0 ], |
||
1339 | [ 'log_user' => $conds['user_id'] ], $fname ); |
||
1340 | } elseif ( $table == $this->tableName( 'image' ) ) { |
||
1341 | $this->update( 'oldimage', [ 'oi_name' => 0 ], |
||
1342 | [ 'oi_name' => $conds['img_name'] ], $fname ); |
||
1343 | } |
||
1344 | |||
1345 | return parent::delete( $table, $conds, $fname ); |
||
1346 | } |
||
1347 | |||
1348 | /** |
||
1349 | * @param string $table |
||
1350 | * @param array $values |
||
1351 | * @param array $conds |
||
1352 | * @param string $fname |
||
1353 | * @param array $options |
||
1354 | * @return bool |
||
1355 | * @throws DBUnexpectedError |
||
1356 | */ |
||
1357 | function update( $table, $values, $conds, $fname = __METHOD__, $options = [] ) { |
||
1358 | global $wgContLang; |
||
1359 | |||
1360 | $table = $this->tableName( $table ); |
||
1361 | $opts = $this->makeUpdateOptions( $options ); |
||
1362 | $sql = "UPDATE $opts $table SET "; |
||
1363 | |||
1364 | $first = true; |
||
1365 | foreach ( $values as $col => &$val ) { |
||
1366 | $sqlSet = $this->fieldBindStatement( $table, $col, $val, true ); |
||
1367 | |||
1368 | if ( !$first ) { |
||
1369 | $sqlSet = ', ' . $sqlSet; |
||
1370 | } else { |
||
1371 | $first = false; |
||
1372 | } |
||
1373 | $sql .= $sqlSet; |
||
1374 | } |
||
1375 | |||
1376 | if ( $conds !== [] && $conds !== '*' ) { |
||
1377 | $conds = $this->wrapConditionsForWhere( $table, $conds ); |
||
1378 | $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND ); |
||
1379 | } |
||
1380 | |||
1381 | $this->mLastResult = $stmt = oci_parse( $this->mConn, $sql ); |
||
1382 | View Code Duplication | if ( $stmt === false ) { |
|
1383 | $e = oci_error( $this->mConn ); |
||
1384 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
1385 | |||
1386 | return false; |
||
1387 | } |
||
1388 | foreach ( $values as $col => &$val ) { |
||
1389 | $col_info = $this->fieldInfoMulti( $table, $col ); |
||
1390 | $col_type = $col_info != false ? $col_info->type() : 'CONSTANT'; |
||
0 ignored issues
–
show
The method
type does only exist in ORAField , but not in ORAResult .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
1391 | |||
1392 | if ( $val === null ) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read.
Loading history...
|
|||
1393 | // do nothing ... null was inserted in statement creation |
||
1394 | } elseif ( $col_type != 'BLOB' && $col_type != 'CLOB' ) { |
||
1395 | if ( is_object( $val ) ) { |
||
1396 | $val = $val->getData(); |
||
1397 | } |
||
1398 | |||
1399 | if ( preg_match( '/^timestamp.*/i', $col_type ) == 1 && strtolower( $val ) == 'infinity' ) { |
||
1400 | $val = '31-12-2030 12:00:00.000000'; |
||
1401 | } |
||
1402 | |||
1403 | $val = ( $wgContLang != null ) ? $wgContLang->checkTitleEncoding( $val ) : $val; |
||
1404 | if ( oci_bind_by_name( $stmt, ":$col", $val ) === false ) { |
||
1405 | $e = oci_error( $stmt ); |
||
1406 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
1407 | |||
1408 | return false; |
||
1409 | } |
||
1410 | View Code Duplication | } else { |
|
1411 | /** @var OCI_Lob[] $lob */ |
||
1412 | $lob[$col] = oci_new_descriptor( $this->mConn, OCI_D_LOB ); |
||
1413 | if ( $lob[$col] === false ) { |
||
1414 | $e = oci_error( $stmt ); |
||
1415 | throw new DBUnexpectedError( $this, "Cannot create LOB descriptor: " . $e['message'] ); |
||
1416 | } |
||
1417 | |||
1418 | if ( is_object( $val ) ) { |
||
1419 | $val = $val->getData(); |
||
1420 | } |
||
1421 | |||
1422 | if ( $col_type == 'BLOB' ) { |
||
1423 | $lob[$col]->writeTemporary( $val ); |
||
1424 | oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, SQLT_BLOB ); |
||
1425 | } else { |
||
1426 | $lob[$col]->writeTemporary( $val ); |
||
1427 | oci_bind_by_name( $stmt, ":$col", $lob[$col], -1, OCI_B_CLOB ); |
||
1428 | } |
||
1429 | } |
||
1430 | } |
||
1431 | |||
1432 | MediaWiki\suppressWarnings(); |
||
1433 | |||
1434 | View Code Duplication | if ( oci_execute( $stmt, $this->execFlags() ) === false ) { |
|
1435 | $e = oci_error( $stmt ); |
||
1436 | if ( !$this->ignoreDupValOnIndex || $e['code'] != '1' ) { |
||
1437 | $this->reportQueryError( $e['message'], $e['code'], $sql, __METHOD__ ); |
||
1438 | |||
1439 | return false; |
||
1440 | } else { |
||
1441 | $this->mAffectedRows = oci_num_rows( $stmt ); |
||
1442 | } |
||
1443 | } else { |
||
1444 | $this->mAffectedRows = oci_num_rows( $stmt ); |
||
1445 | } |
||
1446 | |||
1447 | MediaWiki\restoreWarnings(); |
||
1448 | |||
1449 | if ( isset( $lob ) ) { |
||
1450 | foreach ( $lob as $lob_v ) { |
||
1451 | $lob_v->free(); |
||
1452 | } |
||
1453 | } |
||
1454 | |||
1455 | if ( !$this->mTrxLevel ) { |
||
1456 | oci_commit( $this->mConn ); |
||
1457 | } |
||
1458 | |||
1459 | return oci_free_statement( $stmt ); |
||
1460 | } |
||
1461 | |||
1462 | function bitNot( $field ) { |
||
1463 | // expecting bit-fields smaller than 4bytes |
||
1464 | return 'BITNOT(' . $field . ')'; |
||
1465 | } |
||
1466 | |||
1467 | function bitAnd( $fieldLeft, $fieldRight ) { |
||
1468 | return 'BITAND(' . $fieldLeft . ', ' . $fieldRight . ')'; |
||
1469 | } |
||
1470 | |||
1471 | function bitOr( $fieldLeft, $fieldRight ) { |
||
1472 | return 'BITOR(' . $fieldLeft . ', ' . $fieldRight . ')'; |
||
1473 | } |
||
1474 | |||
1475 | function getDBname() { |
||
1476 | return $this->mDBname; |
||
1477 | } |
||
1478 | |||
1479 | function getServer() { |
||
1480 | return $this->mServer; |
||
1481 | } |
||
1482 | |||
1483 | View Code Duplication | public function buildGroupConcatField( |
|
1484 | $delim, $table, $field, $conds = '', $join_conds = [] |
||
1485 | ) { |
||
1486 | $fld = "LISTAGG($field," . $this->addQuotes( $delim ) . ") WITHIN GROUP (ORDER BY $field)"; |
||
1487 | |||
1488 | return '(' . $this->selectSQLText( $table, $fld, $conds, null, [], $join_conds ) . ')'; |
||
1489 | } |
||
1490 | |||
1491 | /** |
||
1492 | * @param string $field Field or column to cast |
||
1493 | * @return string |
||
1494 | * @since 1.28 |
||
1495 | */ |
||
1496 | public function buildStringCast( $field ) { |
||
1497 | return 'CAST ( ' . $field . ' AS VARCHAR2 )'; |
||
1498 | } |
||
1499 | |||
1500 | public function getInfinity() { |
||
1501 | return '31-12-2030 12:00:00.000000'; |
||
1502 | } |
||
1503 | } |
||
1504 |
If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe: