00001 <?php
00024 class SMWSQLStoreLight extends SMWStore {
00025
00027 protected $m_semdata = array();
00029 protected $m_sdstate = array();
00031 protected static $in_getSemanticData = 0;
00032
00035 private static $special_types = array(
00036 '__typ' => true,
00037 '__tls' => true,
00038 '__sps' => true,
00039 '__spu' => true,
00040 '__spf' => true,
00041 '__imp' => true,
00042 );
00043
00045
00046 public function getSemanticData( $subject, $filter = false ) {
00047 wfProfileIn( "SMWSQLStoreLight::getSemanticData (SMW)" );
00048 SMWSQLStoreLight::$in_getSemanticData++;
00049
00050 if ( $subject instanceof Title ) {
00051 $sid = $subject->getArticleID();
00052 $svalue = SMWWikiPageValue::makePageFromTitle( $subject );
00053 } elseif ( $subject instanceof SMWWikiPageValue ) {
00054 $sid = $subject->isValid() ? $subject->getTitle()->getArticleID() : 0;
00055 $svalue = $subject;
00056 } else {
00057 $sid = 0;
00058 }
00059 if ( $sid == 0 ) {
00060 SMWSQLStoreLight::$in_getSemanticData--;
00061 wfProfileOut( "SMWSQLStoreLight::getSemanticData (SMW)" );
00062 return isset( $svalue ) ? ( new SMWSemanticData( $svalue ) ) : null;
00063 }
00064
00065 if ( !array_key_exists( $sid, $this->m_semdata ) ) {
00066 $this->m_semdata[$sid] = new SMWSemanticData( $svalue, false );
00067 $this->m_sdstate[$sid] = array();
00068 }
00069 if ( ( count( $this->m_semdata ) > 20 ) && ( SMWSQLStoreLight::$in_getSemanticData == 1 ) ) {
00070
00071
00072
00073 $this->m_semdata = array( $sid => $this->m_semdata[$sid] );
00074 $this->m_sdstate = array( $sid => $this->m_sdstate[$sid] );
00075 }
00076
00077 $db = wfGetDB( DB_SLAVE );
00078 foreach ( array( 'smwsimple_data', 'smwsimple_special' ) as $tablename ) {
00079 if ( array_key_exists( $tablename, $this->m_sdstate[$sid] ) ) continue;
00080 if ( $filter !== false ) {
00081 $relevant = false;
00082 foreach ( $filter as $typeid ) {
00083 $relevant = $relevant || ( $tablename == SMWSQLStoreLight::findTypeTableName( $typeid ) );
00084 }
00085 if ( !$relevant ) continue;
00086 }
00087 $res = $db->select( $tablename, array( 'propname', 'value' ), array( 'pageid' => $sid ),
00088 'SMW::getSemanticData', array( 'DISTINCT' ) );
00089 foreach ( $res as $row ) {
00090 $value = ( $tablename == 'smwsimple_special' ) ? array( $row->value ) : unserialize( $row->value );
00091 $this->m_semdata[$sid]->addPropertyStubValue( $row->propname, $value );
00092 }
00093 $db->freeResult( $res );
00094 $this->m_sdstate[$sid][$tablename] = true;
00095 }
00096
00097 SMWSQLStoreLight::$in_getSemanticData--;
00098 wfProfileOut( "SMWSQLStoreLight::getSemanticData (SMW)" );
00099 return $this->m_semdata[$sid];
00100 }
00101
00102 public function getPropertyValues( $subject, SMWDIProperty $property, $requestoptions = null, $outputformat = '' ) {
00103 wfProfileIn( "SMWSQLStoreLight::getPropertyValues (SMW)" );
00104 if ( $property->isInverse() ) {
00105 $noninverse = clone $property;
00106 $noninverse->setInverse( false );
00107 $result = $this->getPropertySubjects( $noninverse, $subject, $requestoptions );
00108 } elseif ( $subject !== null ) {
00109 $sd = $this->getSemanticData( $subject, array( $property->getPropertyTypeID() ) );
00110 $result = $this->applyRequestOptions( $sd->getPropertyValues( $property ), $requestoptions );
00111 if ( $outputformat !== '' ) {
00112 $newres = array();
00113 foreach ( $result as $dv ) {
00114 $ndv = clone $dv;
00115 $ndv->setOutputFormat( $outputformat );
00116 $newres[] = $ndv;
00117 }
00118 $result = $newres;
00119 }
00120 } else {
00121 $tablename = SMWSQLStoreLight::findPropertyTableName( $property );
00122 $db = wfGetDB( DB_SLAVE );
00123 $res = $db->select( $tablename, array( 'value' ), array( 'propname' => $property->getDBkey() ),
00124 'SMW::getPropertyValues', $this->getSQLOptions( $requestoptions, 'value' ) + array( 'DISTINCT' ) );
00125 $result = array();
00126 foreach ( $res as $row ) {
00127 $dv = SMWDataValueFactory::newPropertyObjectValue( $property );
00128 if ( $outputformat !== '' ) $dv->setOutputFormat( $outputformat );
00129 $dv->setDBkeys( ( $tablename == 'smwsimple_special' ) ? array( $row->value ) : unserialize( $row->value ) );
00130 $result[] = $dv;
00131 }
00132 $db->freeResult( $res );
00133 }
00134 wfProfileOut( "SMWSQLStoreLight::getPropertyValues (SMW)" );
00135 return $result;
00136 }
00137
00138 public function getPropertySubjects( SMWDIProperty $property, $value, $requestoptions = null ) {
00139 wfProfileIn( "SMWSQLStoreLight::getPropertySubjects (SMW)" );
00140 if ( $property->isInverse() ) {
00141 $noninverse = clone $property;
00142 $noninverse->setInverse( false );
00143 $result = $this->getPropertyValues( $value, $noninverse, $requestoptions );
00144 wfProfileOut( "SMWSQLStoreLight::getPropertySubjects (SMW)" );
00145 return $result;
00146 }
00147
00148
00149 $tablename = SMWSQLStoreLight::findPropertyTableName( $property );
00150 $db = wfGetDB( DB_SLAVE );
00151 $from = $db->tableName( 'page' ) . " AS p INNER JOIN " . $db->tableName( $tablename ) . " AS t ON t.pageid=p.page_id";
00152 $where = 't.propname=' . $db->addQuotes( $property->getDBkey() );
00153 if ( $value !== null ) {
00154 $valuestring = ( $tablename == 'smwsimple_special' ) ? reset( $value->getDBkeys() ) : serialize( $value->getDBkeys() );
00155 $where .= ' AND t.value=' . $db->addQuotes( $valuestring );
00156 }
00157 $select = array( 'p.page_title AS title', 'p.page_namespace AS namespace' );
00158
00159 $result = array();
00160 $res = $db->select( $from, $select,
00161 $where . $this->getSQLConditions( $requestoptions, 'p.page_title', 'p.page_title' ),
00162 'SMW::getPropertySubjects',
00163 $this->getSQLOptions( $requestoptions, 'p.page_title' ) + array( 'DISTINCT' ) );
00164 foreach ( $res as $row ) {
00165 $result[] = SMWWikiPageValue::makePage( $row->title, $row->namespace, $row->title );
00166 }
00167 $db->freeResult( $res );
00168 wfProfileOut( "SMWSQLStoreLight::getPropertySubjects (SMW)" );
00169 return $result;
00170 }
00171
00172 public function getAllPropertySubjects( SMWDIProperty $property, $requestoptions = null ) {
00173 wfProfileIn( "SMWSQLStoreLight::getAllPropertySubjects (SMW)" );
00174 $result = $this->getPropertySubjects( $property, null, $requestoptions );
00175 wfProfileOut( "SMWSQLStoreLight::getAllPropertySubjects (SMW)" );
00176 return $result;
00177 }
00178
00182 public function getProperties( $subject, $requestoptions = null ) {
00183 wfProfileIn( "SMWSQLStoreLight::getProperties (SMW)" );
00184 $sid = $subject->getTitle()->getArticleID();
00185 if ( $sid == 0 ) {
00186 wfProfileOut( "SMWSQLStoreLight::getProperties (SMW)" );
00187 return array();
00188 }
00189
00190 $db = wfGetDB( DB_SLAVE );
00191 $result = array();
00192 if ( $requestoptions !== null ) {
00193 $suboptions = clone $requestoptions;
00194 $suboptions->limit = $requestoptions->limit + $requestoptions->offset;
00195 $suboptions->offset = 0;
00196 } else {
00197 $suboptions = null;
00198 }
00199 foreach ( array( 'smwsimple_data', 'smwsimple_special' ) as $tablename ) {
00200 $res = $db->select( $tablename, 'DISTINCT propname',
00201 'pageid=' . $db->addQuotes($sid) . $this->getSQLConditions( $suboptions, 'propname', 'propname' ),
00202 'SMW::getProperties', $this->getSQLOptions( $suboptions, 'propname' ) );
00203 foreach ( $res as $row ) {
00204 $result[] = new SMWDIProperty( $row->propname );
00205 }
00206 $db->freeResult( $res );
00207 }
00208 $result = $this->applyRequestOptions( $result, $requestoptions );
00209 wfProfileOut( "SMWSQLStoreLight::getProperties (SMW)" );
00210 return $result;
00211 }
00212
00223 public function getInProperties( SMWDataValue $value, $requestoptions = null ) {
00224 wfProfileIn( "SMWSQLStoreLight::getInProperties (SMW)" );
00225 $db = wfGetDB( DB_SLAVE );
00226 $result = array();
00227 $typeid = $value->getTypeID();
00228
00229 if ( $requestoptions !== null ) {
00230 $suboptions = clone $requestoptions;
00231 $suboptions->limit = $requestoptions->limit + $requestoptions->offset;
00232 $suboptions->offset = 0;
00233 } else {
00234 $suboptions = null;
00235 }
00236 foreach ( array( 'smwsimple_data', 'smwsimple_special' ) as $tablename ) {
00237 if ( SMWSQLStoreLight::findTypeTableName( $typeid ) != $tablename ) continue;
00238 $valuestring = ( $tablename == 'smwsimple_special' ) ? reset( $value->getDBkeys() ) : serialize( $value->getDBkeys() );
00239 $where = 'value=' . $db->addQuotes( $valuestring );
00240 $res = $db->select( $tablename, 'DISTINCT propname',
00241 $where . $this->getSQLConditions( $suboptions, 'propname', 'propname' ),
00242 'SMW::getInProperties', $this->getSQLOptions( $suboptions, 'propname' ) );
00243 foreach ( $res as $row ) {
00244 $result[] = new SMWDIProperty( $row->propname );
00245 }
00246 $db->freeResult( $res );
00247 }
00248 $result = $this->applyRequestOptions( $result, $requestoptions );
00249 wfProfileOut( "SMWSQLStoreLight::getInProperties (SMW)" );
00250 return $result;
00251 }
00252
00254
00255 public function deleteSubject( Title $subject ) {
00256 wfProfileIn( 'SMWSQLStoreLight::deleteSubject (SMW)' );
00257 wfRunHooks( 'SMWSQLStoreLight::deleteSubjectBefore', array( $this, $subject ) );
00258 $this->deleteSemanticData( SMWWikiPageValue::makePageFromTitle( $subject ) );
00262 wfRunHooks( 'SMWSQLStoreLight::deleteSubjectAfter', array( $this, $subject ) );
00263 wfProfileOut( 'SMWSQLStoreLight::deleteSubject (SMW)' );
00264 }
00265
00266 public function doDataUpdate( SMWSemanticData $data ) {
00267 wfProfileIn( "SMWSQLStoreLight::updateData (SMW)" );
00268 wfRunHooks( 'SMWSQLStoreLight::updateDataBefore', array( $this, $data ) );
00269 $subject = $data->getSubject();
00270 $this->deleteSemanticData( $subject );
00271 $sid = $subject->getTitle()->getArticleID();
00272 $updates = array();
00273 foreach ( $data->getProperties() as $property ) {
00274 $tablename = SMWSQLStoreLight::findPropertyTableName( $property );
00275 if ( $tablename === '' ) continue;
00276 foreach ( $data->getPropertyValues( $property ) as $dv ) {
00277 if ( !$dv->isValid() ) continue;
00278 if ( $dv instanceof SMWContainerValue ) {
00279 continue;
00280 } else {
00281 $uvals = array( 'pageid' => $sid, 'propname' => $property->getDBkey(),
00282 'value' => ( $tablename == 'smwsimple_special' ? reset($dv->getDBkeys()) : serialize($dv->getDBkeys()) ) );
00283 }
00284 if ( !array_key_exists( $tablename, $updates ) ) $updates[$tablename] = array();
00285 $updates[$tablename][] = $uvals;
00286 }
00287 }
00288 $db = wfGetDB( DB_MASTER );
00289 foreach ( $updates as $tablename => $uvals ) {
00290 $db->insert( $tablename, $uvals, "SMW::updateData$tablename" );
00291 }
00292
00293
00294 $this->m_semdata[$sid] = clone $data;
00295 $this->m_sdstate[$sid] = array( 'smwsimple_data' => true , 'smwsimple_special' => true );
00296
00297 wfRunHooks( 'SMWSQLStoreLight::updateDataAfter', array( $this, $data ) );
00298 wfProfileOut( "SMWSQLStoreLight::updateData (SMW)" );
00299 }
00300
00301 public function changeTitle( Title $oldtitle, Title $newtitle, $pageid, $redirid = 0 ) {
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312 }
00313
00315
00316 function getQueryResult( SMWQuery $query ) {
00317 return null;
00318 }
00319
00321
00322 public function getPropertiesSpecial( $requestoptions = null ) {
00323 return array();
00324 }
00325
00326 public function getUnusedPropertiesSpecial( $requestoptions = null ) {
00327 return array();
00328 }
00329
00330 public function getWantedPropertiesSpecial( $requestoptions = null ) {
00331 return array();
00332 }
00333
00334 public function getStatistics() {
00335 return array('PROPUSES' => 0, 'USEDPROPS' => 0, 'DECLPROPS' => 0 );
00336 }
00337
00339
00340 public function setup( $verbose = true ) {
00341 $this->reportProgress( "Setting up standard database configuration for SMW ...\n\n", $verbose );
00342 $this->reportProgress( "Selected storage engine is \"SMWSQLStoreLight\" (or an extension thereof)\n\n", $verbose );
00343 $db = wfGetDB( DB_MASTER );
00344 $this->setupTables( $verbose, $db );
00345 return true;
00346 }
00347
00352 protected function setupTables( $verbose, $db ) {
00353 $reportTo = $verbose ? $this : null;
00354
00355 SMWSQLHelpers::setupTable(
00356 'smwsimple_data',
00357 array(
00358 'pageid' => SMWSQLHelpers::getStandardDBType( 'id' ) . ' NOT NULL',
00359 'propname' => SMWSQLHelpers::getStandardDBType( 'title' ) . ' NOT NULL',
00360 'value' => SMWSQLHelpers::getStandardDBType( 'blob' ) . ' NOT NULL'
00361 ),
00362 $db,
00363 $reportTo
00364 );
00365 SMWSQLHelpers::setupIndex( 'smwsimple_data', array( 'pageid', 'propname', 'propname,value(256)' ), $db );
00366 SMWSQLHelpers::setupTable(
00367 'smwsimple_special',
00368 array(
00369 'pageid' => SMWSQLHelpers::getStandardDBType( 'id' ) . ' NOT NULL',
00370 'propname' => SMWSQLHelpers::getStandardDBType( 'title' ) . ' NOT NULL',
00371 'value' => SMWSQLHelpers::getStandardDBType( 'title' ) . ' NOT NULL'
00372 ),
00373 $db,
00374 $reportTo
00375 );
00376 SMWSQLHelpers::setupIndex( 'smwsimple_special', array( 'pageid', 'pageid,propname', 'propname', 'propname,value' ), $db );
00377
00378 $this->reportProgress( "Database initialised successfully.\n\n", $verbose );
00379 }
00380
00381 public function drop( $verbose = true ) {
00382 global $wgDBtype;
00383 $this->reportProgress( "Deleting all database content and tables generated by SMW ...\n\n", $verbose );
00384 $db = wfGetDB( DB_MASTER );
00385 $tables = array( 'smwsimple_data', 'smwsimple_special' );
00386 foreach ( $tables as $table ) {
00387 $name = $db->tableName( $table );
00388 $db->query( 'DROP TABLE' . ( $wgDBtype == 'postgres' ? '':' IF EXISTS' ) . $name, 'SMWSQLStoreLight::drop' );
00389 $this->reportProgress( " ... dropped table $name.\n", $verbose );
00390 }
00391 $this->reportProgress( "All data removed successfully.\n", $verbose );
00392 return true;
00393 }
00394
00395 public function refreshData( &$index, $count, $namespaces = false, $usejobs = true ) {
00396 $updatejobs = array();
00397 $emptyrange = true;
00398
00399
00400 $tids = array();
00401 for ( $i = $index; $i < $index + $count; $i++ ) {
00402 $tids[] = $i;
00403 }
00404 $titles = Title::newFromIDs( $tids );
00405 foreach ( $titles as $title ) {
00406 if ( ( $namespaces == false ) || ( in_array( $title->getNamespace(), $namespaces ) ) ) {
00407 $updatejobs[] = new SMWUpdateJob( $title );
00408 $emptyrange = false;
00409 }
00410 }
00411
00412 wfRunHooks('smwRefreshDataJobs', array(&$updatejobs));
00413
00414 if ( $usejobs ) {
00415 Job::batchInsert( $updatejobs );
00416 } else {
00417 foreach ( $updatejobs as $job ) {
00418 $job->run();
00419 }
00420 }
00421
00422 $db = wfGetDB( DB_SLAVE );
00423 $nextpos = $index + $count;
00424 if ( $emptyrange ) {
00425 $nextpos = $db->selectField( 'page', 'page_id', "page_id >= $nextpos", __METHOD__, array( 'ORDER BY' => "page_id ASC" ) );
00426 }
00427 $maxpos = $db->selectField( 'page', 'MAX(page_id)', '', __METHOD__ );
00428 $index = $nextpos ? $nextpos : -1;
00429 return ( $index > 0 ) ? ( $index / $maxpos ) : 1;
00430 }
00431
00432
00434
00440 public function refreshConceptCache( $concept ) {
00441 return false;
00442 }
00443
00449 public function deleteConceptCache( $concept ) {
00450 return false;
00451 }
00452
00463 public function getConceptCacheStatus( $concept ) {
00464 return array( 'status' => 'no' );
00465 }
00466
00467
00469
00475 protected function getSQLOptions( $requestoptions, $valuecol = '' ) {
00476 $sql_options = array();
00477 if ( $requestoptions !== null ) {
00478 if ( $requestoptions->limit > 0 ) {
00479 $sql_options['LIMIT'] = $requestoptions->limit;
00480 }
00481 if ( $requestoptions->offset > 0 ) {
00482 $sql_options['OFFSET'] = $requestoptions->offset;
00483 }
00484 if ( ( $valuecol !== '' ) && ( $requestoptions->sort ) ) {
00485 $sql_options['ORDER BY'] = $requestoptions->ascending ? $valuecol : $valuecol . ' DESC';
00486 }
00487 }
00488 return $sql_options;
00489 }
00490
00500 protected function getSQLConditions( $requestoptions, $valuecol = '', $labelcol = '', $addand = true ) {
00501 $sql_conds = '';
00502 if ( $requestoptions !== null ) {
00503 $db = wfGetDB( DB_SLAVE );
00504 if ( ( $valuecol !== '' ) && ( $requestoptions->boundary !== null ) ) {
00505 if ( $requestoptions->ascending ) {
00506 $op = $requestoptions->include_boundary ? ' >= ':' > ';
00507 } else {
00508 $op = $requestoptions->include_boundary ? ' <= ':' < ';
00509 }
00510 $sql_conds .= ( $addand ? ' AND ':'' ) . $valuecol . $op . $db->addQuotes( $requestoptions->boundary );
00511 }
00512 if ( $labelcol !== '' ) {
00513 foreach ( $requestoptions->getStringConditions() as $strcond ) {
00514 $string = str_replace( '_', '\_', $strcond->string );
00515 switch ( $strcond->condition ) {
00516 case SMWStringCondition::STRCOND_PRE: $string .= '%'; break;
00517 case SMWStringCondition::STRCOND_POST: $string = '%' . $string; break;
00518 case SMWStringCondition::STRCOND_MID: $string = '%' . $string . '%'; break;
00519 }
00520 $sql_conds .= ( ( $addand || ( $sql_conds !== '' ) ) ? ' AND ':'' ) . $labelcol . ' LIKE ' . $db->addQuotes( $string );
00521 }
00522 }
00523 }
00524 return $sql_conds;
00525 }
00526
00534 protected function applyRequestOptions( $data, $requestoptions ) {
00535 wfProfileIn( "SMWSQLStoreLight::applyRequestOptions (SMW)" );
00536 if ( ( count( $data ) == 0 ) || ( $requestoptions === null ) ) {
00537 wfProfileOut( "SMWSQLStoreLight::applyRequestOptions (SMW)" );
00538 return $data;
00539 }
00540 $result = array();
00541 $sortres = array();
00542 $tablename = SMWSQLStoreLight::findTypeTableName( reset( $data )->getTypeID() );
00543
00544 $i = 0;
00545 foreach ( $data as $item ) {
00546 $ok = true;
00547 $value = ( $tablename == 'smwsimple_special' ) ? reset( $item->getDBkeys() ) : serialize( $item->getDBkeys() );
00548 if ( $requestoptions->boundary !== null ) {
00549 $strc = strcmp( $value, $requestoptions->boundary );
00550 if ( $requestoptions->ascending ) {
00551 if ( $requestoptions->include_boundary ) {
00552 $ok = ( $strc >= 0 );
00553 } else {
00554 $ok = ( $strc > 0 );
00555 }
00556 } else {
00557 if ( $requestoptions->include_boundary ) {
00558 $ok = ( $strc <= 0 );
00559 } else {
00560 $ok = ( $strc < 0 );
00561 }
00562 }
00563 }
00564 foreach ( $requestoptions->getStringConditions() as $strcond ) {
00565 switch ( $strcond->condition ) {
00566 case SMWStringCondition::STRCOND_PRE:
00567 $ok = $ok && ( strpos( $value, $strcond->string ) === 0 );
00568 break;
00569 case SMWStringCondition::STRCOND_POST:
00570 $ok = $ok && ( strpos( strrev( $value ), strrev( $strcond->string ) ) === 0 );
00571 break;
00572 case SMWStringCondition::STRCOND_MID:
00573 $ok = $ok && ( strpos( $value, $strcond->string ) !== false );
00574 break;
00575 }
00576 }
00577 if ( $ok ) {
00578 $result[$i] = $item;
00579 $sortres[$i] = $value;
00580 $i++;
00581 }
00582 }
00583 if ( $requestoptions->sort ) {
00584 $flag = SORT_LOCALE_STRING;
00585 if ( $requestoptions->ascending ) {
00586 asort( $sortres, $flag );
00587 } else {
00588 arsort( $sortres, $flag );
00589 }
00590 $newres = array();
00591 foreach ( $sortres as $key => $value ) {
00592 $newres[] = $result[$key];
00593 }
00594 $result = $newres;
00595 }
00596 if ( $requestoptions->limit > 0 ) {
00597 $result = array_slice( $result, $requestoptions->offset, $requestoptions->limit );
00598 } else {
00599 $result = array_slice( $result, $requestoptions->offset );
00600 }
00601 wfProfileOut( "SMWSQLStoreLight::applyRequestOptions (SMW)" );
00602 return $result;
00603 }
00604
00609 public function reportProgress( $msg, $verbose = true ) {
00610 if ( $verbose ) {
00611 if ( ob_get_level() == 0 ) {
00612 ob_start();
00613 }
00614 print $msg;
00615 ob_flush();
00616 flush();
00617 }
00618 }
00619
00624 public static function findPropertyTableName( $property ) {
00625 return SMWSQLStoreLight::findTypeTableName( $property->getPropertyTypeID() );
00626 }
00627
00632 public static function findTypeTableName( $typeid ) {
00633 if ( array_key_exists( $typeid, SMWSQLStoreLight::$special_types ) ) {
00634 return 'smwsimple_special';
00635 } else {
00636 return 'smwsimple_data';
00637 }
00638 }
00639
00644 protected function deleteSemanticData( SMWWikiPageValue $subject ) {
00645 $db = wfGetDB( DB_MASTER );
00646 $id = $subject->getTitle()->getArticleID();
00647 if ( $id == 0 ) return;
00648 foreach ( array( 'smwsimple_data', 'smwsimple_special' ) as $tablename ) {
00649 $db->delete( $tablename, array( 'pageid' => $id ), 'SMW::deleteSemanticData' );
00650 }
00651 wfRunHooks( 'smwDeleteSemanticData', array( $subject ) );
00652 }
00653
00654 }