Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2.php |
— | — | @@ -3,6 +3,8 @@ |
4 | 4 | * New SQL implementation of SMW's storage abstraction layer. |
5 | 5 | * |
6 | 6 | * @author Markus Krötzsch |
| 7 | + * @author Jeroen De Dauw |
| 8 | + * |
7 | 9 | * @file |
8 | 10 | * @ingroup SMWStore |
9 | 11 | */ |
— | — | @@ -190,7 +192,10 @@ |
191 | 193 | |
192 | 194 | public function getSemanticData( $subject, $filter = false ) { |
193 | 195 | wfProfileIn( "SMWSQLStore2::getSemanticData (SMW)" ); |
194 | | - self::$in_getSemanticData++; // do not clear the cache when called recursively |
| 196 | + |
| 197 | + // Do not clear the cache when called recursively. |
| 198 | + self::$in_getSemanticData++; |
| 199 | + |
195 | 200 | // *** Find out if this subject exists ***// |
196 | 201 | if ( $subject instanceof Title ) { ///TODO: can this still occur? |
197 | 202 | $sid = $this->getSMWPageID( $subject->getDBkey(), $subject->getNamespace(), $subject->getInterwiki() ); |
— | — | @@ -203,17 +208,20 @@ |
204 | 209 | } else { |
205 | 210 | $sid = 0; |
206 | 211 | } |
| 212 | + |
207 | 213 | if ( $sid == 0 ) { // no data, safe our time |
208 | 214 | /// NOTE: we consider redirects for getting $sid, so $sid == 0 also means "no redirects" |
209 | 215 | self::$in_getSemanticData--; |
210 | 216 | wfProfileOut( "SMWSQLStore2::getSemanticData (SMW)" ); |
211 | 217 | return isset( $svalue ) ? ( new SMWSemanticData( $svalue ) ):null; |
212 | 218 | } |
| 219 | + |
213 | 220 | // *** Prepare the cache ***// |
214 | 221 | if ( !array_key_exists( $sid, $this->m_semdata ) ) { // new cache entry |
215 | 222 | $this->m_semdata[$sid] = new SMWSemanticData( $svalue, false ); |
216 | 223 | $this->m_sdstate[$sid] = array(); |
217 | 224 | } |
| 225 | + |
218 | 226 | if ( ( count( $this->m_semdata ) > 20 ) && ( self::$in_getSemanticData == 1 ) ) { |
219 | 227 | // prevent memory leak; |
220 | 228 | // It is not so easy to find the sweet spot between cache size and performance gains (both memory and time), |
— | — | @@ -221,25 +229,34 @@ |
222 | 230 | $this->m_semdata = array( $sid => $this->m_semdata[$sid] ); |
223 | 231 | $this->m_sdstate = array( $sid => $this->m_sdstate[$sid] ); |
224 | 232 | } |
| 233 | + |
225 | 234 | // *** Read the data ***// |
226 | 235 | foreach ( self::getPropertyTables() as $tid => $proptable ) { |
227 | 236 | if ( array_key_exists( $tid, $this->m_sdstate[$sid] ) ) continue; |
| 237 | + |
228 | 238 | if ( $filter !== false ) { |
229 | 239 | $relevant = false; |
| 240 | + |
230 | 241 | foreach ( $filter as $typeid ) { |
231 | 242 | $relevant = $relevant || self::tableFitsType( $tid, $typeid ); |
232 | 243 | } |
| 244 | + |
233 | 245 | if ( !$relevant ) continue; |
234 | 246 | } |
| 247 | + |
235 | 248 | $data = $this->fetchSemanticData( $sid, $svalue, $proptable ); |
| 249 | + |
236 | 250 | foreach ( $data as $d ) { |
237 | 251 | $this->m_semdata[$sid]->addPropertyStubValue( reset( $d ), end( $d ) ); |
238 | 252 | } |
| 253 | + |
239 | 254 | $this->m_sdstate[$sid][$tid] = true; |
240 | 255 | } |
241 | 256 | |
242 | 257 | self::$in_getSemanticData--; |
| 258 | + |
243 | 259 | wfProfileOut( "SMWSQLStore2::getSemanticData (SMW)" ); |
| 260 | + |
244 | 261 | return $this->m_semdata[$sid]; |
245 | 262 | } |
246 | 263 | |
— | — | @@ -254,6 +271,7 @@ |
255 | 272 | */ |
256 | 273 | public function getPropertyValues( $subject, SMWPropertyValue $property, $requestoptions = null, $outputformat = '' ) { |
257 | 274 | wfProfileIn( "SMWSQLStore2::getPropertyValues (SMW)" ); |
| 275 | + |
258 | 276 | if ( $property->isInverse() ) { // inverses are working differently |
259 | 277 | $noninverse = clone $property; |
260 | 278 | $noninverse->setInverse( false ); |
— | — | @@ -261,22 +279,27 @@ |
262 | 280 | } elseif ( $subject !== null ) { // subject given, use semantic data cache: |
263 | 281 | $sd = $this->getSemanticData( $subject, array( $property->getPropertyTypeID() ) ); |
264 | 282 | $result = $this->applyRequestOptions( $sd->getPropertyValues( $property ), $requestoptions ); |
| 283 | + |
265 | 284 | if ( $outputformat != '' ) { // reformat cached values |
266 | 285 | $newres = array(); |
| 286 | + |
267 | 287 | foreach ( $result as $dv ) { |
268 | 288 | $ndv = clone $dv; |
269 | 289 | $ndv->setOutputFormat( $outputformat ); |
270 | 290 | $newres[] = $ndv; |
271 | 291 | } |
| 292 | + |
272 | 293 | $result = $newres; |
273 | 294 | } |
274 | 295 | } else { // no subject given, get all values for the given property |
275 | 296 | $pid = $this->getSMWPropertyID( $property ); |
276 | 297 | $tableid = self::findPropertyTableID( $property ); |
| 298 | + |
277 | 299 | if ( ( $pid == 0 ) || ( $tableid == '' ) ) { |
278 | 300 | wfProfileOut( "SMWSQLStore2::getPropertyValues (SMW)" ); |
279 | 301 | return array(); |
280 | 302 | } |
| 303 | + |
281 | 304 | $proptables = self::getPropertyTables(); |
282 | 305 | $data = $this->fetchSemanticData( $pid, $property, $proptables[$tableid], false, $requestoptions ); |
283 | 306 | $result = array(); |
— | — | @@ -384,7 +407,6 @@ |
385 | 408 | } |
386 | 409 | |
387 | 410 | if ( !$issubject ) { // Needed to apply sorting/string matching in query; only with fixed property. |
388 | | - //.// TODO: support multiple value and label indexes. |
389 | 411 | list( $sig, $valueIndex, $labelIndex ) = self::getTypeSignature( $object->getPropertyTypeID() ); |
390 | 412 | $valuecolumn = ( array_key_exists( $valueIndex, $selectvalues ) ) ? $selectvalues[$valueIndex] : ''; |
391 | 413 | $labelcolumn = ( array_key_exists( $labelIndex, $selectvalues ) ) ? $selectvalues[$labelIndex] : ''; |
— | — | @@ -404,6 +426,7 @@ |
405 | 427 | } elseif ( !$proptable->fixedproperty ) { // use joined or predefined property name |
406 | 428 | if ( $proptable->specpropsonly ) { |
407 | 429 | $propertyname = array_search( $row->p_id, self::$special_ids ); |
| 430 | + |
408 | 431 | if ( $propertyname === false ) { // unknown property that uses a special type, maybe by some extension; look it up in the DB |
409 | 432 | // NOTE: this is just an emergency fallback but not a fast solution; extensions may prefer to use non-special datatypes for new properties! |
410 | 433 | $propertyname = $db->selectField( 'smw_ids', 'smw_title', array( 'smw_id' => $row->p_id ), 'SMW::getSemanticData-LatePropertyFetch' ); |
— | — | @@ -1233,8 +1256,10 @@ |
1234 | 1257 | */ |
1235 | 1258 | protected function setupTables( $verbose, $db ) { |
1236 | 1259 | global $wgDBtype; |
| 1260 | + |
1237 | 1261 | extract( $db->tableNames( 'smw_ids', 'smw_spec2', 'smw_conccache', 'smw_conc2' ) ); |
1238 | 1262 | $reportTo = $verbose ? $this:null; // use $this to report back from static SMWSQLHelpers |
| 1263 | + |
1239 | 1264 | // repeatedly used DB field types defined here for convenience |
1240 | 1265 | $dbtypes = array( 't' => SMWSQLHelpers::getStandardDBType( 'title' ), |
1241 | 1266 | 'u' => ( $wgDBtype == 'postgres' ? 'TEXT' : 'VARCHAR(63) binary' ), |
— | — | @@ -1245,7 +1270,7 @@ |
1246 | 1271 | 'p' => SMWSQLHelpers::getStandardDBType( 'id' ), |
1247 | 1272 | 'n' => SMWSQLHelpers::getStandardDBType( 'namespace' ), |
1248 | 1273 | 'w' => SMWSQLHelpers::getStandardDBType( 'iw' ) ); |
1249 | | - |
| 1274 | +//.// |
1250 | 1275 | // DB update: field renaming between SMW 1.3 and SMW 1.4 |
1251 | 1276 | if ( ( $db->tableExists( $smw_spec2 ) ) && ( $db->fieldExists( $smw_spec2, 'sp_id', 'SMWSQLStore2::setup' ) ) ) { |
1252 | 1277 | if ( $wgDBtype == 'postgres' ) { |
— | — | @@ -1268,7 +1293,9 @@ |
1269 | 1294 | SMWSQLHelpers::setupTable( $smw_conccache, |
1270 | 1295 | array( 's_id' => $dbtypes['p'] . ' NOT NULL', |
1271 | 1296 | 'o_id' => $dbtypes['p'] . ' NOT NULL' ), $db, $reportTo ); |
| 1297 | + |
1272 | 1298 | SMWSQLHelpers::setupIndex( $smw_conccache, array( 'o_id' ), $db ); |
| 1299 | + |
1273 | 1300 | // set up concept descriptions |
1274 | 1301 | SMWSQLHelpers::setupTable( $smw_conc2, |
1275 | 1302 | array( 's_id' => $dbtypes['p'] . ' NOT NULL' . |
— | — | @@ -1291,14 +1318,18 @@ |
1292 | 1319 | $fieldarray = array( 's_title' => $dbtypes['t'] . ' NOT NULL', 's_namespace' => $dbtypes['n'] . ' NOT NULL' ); |
1293 | 1320 | $indexes = array( 's_title,s_namespace' ); |
1294 | 1321 | } |
| 1322 | + |
1295 | 1323 | if ( $proptable->fixedproperty == false ) { |
1296 | 1324 | $fieldarray['p_id'] = $dbtypes['p'] . ' NOT NULL'; |
1297 | 1325 | $indexes[] = 'p_id'; |
1298 | 1326 | } |
| 1327 | + |
1299 | 1328 | foreach ( $proptable->objectfields as $fieldname => $typeid ) { |
1300 | 1329 | $fieldarray[$fieldname] = $dbtypes[$typeid]; |
1301 | 1330 | } |
| 1331 | + |
1302 | 1332 | $indexes = array_merge( $indexes, $proptable->indexes ); |
| 1333 | + |
1303 | 1334 | SMWSQLHelpers::setupTable( $db->tableName( $proptable->name ), $fieldarray, $db, $reportTo ); |
1304 | 1335 | SMWSQLHelpers::setupIndex( $db->tableName( $proptable->name ), $indexes, $db ); |
1305 | 1336 | } |
— | — | @@ -1313,12 +1344,16 @@ |
1314 | 1345 | */ |
1315 | 1346 | protected function setupPredefinedProperties( $verbose, $db ) { |
1316 | 1347 | global $wgDBtype; |
| 1348 | + |
1317 | 1349 | $this->reportProgress( "Setting up internal property indices ...\n", $verbose ); |
| 1350 | + |
1318 | 1351 | // Check if we already have this structure |
1319 | 1352 | $borderiw = $db->selectField( 'smw_ids', 'smw_iw', 'smw_id=' . $db->addQuotes( 50 ) ); |
| 1353 | + |
1320 | 1354 | if ( $borderiw != SMW_SQL2_SMWBORDERIW ) { |
1321 | 1355 | $this->reportProgress( " ... allocate space for internal properties\n", $verbose ); |
1322 | 1356 | $this->moveSMWPageID( 50 ); // make sure position 50 is empty |
| 1357 | + |
1323 | 1358 | $db->insert( 'smw_ids', array( |
1324 | 1359 | 'smw_id' => 50, |
1325 | 1360 | 'smw_title' => '', |
— | — | @@ -1329,16 +1364,20 @@ |
1330 | 1365 | ); // put dummy "border element" on index 50 |
1331 | 1366 | |
1332 | 1367 | $this->reportProgress( " ", $verbose ); |
| 1368 | + |
1333 | 1369 | for ( $i = 0; $i < 50; $i++ ) { // make way for built-in ids |
1334 | 1370 | $this->moveSMWPageID( $i ); |
1335 | 1371 | $this->reportProgress( ".", $verbose ); |
1336 | 1372 | } |
| 1373 | + |
1337 | 1374 | $this->reportProgress( "done\n", $verbose ); |
1338 | 1375 | } else { |
1339 | 1376 | $this->reportProgress( " ... space for internal properties already allocated.\n", $verbose ); |
1340 | 1377 | } |
| 1378 | + |
1341 | 1379 | // now write actual properties; do that each time, it is cheap enough and we can update sortkeys by current language |
1342 | 1380 | $this->reportProgress( " ... writing entries for internal properties.\n", $verbose ); |
| 1381 | + |
1343 | 1382 | foreach ( self::$special_ids as $prop => $id ) { |
1344 | 1383 | $p = SMWPropertyValue::makeProperty( $prop ); |
1345 | 1384 | $db->replace( 'smw_ids', array( 'smw_id' ), array( |
— | — | @@ -1350,29 +1389,36 @@ |
1351 | 1390 | ), 'SMW::setup' |
1352 | 1391 | ); |
1353 | 1392 | } |
| 1393 | + |
1354 | 1394 | if ( $wgDBtype == 'postgres' ) { |
1355 | 1395 | $this->reportProgress( " ... updating smw_ids_smw_id_seq sequence accordingly.\n", $verbose ); |
1356 | 1396 | $max = $db->selectField( 'smw_ids', 'max(smw_id)', array(), __METHOD__ ); |
1357 | 1397 | $max += 1; |
1358 | 1398 | $db->query( "ALTER SEQUENCE smw_ids_smw_id_seq RESTART WITH {$max}", __METHOD__ ); |
1359 | 1399 | } |
| 1400 | + |
1360 | 1401 | $this->reportProgress( "Internal properties initialised successfully.\n", $verbose ); |
1361 | 1402 | } |
1362 | 1403 | |
1363 | 1404 | public function drop( $verbose = true ) { |
1364 | 1405 | global $wgDBtype; |
| 1406 | + |
1365 | 1407 | $this->reportProgress( "Deleting all database content and tables generated by SMW ...\n\n", $verbose ); |
1366 | 1408 | $db = wfGetDB( DB_MASTER ); |
1367 | 1409 | $tables = array( 'smw_ids', 'smw_conc2', 'smw_conccache' ); |
| 1410 | + |
1368 | 1411 | foreach ( self::getPropertyTables() as $proptable ) { |
1369 | 1412 | $tables[] = $proptable->name; |
1370 | 1413 | } |
| 1414 | + |
1371 | 1415 | foreach ( $tables as $table ) { |
1372 | 1416 | $name = $db->tableName( $table ); |
1373 | 1417 | $db->query( 'DROP TABLE' . ( $wgDBtype == 'postgres' ? '':' IF EXISTS' ) . $name, 'SMWSQLStore2::drop' ); |
1374 | 1418 | $this->reportProgress( " ... dropped table $name.\n", $verbose ); |
1375 | 1419 | } |
| 1420 | + |
1376 | 1421 | $this->reportProgress( "All data removed successfully.\n", $verbose ); |
| 1422 | + |
1377 | 1423 | return true; |
1378 | 1424 | } |
1379 | 1425 | |
— | — | @@ -1382,16 +1428,20 @@ |
1383 | 1429 | |
1384 | 1430 | // update by MediaWiki page id --> make sure we get all pages |
1385 | 1431 | $tids = array(); |
| 1432 | + |
1386 | 1433 | for ( $i = $index; $i < $index + $count; $i++ ) { // array of ids |
1387 | 1434 | $tids[] = $i; |
1388 | 1435 | } |
| 1436 | + |
1389 | 1437 | $titles = Title::newFromIDs( $tids ); |
| 1438 | + |
1390 | 1439 | foreach ( $titles as $title ) { |
1391 | 1440 | // set $wgTitle, in case semantic data is set based |
1392 | 1441 | // on values not originating from the page (such as |
1393 | 1442 | // via the External Data extension) |
1394 | 1443 | global $wgTitle; |
1395 | 1444 | $wgTitle = $title; |
| 1445 | + |
1396 | 1446 | if ( ( $namespaces == false ) || ( in_array( $title->getNamespace(), $namespaces ) ) ) { |
1397 | 1447 | $updatejobs[] = new SMWUpdateJob( $title ); |
1398 | 1448 | $emptyrange = false; |
— | — | @@ -1402,12 +1452,16 @@ |
1403 | 1453 | $db = wfGetDB( DB_SLAVE ); |
1404 | 1454 | $res = $db->select( 'smw_ids', array( 'smw_id', 'smw_title', 'smw_namespace', 'smw_iw' ), |
1405 | 1455 | "smw_id >= $index AND smw_id < " . $db->addQuotes( $index + $count ), __METHOD__ ); |
| 1456 | + |
1406 | 1457 | foreach ( $res as $row ) { |
1407 | 1458 | $emptyrange = false; // note this even if no jobs were created |
| 1459 | + |
1408 | 1460 | if ( ( $namespaces != false ) && ( !in_array( $row->smw_namespace, $namespaces ) ) ) continue; |
| 1461 | + |
1409 | 1462 | if ( ( $row->smw_iw == '' ) || ( $row->smw_iw == SMW_SQL2_SMWREDIIW ) ) { // objects representing pages in the wiki, even special pages |
1410 | 1463 | // TODO: special treament of redirects needed, since the store will not act on redirects that did not change according to its records |
1411 | 1464 | $title = Title::makeTitle( $row->smw_namespace, $row->smw_title ); |
| 1465 | + |
1412 | 1466 | if ( !$title->exists() ) { |
1413 | 1467 | $updatejobs[] = new SMWUpdateJob( $title ); |
1414 | 1468 | } |
— | — | @@ -1426,15 +1480,19 @@ |
1427 | 1481 | $job->run(); |
1428 | 1482 | } |
1429 | 1483 | } |
| 1484 | + |
1430 | 1485 | $nextpos = $index + $count; |
| 1486 | + |
1431 | 1487 | if ( $emptyrange ) { // nothing found, check if there will be more pages later on |
1432 | 1488 | $next1 = $db->selectField( 'page', 'page_id', "page_id >= $nextpos", __METHOD__, array( 'ORDER BY' => "page_id ASC" ) ); |
1433 | 1489 | $next2 = $db->selectField( 'smw_ids', 'smw_id', "smw_id >= $nextpos", __METHOD__, array( 'ORDER BY' => "smw_id ASC" ) ); |
1434 | 1490 | $nextpos = ( ( $next2 != 0 ) && ( $next2 < $next1 ) ) ? $next2:$next1; |
1435 | 1491 | } |
| 1492 | + |
1436 | 1493 | $max1 = $db->selectField( 'page', 'MAX(page_id)', '', __METHOD__ ); |
1437 | 1494 | $max2 = $db->selectField( 'smw_ids', 'MAX(smw_id)', '', __METHOD__ ); |
1438 | 1495 | $index = $nextpos ? $nextpos: - 1; |
| 1496 | + |
1439 | 1497 | return ( $index > 0 ) ? $index / max( $max1, $max2 ) : 1; |
1440 | 1498 | } |
1441 | 1499 | |
— | — | @@ -1449,10 +1507,14 @@ |
1450 | 1508 | public function refreshConceptCache( $concept ) { |
1451 | 1509 | wfProfileIn( 'SMWSQLStore2::refreshConceptCache (SMW)' ); |
1452 | 1510 | global $smwgIP; |
| 1511 | + |
1453 | 1512 | include_once( "$smwgIP/includes/storage/SMW_SQLStore2_Queries.php" ); |
| 1513 | + |
1454 | 1514 | $qe = new SMWSQLStore2QueryEngine( $this, wfGetDB( DB_MASTER ) ); |
1455 | 1515 | $result = $qe->refreshConceptCache( $concept ); |
| 1516 | + |
1456 | 1517 | wfProfileOut( 'SMWSQLStore2::refreshConceptCache (SMW)' ); |
| 1518 | + |
1457 | 1519 | return $result; |
1458 | 1520 | } |
1459 | 1521 | |
— | — | @@ -1464,10 +1526,14 @@ |
1465 | 1527 | public function deleteConceptCache( $concept ) { |
1466 | 1528 | wfProfileIn( 'SMWSQLStore2::deleteConceptCache (SMW)' ); |
1467 | 1529 | global $smwgIP; |
| 1530 | + |
1468 | 1531 | include_once( "$smwgIP/includes/storage/SMW_SQLStore2_Queries.php" ); |
| 1532 | + |
1469 | 1533 | $qe = new SMWSQLStore2QueryEngine( $this, wfGetDB( DB_MASTER ) ); |
1470 | 1534 | $result = $qe->deleteConceptCache( $concept ); |
| 1535 | + |
1471 | 1536 | wfProfileOut( 'SMWSQLStore2::deleteConceptCache (SMW)' ); |
| 1537 | + |
1472 | 1538 | return $result; |
1473 | 1539 | } |
1474 | 1540 | |
— | — | @@ -1483,13 +1549,17 @@ |
1484 | 1550 | */ |
1485 | 1551 | public function getConceptCacheStatus( $concept ) { |
1486 | 1552 | wfProfileIn( 'SMWSQLStore2::getConceptCacheStatus (SMW)' ); |
| 1553 | + |
1487 | 1554 | $db = wfGetDB( DB_SLAVE ); |
1488 | 1555 | $cid = $this->getSMWPageID( $concept->getDBkey(), $concept->getNamespace(), '', false ); |
| 1556 | + |
1489 | 1557 | $row = $db->selectRow( 'smw_conc2', |
1490 | 1558 | array( 'concept_txt', 'concept_features', 'concept_size', 'concept_depth', 'cache_date', 'cache_count' ), |
1491 | 1559 | array( 's_id' => $cid ), 'SMWSQLStore2::getConceptCacheStatus (SMW)' ); |
| 1560 | + |
1492 | 1561 | if ( $row !== false ) { |
1493 | 1562 | $result = array( 'size' => $row->concept_size, 'depth' => $row->concept_depth, 'features' => $row->concept_features ); |
| 1563 | + |
1494 | 1564 | if ( $row->cache_date ) { |
1495 | 1565 | $result['status'] = 'full'; |
1496 | 1566 | $result['date'] = $row->cache_date; |
— | — | @@ -1500,7 +1570,9 @@ |
1501 | 1571 | } else { |
1502 | 1572 | $result = array( 'status' => 'no' ); |
1503 | 1573 | } |
| 1574 | + |
1504 | 1575 | wfProfileOut( 'SMWSQLStore2::getConceptCacheStatus (SMW)' ); |
| 1576 | + |
1505 | 1577 | return $result; |
1506 | 1578 | } |
1507 | 1579 | |
— | — | @@ -1514,17 +1586,21 @@ |
1515 | 1587 | */ |
1516 | 1588 | protected function getSQLOptions( $requestoptions, $valuecol = '' ) { |
1517 | 1589 | $sql_options = array(); |
| 1590 | + |
1518 | 1591 | if ( $requestoptions !== null ) { |
1519 | 1592 | if ( $requestoptions->limit > 0 ) { |
1520 | 1593 | $sql_options['LIMIT'] = $requestoptions->limit; |
1521 | 1594 | } |
| 1595 | + |
1522 | 1596 | if ( $requestoptions->offset > 0 ) { |
1523 | 1597 | $sql_options['OFFSET'] = $requestoptions->offset; |
1524 | 1598 | } |
| 1599 | + |
1525 | 1600 | if ( ( $valuecol != '' ) && ( $requestoptions->sort ) ) { |
1526 | 1601 | $sql_options['ORDER BY'] = $requestoptions->ascending ? $valuecol : $valuecol . ' DESC'; |
1527 | 1602 | } |
1528 | 1603 | } |
| 1604 | + |
1529 | 1605 | return $sql_options; |
1530 | 1606 | } |
1531 | 1607 | |
— | — | @@ -1679,6 +1755,7 @@ |
1680 | 1756 | if ( ob_get_level() == 0 ) { // be sure to have some buffer, otherwise some PHPs complain |
1681 | 1757 | ob_start(); |
1682 | 1758 | } |
| 1759 | + |
1683 | 1760 | print $msg; |
1684 | 1761 | ob_flush(); |
1685 | 1762 | flush(); |
— | — | @@ -1798,36 +1875,46 @@ |
1799 | 1876 | */ |
1800 | 1877 | public function getSMWPageIDandSort( $title, $namespace, $iw, &$sort, $canonical ) { |
1801 | 1878 | global $smwgQEqualitySupport; |
| 1879 | + |
1802 | 1880 | wfProfileIn( 'SMWSQLStore2::getSMWPageID (SMW)' ); |
| 1881 | + |
1803 | 1882 | $ckey = "$iw $namespace $title C"; |
1804 | 1883 | $nkey = "$iw $namespace $title -"; |
1805 | 1884 | $key = ( $canonical ? $ckey:$nkey ); |
| 1885 | + |
1806 | 1886 | if ( array_key_exists( $key, $this->m_ids ) ) { |
1807 | 1887 | wfProfileOut( 'SMWSQLStore2::getSMWPageID (SMW)' ); |
1808 | 1888 | return $this->m_ids[$key]; |
1809 | 1889 | } |
| 1890 | + |
1810 | 1891 | if ( count( $this->m_ids ) > 1500 ) { // prevent memory leak in very long PHP runs |
1811 | 1892 | $this->m_ids = array(); |
1812 | 1893 | } |
| 1894 | + |
1813 | 1895 | $db = wfGetDB( DB_SLAVE ); |
1814 | 1896 | $id = 0; |
| 1897 | + |
1815 | 1898 | if ( $iw != '' ) { // external page; no need to think about redirects |
1816 | 1899 | $res = $db->select( 'smw_ids', array( 'smw_id', 'smw_sortkey' ), |
1817 | 1900 | array( 'smw_title' => $title, 'smw_namespace' => $namespace, 'smw_iw' => $iw ), |
1818 | 1901 | 'SMW::getSMWPageID', array( 'LIMIT' => 1 ) ); |
| 1902 | + |
1819 | 1903 | if ( $row = $db->fetchObject( $res ) ) { |
1820 | 1904 | $id = $row->smw_id; |
1821 | 1905 | $sort = $row->smw_sortkey; |
1822 | 1906 | } |
| 1907 | + |
1823 | 1908 | $this->m_ids[ $canonical ? $nkey:$ckey ] = $id; // unique id, make sure cache for canonical+non-cacnonical gets filled |
1824 | 1909 | } else { // check for potential redirects also |
1825 | 1910 | $res = $db->select( 'smw_ids', array( 'smw_id', 'smw_iw', 'smw_sortkey' ), |
1826 | 1911 | 'smw_title=' . $db->addQuotes( $title ) . ' AND smw_namespace=' . $db->addQuotes( $namespace ) . |
1827 | 1912 | ' AND (smw_iw=' . $db->addQuotes( '' ) . ' OR smw_iw=' . $db->addQuotes( SMW_SQL2_SMWREDIIW ) . ')', |
1828 | 1913 | 'SMW::getSMWPageID', array( 'LIMIT' => 1 ) ); |
| 1914 | + |
1829 | 1915 | if ( $row = $db->fetchObject( $res ) ) { |
1830 | 1916 | $id = $row->smw_id; // set id in any case, the below check for properties will use even the redirect id in emergency |
1831 | 1917 | $sort = $row->smw_sortkey; |
| 1918 | + |
1832 | 1919 | if ( ( $row->smw_iw == '' ) ) { // the id found is unique (canonical and non-canonical); fill cache also for the case *not* asked for |
1833 | 1920 | $this->m_ids[ $canonical ? $nkey:$ckey ] = $id; // (the other cache is filled below) |
1834 | 1921 | } elseif ( $canonical && ( $smwgQEqualitySupport != SMW_EQ_NONE ) ) { // get redirect alias |
— | — | @@ -1841,17 +1928,21 @@ |
1842 | 1929 | 's_title=' . $db->addQuotes( $title ) . ' AND s_namespace=' . $db->addQuotes( $namespace ), |
1843 | 1930 | 'SMW::getSMWPageID', array( 'LIMIT' => 1 ) ); |
1844 | 1931 | } |
| 1932 | + |
1845 | 1933 | if ( $row = $db->fetchObject( $res2 ) ) { |
1846 | 1934 | $id = $row->o_id; |
1847 | 1935 | } |
| 1936 | + |
1848 | 1937 | $db->freeResult( $res2 ); |
1849 | 1938 | } |
1850 | 1939 | } |
1851 | 1940 | } |
| 1941 | + |
1852 | 1942 | $db->freeResult( $res ); |
1853 | 1943 | |
1854 | 1944 | $this->m_ids[$key] = $id; |
1855 | 1945 | wfProfileOut( 'SMWSQLStore2::getSMWPageID (SMW)' ); |
| 1946 | + |
1856 | 1947 | return $id; |
1857 | 1948 | } |
1858 | 1949 | |
— | — | @@ -1867,17 +1958,21 @@ |
1868 | 1959 | */ |
1869 | 1960 | protected function makeSMWPageID( $title, $namespace, $iw, $canonical = true, $sortkey = '' ) { |
1870 | 1961 | wfProfileIn( 'SMWSQLStore2::makeSMWPageID (SMW)' ); |
| 1962 | + |
1871 | 1963 | $oldsort = ''; |
1872 | 1964 | $id = $this->getSMWPageIDandSort( $title, $namespace, $iw, $oldsort, $canonical ); |
| 1965 | + |
1873 | 1966 | if ( $id == 0 ) { |
1874 | 1967 | $db = wfGetDB( DB_MASTER ); |
1875 | 1968 | $sortkey = $sortkey ? $sortkey:( str_replace( '_', ' ', $title ) ); |
| 1969 | + |
1876 | 1970 | $db->insert( 'smw_ids', |
1877 | 1971 | array( 'smw_id' => $db->nextSequenceValue( 'smw_ids_smw_id_seq' ), |
1878 | 1972 | 'smw_title' => $title, |
1879 | 1973 | 'smw_namespace' => $namespace, |
1880 | 1974 | 'smw_iw' => $iw, |
1881 | 1975 | 'smw_sortkey' => $sortkey ), 'SMW::makeSMWPageID' ); |
| 1976 | + |
1882 | 1977 | $id = $db->insertId(); |
1883 | 1978 | $this->m_ids["$iw $namespace $title -"] = $id; // fill that cache, even if canonical was given |
1884 | 1979 | // This ID is also authorative for the canonical version. |
— | — | @@ -1888,7 +1983,9 @@ |
1889 | 1984 | $db = wfGetDB( DB_MASTER ); |
1890 | 1985 | $db->update( 'smw_ids', array( 'smw_sortkey' => $sortkey ), array( 'smw_id' => $id ), 'SMW::makeSMWPageID' ); |
1891 | 1986 | } |
| 1987 | + |
1892 | 1988 | wfProfileOut( 'SMWSQLStore2::makeSMWPageID (SMW)' ); |
| 1989 | + |
1893 | 1990 | return $id; |
1894 | 1991 | } |
1895 | 1992 | |
— | — | @@ -1940,10 +2037,13 @@ |
1941 | 2038 | public function cacheSMWPageID( $id, $title, $namespace, $iw ) { |
1942 | 2039 | $ckey = "$iw $namespace $title C"; |
1943 | 2040 | $nkey = "$iw $namespace $title -"; |
| 2041 | + |
1944 | 2042 | if ( count( $this->m_ids ) > 1500 ) { // prevent memory leak in very long PHP runs |
1945 | 2043 | $this->m_ids = array(); |
1946 | 2044 | } |
| 2045 | + |
1947 | 2046 | $this->m_ids[$nkey] = $id; |
| 2047 | + |
1948 | 2048 | if ( $iw != SMW_SQL2_SMWREDIIW ) { |
1949 | 2049 | $this->m_ids[$ckey] = $id; |
1950 | 2050 | } |
— | — | @@ -1962,18 +2062,22 @@ |
1963 | 2063 | */ |
1964 | 2064 | protected function makeSMWBnodeID( $sid ) { |
1965 | 2065 | $db = wfGetDB( DB_MASTER ); |
| 2066 | + |
1966 | 2067 | // check if there is an unused bnode to take: |
1967 | 2068 | $res = $db->select( 'smw_ids', 'smw_id', array( 'smw_title' => '', 'smw_namespace' => 0, 'smw_iw' => SMW_SQL2_SMWIW ), |
1968 | 2069 | 'SMW::makeSMWBnodeID', array( 'LIMIT' => 1 ) ); |
| 2070 | + |
1969 | 2071 | $id = ( $row = $db->fetchObject( $res ) ) ? $row->smw_id:0; |
1970 | 2072 | $db->freeResult( $res ); |
| 2073 | + |
1971 | 2074 | // claim that bnode: |
1972 | 2075 | if ( $id != 0 ) { |
1973 | 2076 | $db->update( 'smw_ids', array( 'smw_namespace' => $sid ), |
1974 | 2077 | array( 'smw_id' => $id, |
1975 | 2078 | 'smw_title' => '', |
1976 | 2079 | 'smw_namespace' => 0, |
1977 | | - 'smw_iw' => SMW_SQL2_SMWIW ), 'SMW::makeSMWBnodeID', array( 'LIMIT' => 1 ) ); |
| 2080 | + 'smw_iw' => SMW_SQL2_SMWIW ), 'SMW::makeSMWBnodeID', array( 'LIMIT' => 1 ) ); |
| 2081 | + |
1978 | 2082 | if ( $db->affectedRows() == 0 ) { // Oops, someone was faster (collisions are possible here, no locks) |
1979 | 2083 | $id = 0; // fallback: make a new node (TODO: we could also repeat to try another ID) |
1980 | 2084 | } |
— | — | @@ -1985,8 +2089,10 @@ |
1986 | 2090 | 'smw_title' => '', |
1987 | 2091 | 'smw_namespace' => $sid, |
1988 | 2092 | 'smw_iw' => SMW_SQL2_SMWIW ), 'SMW::makeSMWBnodeID' ); |
| 2093 | + |
1989 | 2094 | $id = $db->insertId(); |
1990 | 2095 | } |
| 2096 | + |
1991 | 2097 | return $id; |
1992 | 2098 | } |
1993 | 2099 | |
— | — | @@ -2005,13 +2111,16 @@ |
2006 | 2112 | $row = $db->selectRow( 'smw_ids', |
2007 | 2113 | array( 'smw_id', 'smw_namespace', 'smw_title', 'smw_iw', 'smw_sortkey' ), |
2008 | 2114 | array( 'smw_id' => $curid ), 'SMWSQLStore2::moveSMWPageID' ); |
| 2115 | + |
2009 | 2116 | if ( $row === false ) return; // no id at current position, ignore |
| 2117 | + |
2010 | 2118 | if ( $targetid == 0 ) { // append new id |
2011 | 2119 | $db->insert( 'smw_ids', array( 'smw_id' => $db->nextSequenceValue( 'smw_ids_smw_id_seq' ), |
2012 | 2120 | 'smw_title' => $row->smw_title, |
2013 | 2121 | 'smw_namespace' => $row->smw_namespace, |
2014 | 2122 | 'smw_iw' => $row->smw_iw, |
2015 | 2123 | 'smw_sortkey' => $row->smw_sortkey ), 'SMW::moveSMWPageID' ); |
| 2124 | + |
2016 | 2125 | $targetid = $db->insertId(); |
2017 | 2126 | } else { // change to given id |
2018 | 2127 | $db->insert( 'smw_ids', array( 'smw_id' => $targetid, |
— | — | @@ -2020,6 +2129,7 @@ |
2021 | 2130 | 'smw_iw' => $row->smw_iw, |
2022 | 2131 | 'smw_sortkey' => $row->smw_sortkey ), 'SMW::moveSMWPageID' ); |
2023 | 2132 | } |
| 2133 | + |
2024 | 2134 | $db->delete( 'smw_ids', array( 'smw_id' => $curid ), 'SMWSQLStore2::moveSMWPageID' ); |
2025 | 2135 | $this->changeSMWPageID( $curid, $targetid, $row->smw_namespace, $row->smw_namespace ); |
2026 | 2136 | } |
— | — | @@ -2044,16 +2154,19 @@ |
2045 | 2155 | protected function changeSMWPageID( $oldid, $newid, $oldnamespace = - 1, $newnamespace = - 1, $sdata = true, $podata = true ) { |
2046 | 2156 | $fname = 'SMW::changeSMWPageID'; |
2047 | 2157 | $db = wfGetDB( DB_MASTER ); |
| 2158 | + |
2048 | 2159 | // Update bnode references that use namespace field to store ids: |
2049 | 2160 | if ( $sdata ) { // bnodes are part of the data of a subject |
2050 | 2161 | $db->update( 'smw_ids', array( 'smw_namespace' => $newid ), |
2051 | 2162 | array( 'smw_title' => '', 'smw_namespace' => $oldid, 'smw_iw' => SMW_SQL2_SMWIW ), $fname ); |
2052 | 2163 | } |
| 2164 | + |
2053 | 2165 | // change all id entries in property tables: |
2054 | 2166 | foreach ( self::getPropertyTables() as $proptable ) { |
2055 | 2167 | if ( $sdata && $proptable->idsubject ) { |
2056 | 2168 | $db->update( $proptable->name, array( 's_id' => $newid ), array( 's_id' => $oldid ), $fname ); |
2057 | 2169 | } |
| 2170 | + |
2058 | 2171 | if ( $podata ) { |
2059 | 2172 | if ( ( ( $oldnamespace == - 1 ) || ( $oldnamespace == SMW_NS_PROPERTY ) ) && ( $proptable->fixedproperty == false ) ) { |
2060 | 2173 | if ( ( $newnamespace == - 1 ) || ( $newnamespace == SMW_NS_PROPERTY ) ) { |
— | — | @@ -2062,6 +2175,7 @@ |
2063 | 2176 | $db->delete( $proptable->name, array( 'p_id' => $oldid ), $fname ); |
2064 | 2177 | } |
2065 | 2178 | } |
| 2179 | + |
2066 | 2180 | foreach ( $proptable->objectfields as $fieldname => $type ) { |
2067 | 2181 | if ( $type == 'p' ) { |
2068 | 2182 | $db->update( $proptable->name, array( $fieldname => $newid ), array( $fieldname => $oldid ), $fname ); |
— | — | @@ -2090,9 +2204,12 @@ |
2091 | 2205 | */ |
2092 | 2206 | protected function deleteSemanticData( SMWWikiPageValue $subject ) { |
2093 | 2207 | $db = wfGetDB( DB_MASTER ); |
| 2208 | + |
2094 | 2209 | $fname = 'SMW::deleteSemanticData'; |
2095 | 2210 | $id = $this->getSMWPageID( $subject->getDBkey(), $subject->getNamespace(), $subject->getInterwiki(), false ); |
| 2211 | + |
2096 | 2212 | if ( $id == 0 ) return; // not (directly) used anywhere yet, maybe a redirect but we do not care here |
| 2213 | + |
2097 | 2214 | foreach ( self::getPropertyTables() as $proptable ) { |
2098 | 2215 | if ( $proptable->idsubject ) { |
2099 | 2216 | $db->delete( $proptable->name, array( 's_id' => $id ), $fname ); |
— | — | @@ -2100,8 +2217,10 @@ |
2101 | 2218 | $db->delete( $proptable->name, array( 's_title' => $subject->getDBkey(), 's_namespace' => $subject->getNamespace() ), $fname ); |
2102 | 2219 | } |
2103 | 2220 | } |
| 2221 | + |
2104 | 2222 | // also find bnodes used by this ID ... |
2105 | 2223 | $res = $db->select( 'smw_ids', 'smw_id', array( 'smw_title' => '', 'smw_namespace' => $id, 'smw_iw' => SMW_SQL2_SMWIW ), $fname ); |
| 2224 | + |
2106 | 2225 | // ... and delete them as well |
2107 | 2226 | while ( $row = $db->fetchObject( $res ) ) { |
2108 | 2227 | foreach ( self::getPropertyTables() as $proptable ) { |
— | — | @@ -2110,9 +2229,12 @@ |
2111 | 2230 | } |
2112 | 2231 | } |
2113 | 2232 | } |
| 2233 | + |
2114 | 2234 | $db->freeResult( $res ); |
| 2235 | + |
2115 | 2236 | // free all affected bnodes in one call: |
2116 | 2237 | $db->update( 'smw_ids', array( 'smw_namespace' => 0 ), array( 'smw_title' => '', 'smw_namespace' => $id, 'smw_iw' => SMW_SQL2_SMWIW ), $fname ); |
| 2238 | + |
2117 | 2239 | wfRunHooks( 'smwDeleteSemanticData', array( $subject ) ); |
2118 | 2240 | } |
2119 | 2241 | |
— | — | @@ -2215,19 +2337,23 @@ |
2216 | 2338 | $db->update( 'smw_ids', array( 'smw_iw' => SMW_SQL2_SMWREDIIW ), array( 'smw_id' => $sid ), $fname ); |
2217 | 2339 | } |
2218 | 2340 | } |
| 2341 | + |
2219 | 2342 | $db->insert( 'smw_redi2', array( 's_title' => $subject_t, 's_namespace' => $subject_ns, 'o_id' => $new_tid ), $fname ); |
2220 | 2343 | $this->m_ids[" $subject_ns $subject_t C"] = $new_tid; // "iw" is empty here |
2221 | 2344 | } else { // delete old redirect |
2222 | 2345 | // This case implies $old_tid != 0 (or we would have new_tid == old_tid above). |
2223 | 2346 | // Therefore $subject had a redirect, and it must also have an ID. This shows that $sid != 0 here. |
2224 | 2347 | $this->m_ids[" $subject_ns $subject_t C"] = $sid; // "iw" is empty here |
| 2348 | + |
2225 | 2349 | if ( $smwgQEqualitySupport != SMW_EQ_NONE ) { // mark subject as non-redirect |
2226 | 2350 | $db->update( 'smw_ids', array( 'smw_iw' => '' ), array( 'smw_id' => $sid ), $fname ); |
2227 | 2351 | } |
2228 | 2352 | } |
| 2353 | + |
2229 | 2354 | // *** Flush some caches to be safe, though they are not essential in program runs with redirect updates ***// |
2230 | 2355 | unset( $this->m_semdata[$sid] ); unset( $this->m_semdata[$new_tid] ); unset( $this->m_semdata[$old_tid] ); |
2231 | 2356 | unset( $this->m_sdstate[$sid] ); unset( $this->m_sdstate[$new_tid] ); unset( $this->m_sdstate[$old_tid] ); |
| 2357 | + |
2232 | 2358 | return ( $new_tid == 0 ) ? $sid : $new_tid; |
2233 | 2359 | } |
2234 | 2360 | |