00001 <?php
00013
00014 define( 'SMW_SQL2_NOQUERY', 0 );
00015 define( 'SMW_SQL2_TABLE', 1 );
00016 define( 'SMW_SQL2_VALUE', 2 );
00017 define( 'SMW_SQL2_DISJUNCTION', 3 );
00018 define( 'SMW_SQL2_CONJUNCTION', 4 );
00019 define( 'SMW_SQL2_CLASS_HIERARCHY', 5 );
00020 define( 'SMW_SQL2_PROP_HIERARCHY', 6 );
00021
00027 class SMWSQLStore2Query {
00028 public $type = SMW_SQL2_TABLE;
00029 public $jointable = '';
00030 public $joinfield = '';
00031 public $from = '';
00032 public $where = '';
00033 public $components = array();
00034
00039 public $alias;
00040
00045 public $sortfields = array();
00046
00047 public static $qnum = 0;
00048
00049 public function __construct() {
00050 $this->alias = 't' . self::$qnum++;
00051 }
00052 }
00053
00058 class SMWSQLStore2QueryEngine {
00059
00061 protected $m_dbs;
00063 protected $m_store;
00065 protected $m_qmode;
00067 protected $m_queries = array();
00069 protected $m_querylog = array();
00075 protected $m_sortkeys;
00077 protected $m_hierarchies = array();
00079 protected $m_errors = array();
00080
00081 public function __construct( &$parentstore, &$dbslave ) {
00082 $this->m_store = $parentstore;
00083 $this->m_dbs = $dbslave;
00084 }
00085
00093 public function refreshConceptCache( Title $concept ) {
00094 global $smwgQMaxLimit, $smwgQConceptFeatures, $wgDBtype;
00095
00096 $fname = 'SMW::refreshConceptCache';
00097
00098 $cid = $this->m_store->getSMWPageID( $concept->getDBkey(), SMW_NS_CONCEPT, '', '' );
00099 $cid_c = $this->m_store->getSMWPageID( $concept->getDBkey(), SMW_NS_CONCEPT, '', '', false );
00100
00101 if ( $cid != $cid_c ) {
00102 $this->m_errors[] = "Skipping redirect concept.";
00103 return $this->m_errors;
00104 }
00105
00106 $values = $this->m_store->getPropertyValues( SMWDIWikiPage::newFromTitle( $concept ), new SMWDIProperty( '_CONC' ) );
00107 $di = end( $values );
00108 $desctxt = ( $di !== false ) ? $di->getConceptQuery() : false;
00109 $this->m_errors = array();
00110
00111 if ( $desctxt ) {
00112 $this->m_qmode = SMWQuery::MODE_INSTANCES;
00113 $this->m_queries = array();
00114 $this->m_hierarchies = array();
00115 $this->m_querylog = array();
00116 $this->m_sortkeys = array();
00117 SMWSQLStore2Query::$qnum = 0;
00118
00119
00120 $qp = new SMWQueryParser( $smwgQConceptFeatures );
00121 $desc = $qp->getQueryDescription( $desctxt );
00122 $qid = $this->compileQueries( $desc );
00123
00124 $this->executeQueries( $this->m_queries[$qid] );
00125 $qobj = $this->m_queries[$qid];
00126
00127 if ( $qobj->joinfield === '' ) {
00128 return;
00129 }
00130
00131
00132 $this->m_dbs->delete( 'smw_conccache', array( 'o_id' => $cid ), $fname );
00133 $smw_conccache = $this->m_dbs->tablename( 'smw_conccache' );
00134
00135 if ( $wgDBtype == 'postgres' ) {
00136 $where = $qobj->where . ( $qobj->where ? ' AND ' : '' ) .
00137 "NOT EXISTS (SELECT NULL FROM $smw_conccache" .
00138 " WHERE {$smw_conccache}.s_id = {$qobj->alias}.s_id " .
00139 " AND {$smw_conccache}.o_id = {$qobj->alias}.o_id )";
00140 } else {
00141 $where = $qobj->where;
00142 }
00143
00144 $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? '' : 'IGNORE ' ) .
00145 "INTO $smw_conccache" .
00146 " SELECT DISTINCT {$qobj->joinfield} AS s_id, $cid AS o_id FROM " .
00147 $this->m_dbs->tableName( $qobj->jointable ) . " AS {$qobj->alias}" .
00148 $qobj->from .
00149 ( $where ? ' WHERE ' : '' ) . $where . " LIMIT $smwgQMaxLimit",
00150 $fname );
00151
00152 $this->m_dbs->update( 'smw_conc2',
00153 array( 'cache_date' => strtotime( "now" ), 'cache_count' => $this->m_dbs->affectedRows() ),
00154 array( 's_id' => $cid ), $fname );
00155 } else {
00156 $this->m_dbs->delete( 'smw_conccache', array( 'o_id' => $cid ), $fname );
00157 $this->m_dbs->update( 'smw_conc2',
00158 array( 'cache_date' => null, 'cache_count' => null ),
00159 array( 's_id' => $cid ), $fname );
00160 $this->m_errors[] = "No concept description found.";
00161 }
00162
00163 $this->cleanUp();
00164
00165 return $this->m_errors;
00166 }
00167
00173 public function deleteConceptCache( $concept ) {
00174 $cid = $this->m_store->getSMWPageID( $concept->getDBkey(), SMW_NS_CONCEPT, '', '', false );
00175 $this->m_dbs->delete( 'smw_conccache', array( 'o_id' => $cid ), 'SMW::refreshConceptCache' );
00176 $this->m_dbs->update( 'smw_conc2', array( 'cache_date' => null, 'cache_count' => null ), array( 's_id' => $cid ), 'SMW::refreshConceptCache' );
00177 }
00178
00209 public function getQueryResult( SMWQuery $query ) {
00210 global $smwgIgnoreQueryErrors, $smwgQSortingSupport;
00211
00212 if ( ( !$smwgIgnoreQueryErrors || $query->getDescription() instanceof SMWThingDescription ) &&
00213 $query->querymode != SMWQuery::MODE_DEBUG &&
00214 count( $query->getErrors() ) > 0 ) {
00215 return new SMWQueryResult( $query->getDescription()->getPrintrequests(), $query, array(), $this->m_store, false );
00216
00217
00218 } elseif ( $query->querymode == SMWQuery::MODE_NONE ) {
00219
00220 return new SMWQueryResult( $query->getDescription()->getPrintrequests(), $query, array(), $this->m_store, true );
00221 }
00222
00223 $this->m_qmode = $query->querymode;
00224 $this->m_queries = array();
00225 $this->m_hierarchies = array();
00226 $this->m_querylog = array();
00227 $this->m_errors = array();
00228 SMWSQLStore2Query::$qnum = 0;
00229 $this->m_sortkeys = $query->sortkeys;
00230
00231
00232 wfProfileIn( 'SMWSQLStore2Queries::compileMainQuery (SMW)' );
00233 $qid = $this->compileQueries( $query->getDescription() );
00234 wfProfileOut( 'SMWSQLStore2Queries::compileMainQuery (SMW)' );
00235
00236 if ( $qid < 0 ) {
00237 $qid = SMWSQLStore2Query::$qnum;
00238 $q = new SMWSQLStore2Query();
00239 $q->jointable = 'smw_ids';
00240 $q->joinfield = "$q->alias.smw_id";
00241 $q->where = "$q->alias.smw_iw!=" . $this->m_dbs->addQuotes( SMW_SQL2_SMWIW_OUTDATED ) . " AND $q->alias.smw_iw!=" . $this->m_dbs->addQuotes( SMW_SQL2_SMWREDIIW ) . " AND $q->alias.smw_iw!=" . $this->m_dbs->addQuotes( SMW_SQL2_SMWBORDERIW ) . " AND $q->alias.smw_iw!=" . $this->m_dbs->addQuotes( SMW_SQL2_SMWINTDEFIW );
00242 $this->m_queries[$qid] = $q;
00243 }
00244
00245 if ( $this->m_queries[$qid]->jointable != 'smw_ids' ) {
00246
00247 $rootid = SMWSQLStore2Query::$qnum;
00248 $qobj = new SMWSQLStore2Query();
00249 $qobj->jointable = 'smw_ids';
00250 $qobj->joinfield = "$qobj->alias.smw_id";
00251 $qobj->components = array( $qid => "$qobj->alias.smw_id" );
00252 $qobj->sortfields = $this->m_queries[$qid]->sortfields;
00253 $this->m_queries[$rootid] = $qobj;
00254 } else {
00255 $rootid = $qid;
00256 }
00257
00258
00259 if ( $smwgQSortingSupport ) {
00260 $this->applyOrderConditions( $rootid );
00261 }
00262
00263
00264 if ( !$smwgIgnoreQueryErrors &&
00265 $query->querymode != SMWQuery::MODE_DEBUG &&
00266 count( $this->m_errors ) > 0 ) {
00267 $query->addErrors( $this->m_errors );
00268 return new SMWQueryResult( $query->getDescription()->getPrintrequests(), $query, array(), $this->m_store, false );
00269 }
00270
00271
00272 wfProfileIn( 'SMWSQLStore2Queries::executeMainQuery (SMW)' );
00273 $this->executeQueries( $this->m_queries[$rootid] );
00274 wfProfileOut( 'SMWSQLStore2Queries::executeMainQuery (SMW)' );
00275
00276 switch ( $query->querymode ) {
00277 case SMWQuery::MODE_DEBUG:
00278 $result = $this->getDebugQueryResult( $query, $rootid );
00279 break;
00280 case SMWQuery::MODE_COUNT:
00281 $result = $this->getCountQueryResult( $query, $rootid );
00282 break;
00283 default:
00284 $result = $this->getInstanceQueryResult( $query, $rootid );
00285 break;
00286 }
00287
00288 $this->cleanUp();
00289 $query->addErrors( $this->m_errors );
00290
00291 return $result;
00292 }
00293
00303 protected function getDebugQueryResult( SMWQuery $query, $rootid ) {
00304 $qobj = $this->m_queries[$rootid];
00305
00306 $entries = array();
00307
00308 $sql_options = $this->getSQLOptions( $query, $rootid );
00309 list( $startOpts, $useIndex, $tailOpts ) = $this->m_dbs->makeSelectOptions( $sql_options );
00310 if ( $qobj->joinfield !== '' ) {
00311 $entries['SQL Query'] =
00312 "<tt>SELECT DISTINCT $qobj->alias.smw_title AS t,$qobj->alias.smw_namespace AS ns FROM " .
00313 $this->m_dbs->tableName( $qobj->jointable ) . " AS $qobj->alias" . $qobj->from .
00314 ( ( $qobj->where === '' ) ? '':' WHERE ' ) . $qobj->where . "$tailOpts LIMIT " .
00315 $sql_options['LIMIT'] . ' OFFSET ' . $sql_options['OFFSET'] . ';</tt>';
00316 } else {
00317 $entries['SQL Query'] = 'Empty result, no SQL query created.';
00318 }
00319
00320 $auxtables = '';
00321 foreach ( $this->m_querylog as $table => $log ) {
00322 $auxtables .= "<li>Temporary table $table";
00323 foreach ( $log as $q ) {
00324 $auxtables .= "<br />  <tt>$q</tt>";
00325 }
00326 $auxtables .= '</li>';
00327 }
00328 if ( $auxtables ) {
00329 $entries['Auxilliary Tables Used'] = "<ul>$auxtables</ul>";
00330 } else {
00331 $entries['Auxilliary Tables Used'] = 'No auxilliary tables used.';
00332 }
00333
00334 return SMWStore::formatDebugOutput( 'SMWSQLStore2', $entries, $query );
00335 }
00336
00346 protected function getCountQueryResult( SMWQuery $query, $rootid ) {
00347 wfProfileIn( 'SMWSQLStore2Queries::getCountQueryResult (SMW)' );
00348
00349 $qobj = $this->m_queries[$rootid];
00350
00351 if ( $qobj->joinfield === '' ) {
00352 wfProfileOut( 'SMWSQLStore2Queries::getCountQueryResult (SMW)' );
00353 return 0;
00354 }
00355
00356 $sql_options = array( 'LIMIT' => $query->getLimit() + 1, 'OFFSET' => $query->getOffset() );
00357 $res = $this->m_dbs->select( $this->m_dbs->tableName( $qobj->jointable ) . " AS $qobj->alias" . $qobj->from, "COUNT(DISTINCT $qobj->alias.smw_id) AS count", $qobj->where, 'SMW::getQueryResult', $sql_options );
00358 $row = $this->m_dbs->fetchObject( $res );
00359
00360 $count = $row->count;
00361 $this->m_dbs->freeResult( $res );
00362
00363 wfProfileOut( 'SMWSQLStore2Queries::getCountQueryResult (SMW)' );
00364
00365 return $count;
00366 }
00367
00386 protected function getInstanceQueryResult( SMWQuery $query, $rootid ) {
00387 global $wgDBtype;
00388
00389 wfProfileIn( 'SMWSQLStore2Queries::getInstanceQueryResult (SMW)' );
00390 $qobj = $this->m_queries[$rootid];
00391
00392 if ( $qobj->joinfield === '' ) {
00393 $result = new SMWQueryResult( $query->getDescription()->getPrintrequests(), $query, array(), $this->m_store, false );
00394 wfProfileOut( 'SMWSQLStore2Queries::getInstanceQueryResult (SMW)' );
00395 return $result;
00396 }
00397
00398 $sql_options = $this->getSQLOptions( $query, $rootid );
00399
00400
00401 $sortfields = implode( $qobj->sortfields, ',' );
00402
00403 $res = $this->m_dbs->select( $this->m_dbs->tableName( $qobj->jointable ) . " AS $qobj->alias" . $qobj->from,
00404 "DISTINCT $qobj->alias.smw_id AS id,$qobj->alias.smw_title AS t,$qobj->alias.smw_namespace AS ns,$qobj->alias.smw_iw AS iw,$qobj->alias.smw_subobject AS so,$qobj->alias.smw_sortkey AS sortkey" .
00405 ( $wgDBtype == 'postgres' ? ( ( $sortfields ? ',' : '' ) . $sortfields ) : '' ),
00406 $qobj->where, 'SMW::getQueryResult', $sql_options );
00407
00408 $qr = array();
00409 $count = 0;
00410 $prs = $query->getDescription()->getPrintrequests();
00411
00412 while ( ( $count < $query->getLimit() ) && ( $row = $this->m_dbs->fetchObject( $res ) ) ) {
00413 $count++;
00414 if ( $row->iw === '' || $row->iw{0} != ':' ) {
00415 $v = new SMWDIWikiPage( $row->t, $row->ns, $row->iw, $row->so );
00416 $qr[] = $v;
00417 $this->m_store->cacheSMWPageID( $row->id, $row->t, $row->ns, $row->iw, $row->so );
00418 }
00419 }
00420
00421 if ( $this->m_dbs->fetchObject( $res ) ) {
00422 $count++;
00423 }
00424
00425 $this->m_dbs->freeResult( $res );
00426 $result = new SMWQueryResult( $prs, $query, $qr, $this->m_store, ( $count > $query->getLimit() ) );
00427
00428 wfProfileOut( 'SMWSQLStore2Queries::getInstanceQueryResult (SMW)' );
00429
00430 return $result;
00431 }
00432
00447 protected function compileQueries( SMWDescription $description ) {
00448 $qid = SMWSQLStore2Query::$qnum;
00449 $query = new SMWSQLStore2Query();
00450
00451 if ( $description instanceof SMWSomeProperty ) {
00452 $this->compilePropertyCondition( $query, $description->getProperty(), $description->getDescription() );
00453
00454 if ( $query->type == SMW_SQL2_NOQUERY ) $qid = - 1;
00455 } elseif ( $description instanceof SMWNamespaceDescription ) {
00456
00457 $query->jointable = 'smw_ids';
00458 $query->joinfield = "$query->alias.smw_id";
00459 $query->where = "$query->alias.smw_namespace=" . $this->m_dbs->addQuotes( $description->getNamespace() );
00460 } elseif ( ( $description instanceof SMWConjunction ) || ( $description instanceof SMWDisjunction ) ) {
00461 $query->type = ( $description instanceof SMWConjunction ) ? SMW_SQL2_CONJUNCTION:SMW_SQL2_DISJUNCTION;
00462
00463 foreach ( $description->getDescriptions() as $subdesc ) {
00464 $sub = $this->compileQueries( $subdesc );
00465 if ( $sub >= 0 ) {
00466 $query->components[$sub] = true;
00467 }
00468 }
00469
00470
00471 if ( count( $query->components ) == 0 ) $qid = - 1;
00472 } elseif ( $description instanceof SMWClassDescription ) {
00473 $cqid = SMWSQLStore2Query::$qnum;
00474 $cquery = new SMWSQLStore2Query();
00475 $cquery->type = SMW_SQL2_CLASS_HIERARCHY;
00476 $cquery->joinfield = array();
00477
00478 foreach ( $description->getCategories() as $cat ) {
00479 $cid = $this->m_store->getSMWPageID( $cat->getDBkey(), NS_CATEGORY, $cat->getInterwiki(), '' );
00480 if ( $cid != 0 ) {
00481 $cquery->joinfield[] = $cid;
00482 }
00483 }
00484
00485 if ( count( $cquery->joinfield ) == 0 ) {
00486 $query->type = SMW_SQL2_VALUE;
00487 $query->jointable = '';
00488 $query->joinfield = '';
00489 } else {
00490 $query->jointable = 'smw_inst2';
00491 $query->joinfield = "$query->alias.s_id";
00492 $query->components[$cqid] = "$query->alias.o_id";
00493 $this->m_queries[$cqid] = $cquery;
00494 }
00495 } elseif ( $description instanceof SMWValueDescription ) {
00496 if ( $description->getDataItem() instanceof SMWDIWikiPage ) {
00497 if ( $description->getComparator() == SMW_CMP_EQ ) {
00498 $query->type = SMW_SQL2_VALUE;
00499 $oid = $this->m_store->getSMWPageID( $description->getDataItem()->getDBkey(), $description->getDataItem()->getNamespace(), $description->getDataItem()->getInterwiki(), $description->getDataItem()->getSubobjectName() );
00500 $query->joinfield = array( $oid );
00501 } else {
00502 $query->jointable = 'smw_ids';
00503 $query->joinfield = "$query->alias.smw_id";
00504 $value = $description->getDataItem()->getSortKey();
00505
00506 switch ( $description->getComparator() ) {
00507 case SMW_CMP_LEQ: $comp = '<='; break;
00508 case SMW_CMP_GEQ: $comp = '>='; break;
00509 case SMW_CMP_LESS: $comp = '<'; break;
00510 case SMW_CMP_GRTR: $comp = '>'; break;
00511 case SMW_CMP_NEQ: $comp = '!='; break;
00512 case SMW_CMP_LIKE: case SMW_CMP_NLKE:
00513 $comp = ' LIKE ';
00514 if ( $description->getComparator() == SMW_CMP_NLKE ) $comp = " NOT{$comp}";
00515 $value = str_replace( array( '%', '_', '*', '?' ), array( '\%', '\_', '%', '_' ), $value );
00516 break;
00517 }
00518 $query->where = "$query->alias.smw_sortkey$comp" . $this->m_dbs->addQuotes( $value );
00519 }
00520 }
00521 } elseif ( $description instanceof SMWConceptDescription ) {
00522 $cid = $this->m_store->getSMWPageID( $description->getConcept()->getDBkey(), SMW_NS_CONCEPT, '', '' );
00523
00524
00525 $row = $this->m_dbs->selectRow(
00526 'smw_conc2',
00527 array( 'concept_txt', 'concept_features', 'concept_size', 'concept_depth', 'cache_date' ),
00528 array( 's_id' => $cid ),
00529 'SMWSQLStore2Queries::compileQueries'
00530 );
00531
00532 if ( $row === false ) {
00533
00534
00535
00536 } else {
00537 global $smwgQConceptCaching, $smwgQMaxSize, $smwgQMaxDepth, $smwgQFeatures, $smwgQConceptCacheLifetime;
00538
00539 $may_be_computed = ( $smwgQConceptCaching == CONCEPT_CACHE_NONE ) ||
00540 ( ( $smwgQConceptCaching == CONCEPT_CACHE_HARD ) && ( ( ~( ~( $row->concept_features + 0 ) | $smwgQFeatures ) ) == 0 ) &&
00541 ( $smwgQMaxSize >= $row->concept_size ) && ( $smwgQMaxDepth >= $row->concept_depth ) );
00542
00543 if ( $row->cache_date &&
00544 ( ( $row->cache_date > ( strtotime( "now" ) - $smwgQConceptCacheLifetime * 60 ) ) ||
00545 !$may_be_computed ) ) {
00546
00547 $query->jointable = 'smw_conccache';
00548 $query->joinfield = "$query->alias.s_id";
00549 $query->where = "$query->alias.o_id=" . $this->m_dbs->addQuotes( $cid );
00550 } elseif ( $row->concept_txt ) {
00551 if ( $may_be_computed ) {
00552 $qp = new SMWQueryParser();
00553
00554
00555
00556 $desc = $qp->getQueryDescription( str_replace( array( '<', '>', '&' ), array( '<', '>', '&' ), $row->concept_txt ) );
00557 $qid = $this->compileQueries( $desc );
00558 if ($qid != -1) {
00559 $query = $this->m_queries[$qid];
00560 } else {
00561 $this->m_errors[] = wfMsg( 'smw_emptysubquery' );
00562 }
00563 } else {
00564 $this->m_errors[] = wfMsg( 'smw_concept_cache_miss', $description->getConcept()->getText() );
00565 }
00566 }
00567 }
00568 } else {
00569 $qid = - 1;
00570 }
00571
00572 if ( $qid >= 0 ) {
00573 $this->m_queries[$qid] = $query;
00574
00575 if ( $query->type != SMW_SQL2_DISJUNCTION ) {
00576
00577 foreach ( $query->components as $cid => $field ) {
00578 $query->sortfields = array_merge( $this->m_queries[$cid]->sortfields, $query->sortfields );
00579 }
00580 }
00581 }
00582
00583 return $qid;
00584 }
00585
00593 protected function compilePropertyCondition( SMWSQLStore2Query $query, SMWDIProperty $property, SMWDescription $valuedesc ) {
00594 $tableid = SMWSQLStore2::findPropertyTableID( $property );
00595 $typeid = $property->findPropertyTypeID();
00596
00597 if ( $tableid === '' ) {
00598 $query->type = SMW_SQL2_NOQUERY;
00599 return;
00600 }
00601
00602 $proptables = SMWSQLStore2::getPropertyTables();
00603 $proptable = $proptables[$tableid];
00604
00605 if ( !$proptable->idsubject ) {
00606 $query->type = SMW_SQL2_NOQUERY;
00607 return;
00608 }
00609
00610 list( $sig, $valueindex, $labelindex ) = SMWSQLStore2::getTypeSignature( $typeid );
00611 $sortkey = $property->getKey();
00612
00613
00614 $query->jointable = $proptable->name;
00615
00616 if ( $property->isInverse() ) {
00617 if ( ( count( $proptable->objectfields ) == 1 ) && ( reset( $proptable->objectfields ) == 'p' ) ) {
00618 $keys = array_keys( $proptable->objectfields );
00619 $query->joinfield = $query->alias . '.' . $keys[0];
00620 $objectfields = array( 's_id' => 'p' );
00621 $valueindex = $labelindex = 3;
00622 } else {
00623 $query->type = SMW_SQL2_NOQUERY;
00624 return;
00625 }
00626 } else {
00627 $query->joinfield = "{$query->alias}.s_id";
00628 $objectfields = $proptable->objectfields;
00629 }
00630
00631
00632 if ( $proptable->fixedproperty == false ) {
00633 $pid = $this->m_store->getSMWPropertyID( $property );
00634
00635 if ( $property->isUserDefined() || ( $property->findPropertyTypeID() != '__err' ) ) {
00636
00637
00638
00639 $pqid = SMWSQLStore2Query::$qnum;
00640 $pquery = new SMWSQLStore2Query();
00641 $pquery->type = SMW_SQL2_PROP_HIERARCHY;
00642 $pquery->joinfield = array( $pid );
00643 $query->components[$pqid] = "{$query->alias}.p_id";
00644 $this->m_queries[$pqid] = $pquery;
00645 } else {
00646 $query->where = "{$query->alias}.p_id=" . $this->m_dbs->addQuotes( $pid );
00647 }
00648 }
00649
00650
00651 if ( ( count( $objectfields ) == 1 ) && ( reset( $objectfields ) == 'p' ) ) {
00652 $sub = $this->compileQueries( $valuedesc );
00653
00654 $objectfield = array_keys( $objectfields );
00655 $objectfield = reset( $objectfield );
00656
00657 if ( $sub >= 0 ) {
00658 $query->components[$sub] = "{$query->alias}.{$objectfield}";
00659 }
00660 } else {
00661 $this->compileAttributeWhere( $query, $valuedesc, $proptable, $valueindex );
00662
00663 }
00664
00665
00666 if ( ( $valueindex >= 0 ) && array_key_exists( $sortkey, $this->m_sortkeys ) ) {
00667
00668
00669
00670
00671
00672
00673 $smwidjoinfield = false;
00674 $fieldName = $this->getDBFieldsForDVIndex( $objectfields, $valueindex, $smwidjoinfield );
00675
00676 if ( $fieldName ) {
00677 if ( $smwidjoinfield ) {
00678
00679 $query->from = ' INNER JOIN ' . $this->m_dbs->tableName( 'smw_ids' ) .
00680 " AS ids{$query->alias} ON ids{$query->alias}.smw_id={$query->alias}.{$smwidjoinfield}";
00681 $query->sortfields[$sortkey] = "ids{$query->alias}.{$fieldName}";
00682 } else {
00683 $query->sortfields[$sortkey] = "{$query->alias}.{$fieldName}";
00684 }
00685 }
00686 }
00687 }
00688
00706 protected function getDBFieldsForDVIndex( array $objectFields, $index, &$smwidjoinfield ) {
00707 $fieldName = false;
00708
00709 $curindex = 0;
00710 foreach( $objectFields as $fname => $ftype ) {
00711 if ( $ftype == 'p' ) {
00712 if ( $curindex + 4 >= $index ) {
00713 $idfieldnames = array( 'smw_title', 'smw_namespace', 'smw_iw', 'smw_sortkey' );
00714 $smwidjoinfield = $fname;
00715 $fieldName = $idfieldnames[$index - $curindex];
00716 break;
00717 }
00718 $curindex += 3;
00719 } elseif ( $curindex == $index ) {
00720 $smwidjoinfield = false;
00721 $fieldName = $fname;
00722 break;
00723 }
00724 $curindex++;
00725 }
00726
00727 return $fieldName;
00728 }
00729
00741 protected function compileAttributeWhere(
00742 $query, SMWDescription $description, SMWSQLStore2Table $proptable, $valueIndex, $operator = 'AND' ) {
00743
00744 $where = '';
00745
00746 if ( $description instanceof SMWValueDescription ) {
00747 $dataItem = $description->getDataItem();
00748 $keys = SMWCompatibilityHelpers::getDBkeysFromDataItem( $dataItem );
00749
00750
00751 if ( $valueIndex >= 0 ) {
00752
00753 $smwidjoinfield = false;
00754 $fieldName = $this->getDBFieldsForDVIndex( $proptable->objectfields, $valueIndex, $smwidjoinfield );
00755
00756
00757 if ( $fieldName && !$smwidjoinfield ) {
00758 $comparator = false;
00759 $customSQL = false;
00760
00761
00762 if ( method_exists( $description, 'getSQLCondition' ) ) {
00763 $customSQL = $description->getSQLCondition( $query->alias, array_keys( $proptable->objectfields ), $this->m_dbs );
00764 }
00765
00766 if ( $customSQL ) {
00767 $where = $customSQL;
00768 } else {
00769 $value = $keys[$valueIndex];
00770
00771 switch ( $description->getComparator() ) {
00772 case SMW_CMP_EQ: $comparator = '='; break;
00773 case SMW_CMP_LESS: $comparator = '<'; break;
00774 case SMW_CMP_GRTR: $comparator = '>'; break;
00775 case SMW_CMP_LEQ: $comparator = '<='; break;
00776 case SMW_CMP_GEQ: $comparator = '>='; break;
00777 case SMW_CMP_NEQ: $comparator = '!='; break;
00778 case SMW_CMP_LIKE: case SMW_CMP_NLKE:
00779 $comparator = ' LIKE ';
00780 if ( $description->getComparator() == SMW_CMP_NLKE ) $comparator = " NOT{$comparator}";
00781 $value = str_replace( array( '%', '_', '*', '?' ), array( '\%', '\_', '%', '_' ), $value );
00782 }
00783
00784 if ( $comparator ) {
00785 $where = "$query->alias.{$fieldName}{$comparator}" . $this->m_dbs->addQuotes( $value );
00786 }
00787 }
00788 }
00789 }
00790
00791 if ( $where === '' ) {
00792 $i = 0;
00793
00794 foreach ( $proptable->objectfields as $fname => $ftype ) {
00795 if ( $i >= count( $keys ) ) break;
00796
00797 if ( $ftype == 'p' ) {
00798 $oid = $this->getSMWPageID( $dataItem->getDBkey(), $dataItem->getNamespace(), $dataItem->getInterwiki(), $dataItem->getSubobjectName() );
00799 $where .= ( $where ? ' AND ' : '' ) . "{$query->alias}.$fname=" . $this->m_dbs->addQuotes( $oid );
00800 break;
00801 } elseif ( $ftype != 'l' ) {
00802 $where .= ( $where ? ' AND ' : '' ) . "{$query->alias}.$fname=" . $this->m_dbs->addQuotes( $keys[$i] );
00803 }
00804
00805 $i++;
00806 }
00807 }
00808
00809 } elseif ( ( $description instanceof SMWConjunction ) || ( $description instanceof SMWDisjunction ) ) {
00810 $op = ( $description instanceof SMWConjunction ) ? 'AND' : 'OR';
00811
00812 foreach ( $description->getDescriptions() as $subdesc ) {
00813 $this->compileAttributeWhere( $query, $subdesc, $proptable, $valueIndex, $op );
00814 }
00815 }
00816
00817 if ( $where !== '' ) $query->where .= ( $query->where ? " $operator " : '' ) . "($where)";
00818 }
00819
00827 protected function executeQueries( SMWSQLStore2Query &$query ) {
00828 global $wgDBtype;
00829
00830 switch ( $query->type ) {
00831 case SMW_SQL2_TABLE:
00832 foreach ( $query->components as $qid => $joinfield ) {
00833 $subquery = $this->m_queries[$qid];
00834 $this->executeQueries( $subquery );
00835
00836 if ( $subquery->jointable !== '' ) {
00837 $query->from .= ' INNER JOIN ' . $this->m_dbs->tableName( $subquery->jointable ) . " AS $subquery->alias ON $joinfield=" . $subquery->joinfield;
00838 } elseif ( $subquery->joinfield !== '' ) {
00839 $condition = '';
00840
00841 foreach ( $subquery->joinfield as $value ) {
00842 $condition .= ( $condition ? ' OR ':'' ) . "$joinfield=" . $this->m_dbs->addQuotes( $value );
00843 }
00844
00845 if ( count( $subquery->joinfield ) > 1 ) {
00846 $condition = "($condition)";
00847 }
00848
00849 $query->where .= ( ( $query->where === '' ) ? '':' AND ' ) . $condition;
00850 } else {
00851 $query->joinfield = '';
00852 $query->jointable = '';
00853 $query->where = '';
00854 $query->from = '';
00855 break;
00856 }
00857
00858 if ( $subquery->where !== '' ) {
00859 $query->where .= ( ( $query->where === '' ) ? '':' AND ' ) . '(' . $subquery->where . ')';
00860 }
00861
00862 $query->from .= $subquery->from;
00863 }
00864
00865 $query->components = array();
00866 break;
00867 case SMW_SQL2_CONJUNCTION:
00868
00869 reset( $query->components );
00870 $key = false;
00871
00872 foreach ( $query->components as $qkey => $qid ) {
00873 if ( $this->m_queries[$qkey]->jointable !== '' ) {
00874 $key = $qkey;
00875 break;
00876 }
00877 }
00878
00879 if ( $key !== false ) {
00880 $result = $this->m_queries[$key];
00881 unset( $query->components[$key] );
00882
00883
00884 $this->executeQueries( $result );
00885
00886
00887 foreach ( $query->components as $qid => $joinfield ) {
00888 $result->components[$qid] = $result->joinfield;
00889 }
00890
00891
00892 $this->executeQueries( $result );
00893 } else {
00894 $key = $qkey;
00895 $result = $this->m_queries[$key];
00896 unset( $query->components[$key] );
00897
00898 foreach ( $query->components as $qid => $joinfield ) {
00899 if ( $result->joinfield != $this->m_queries[$qid]->joinfield ) {
00900 $result->joinfield = '';
00901 break;
00902 }
00903 }
00904 }
00905 $query = $result;
00906 break;
00907 case SMW_SQL2_DISJUNCTION:
00908 if ( $this->m_qmode !== SMWQuery::MODE_DEBUG ) {
00909 $this->m_dbs->query( $this->getCreateTempIDTableSQL( $this->m_dbs->tableName( $query->alias ) ), 'SMW::executeQueries' );
00910 }
00911
00912 $this->m_querylog[$query->alias] = array();
00913
00914 foreach ( $query->components as $qid => $joinfield ) {
00915 $subquery = $this->m_queries[$qid];
00916 $this->executeQueries( $subquery );
00917 $sql = '';
00918
00919 if ( $subquery->jointable !== '' ) {
00920 $sql = 'INSERT ' . ( ( $wgDBtype == 'postgres' ) ? '':'IGNORE ' ) . 'INTO ' .
00921 $this->m_dbs->tableName( $query->alias ) .
00922 " SELECT $subquery->joinfield FROM " . $this->m_dbs->tableName( $subquery->jointable ) .
00923 " AS $subquery->alias $subquery->from" . ( $subquery->where ? " WHERE $subquery->where":'' );
00924 } elseif ( $subquery->joinfield !== '' ) {
00925
00926
00927 $values = '';
00928
00929 foreach ( $subquery->joinfield as $value ) {
00930 $values .= ( $values ? ',' : '' ) . '(' . $this->m_dbs->addQuotes( $value ) . ')';
00931 }
00932
00933 $sql = 'INSERT ' . ( ( $wgDBtype == 'postgres' ) ? '':'IGNORE ' ) . 'INTO ' . $this->m_dbs->tableName( $query->alias ) . " (id) VALUES $values";
00934 }
00935 if ( $sql ) {
00936 $this->m_querylog[$query->alias][] = $sql;
00937
00938 if ( $this->m_qmode !== SMWQuery::MODE_DEBUG ) {
00939 $this->m_dbs->query( $sql , 'SMW::executeQueries' );
00940 }
00941 }
00942 }
00943
00944 $query->jointable = $query->alias;
00945 $query->joinfield = "$query->alias.id";
00946 $query->sortfields = array();
00947
00948 break;
00949 case SMW_SQL2_PROP_HIERARCHY: case SMW_SQL2_CLASS_HIERARCHY:
00950 $this->executeHierarchyQuery( $query );
00951 break;
00952 case SMW_SQL2_VALUE: break;
00953 }
00954 }
00955
00962 protected function executeHierarchyQuery( SMWSQLStore2Query &$query ) {
00963 global $wgDBtype;
00964 global $smwgQSubpropertyDepth, $smwgQSubcategoryDepth;
00965
00966 $fname = "SMWSQLStore2Queries::executeQueries-hierarchy-$query->type (SMW)";
00967 wfProfileIn( $fname );
00968
00969 $depth = ( $query->type == SMW_SQL2_PROP_HIERARCHY ) ? $smwgQSubpropertyDepth : $smwgQSubcategoryDepth;
00970
00971 if ( $depth <= 0 ) {
00972 $query->type = SMW_SQL2_VALUE;
00973 wfProfileOut( $fname );
00974 return;
00975 }
00976
00977 $values = '';
00978 $valuecond = '';
00979
00980 foreach ( $query->joinfield as $value ) {
00981 $values .= ( $values ? ',':'' ) . '(' . $this->m_dbs->addQuotes( $value ) . ')';
00982 $valuecond .= ( $valuecond ? ' OR ':'' ) . 'o_id=' . $this->m_dbs->addQuotes( $value );
00983 }
00984
00985 $smwtable = $this->m_dbs->tableName( ( $query->type == SMW_SQL2_PROP_HIERARCHY ) ? 'smw_subp2':'smw_subs2' );
00986
00987
00988 $res = $this->m_dbs->select( $smwtable, 's_id', $valuecond, __METHOD__, array( 'LIMIT' => 1 ) );
00989
00990 if ( !$this->m_dbs->fetchObject( $res ) ) {
00991 $this->m_dbs->freeResult( $res );
00992 $query->type = SMW_SQL2_VALUE;
00993 wfProfileOut( $fname );
00994 return;
00995 }
00996
00997 $this->m_dbs->freeResult( $res );
00998 $tablename = $this->m_dbs->tableName( $query->alias );
00999 $this->m_querylog[$query->alias] = array( "Recursively computed hierarchy for element(s) $values." );
01000 $query->jointable = $query->alias;
01001 $query->joinfield = "$query->alias.id";
01002
01003 if ( $this->m_qmode == SMWQuery::MODE_DEBUG ) {
01004 wfProfileOut( $fname );
01005 return;
01006 }
01007
01008 $this->m_dbs->query( $this->getCreateTempIDTableSQL( $tablename ), 'SMW::executeHierarchyQuery' );
01009
01010 if ( array_key_exists( $values, $this->m_hierarchies ) ) {
01011 $this->m_dbs->query( "INSERT INTO $tablename (id) SELECT id" .
01012 ' FROM ' . $this->m_hierarchies[$values],
01013 'SMW::executeHierarchyQuery' );
01014 wfProfileOut( $fname );
01015 return;
01016 }
01017
01018
01019
01020
01021
01022 $tmpnew = 'smw_new';
01023 $tmpres = 'smw_res';
01024 $this->m_dbs->query( $this->getCreateTempIDTableSQL( $tmpnew ), 'SMW::executeQueries' );
01025 $this->m_dbs->query( $this->getCreateTempIDTableSQL( $tmpres ), 'SMW::executeQueries' );
01026 $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? "" : "IGNORE" ) . " INTO $tablename (id) VALUES $values", 'SMW::executeHierarchyQuery' );
01027 $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? "" : "IGNORE" ) . " INTO $tmpnew (id) VALUES $values", 'SMW::executeHierarchyQuery' );
01028
01029 for ( $i = 0; $i < $depth; $i++ ) {
01030 $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? '' : 'IGNORE ' ) . "INTO $tmpres (id) SELECT s_id" . ( $wgDBtype == 'postgres' ? '::integer':'' ) . " FROM $smwtable, $tmpnew WHERE o_id=id",
01031 'SMW::executeHierarchyQuery' );
01032 if ( $this->m_dbs->affectedRows() == 0 ) {
01033 break;
01034 }
01035
01036 $this->m_dbs->query( "INSERT " . ( ( $wgDBtype == 'postgres' ) ? '' : 'IGNORE ' ) . "INTO $tablename (id) SELECT $tmpres.id FROM $tmpres",
01037 'SMW::executeHierarchyQuery' );
01038
01039 if ( $this->m_dbs->affectedRows() == 0 ) {
01040 break;
01041 }
01042
01043 $this->m_dbs->query( 'TRUNCATE TABLE ' . $tmpnew, 'SMW::executeHierarchyQuery' );
01044 $tmpname = $tmpnew;
01045 $tmpnew = $tmpres;
01046 $tmpres = $tmpname;
01047 }
01048
01049 $this->m_hierarchies[$values] = $tablename;
01050 $this->m_dbs->query( ( ( $wgDBtype == 'postgres' ) ? 'DROP TABLE IF EXISTS smw_new' : 'DROP TEMPORARY TABLE smw_new' ), 'SMW::executeHierarchyQuery' );
01051 $this->m_dbs->query( ( ( $wgDBtype == 'postgres' ) ? 'DROP TABLE IF EXISTS smw_res' : 'DROP TEMPORARY TABLE smw_res' ), 'SMW::executeHierarchyQuery' );
01052
01053 wfProfileOut( $fname );
01054 }
01055
01063 protected function applyOrderConditions( $qid ) {
01064 $qobj = $this->m_queries[$qid];
01065
01066 $extraproperties = array();
01067
01068 foreach ( $this->m_sortkeys as $propkey => $order ) {
01069 if ( !array_key_exists( $propkey, $qobj->sortfields ) ) {
01070 if ( $propkey === '' ) {
01071 $qobj->sortfields[$propkey] = "$qobj->alias.smw_sortkey";
01072 } else {
01073 $sortprop = SMWPropertyValue::makeUserProperty( $propkey );
01074
01075 if ( $sortprop->isValid() ) {
01076 $extraproperties[] = new SMWSomeProperty( $sortprop->getDataItem(), new SMWThingDescription() );
01077 }
01078 }
01079 }
01080 }
01081
01082
01083 if ( count( $extraproperties ) > 0 ) {
01084 $desc = new SMWConjunction( $extraproperties );
01085 $newqid = $this->compileQueries( $desc );
01086 $newqobj = $this->m_queries[$newqid];
01087
01088 foreach ( $newqobj->components as $cid => $field ) {
01089 $qobj->components[$cid] = $qobj->joinfield;
01090 $qobj->sortfields = array_merge( $qobj->sortfields, $this->m_queries[$cid]->sortfields );
01091 }
01092
01093 $this->m_queries[$qid] = $qobj;
01094 }
01095 }
01096
01103 protected function getSQLOptions( SMWQuery $query, $rootid ) {
01104 global $smwgQSortingSupport, $smwgQRandSortingSupport;
01105
01106 $result = array( 'LIMIT' => $query->getLimit() + 1, 'OFFSET' => $query->getOffset() );
01107
01108
01109 if ( $smwgQSortingSupport ) {
01110 $qobj = $this->m_queries[$rootid];
01111
01112 foreach ( $this->m_sortkeys as $propkey => $order ) {
01113 if ( ( $order != 'RANDOM' ) && array_key_exists( $propkey, $qobj->sortfields ) ) {
01114 $result['ORDER BY'] = ( array_key_exists( 'ORDER BY', $result ) ? $result['ORDER BY'] . ', ' : '' ) . $qobj->sortfields[$propkey] . " $order ";
01115 } elseif ( ( $order == 'RANDOM' ) && $smwgQRandSortingSupport ) {
01116 $result['ORDER BY'] = ( array_key_exists( 'ORDER BY', $result ) ? $result['ORDER BY'] . ', ' : '' ) . ' RAND() ';
01117 }
01118 }
01119 }
01120 return $result;
01121 }
01122
01128 protected function cleanUp() {
01129 global $wgDBtype;
01130 if ( $this->m_qmode !== SMWQuery::MODE_DEBUG ) {
01131 foreach ( $this->m_querylog as $table => $log ) {
01132 $this->m_dbs->query( ( ( $wgDBtype == 'postgres' ) ? "DROP TABLE IF EXISTS ":"DROP TEMPORARY TABLE " ) . $this->m_dbs->tableName( $table ), 'SMW::getQueryResult' );
01133 }
01134 }
01135 }
01136
01146 protected function getCreateTempIDTableSQL( $tablename ) {
01147 global $wgDBtype;
01148
01149 if ( $wgDBtype == 'postgres' ) {
01150 return "CREATE OR REPLACE FUNCTION create_" . $tablename . "() RETURNS void AS "
01151 . "$$ "
01152 . "BEGIN "
01153 . " IF EXISTS(SELECT NULL FROM pg_tables WHERE tablename='" . $tablename . "' AND schemaname = ANY (current_schemas(true))) "
01154 . " THEN DELETE FROM " . $tablename . "; "
01155 . " ELSE "
01156 . " CREATE TEMPORARY TABLE " . $tablename . " (id INTEGER PRIMARY KEY); "
01157 . " CREATE RULE " . $tablename . "_ignore AS ON INSERT TO " . $tablename . " WHERE (EXISTS (SELECT NULL FROM " . $tablename
01158 . " WHERE (" . $tablename . ".id = new.id))) DO INSTEAD NOTHING; "
01159 . " END IF; "
01160 . "END; "
01161 . "$$ "
01162 . "LANGUAGE 'plpgsql'; "
01163 . "SELECT create_" . $tablename . "(); ";
01164 } else {
01165 return "CREATE TEMPORARY TABLE " . $tablename . "( id INT UNSIGNED KEY ) ENGINE=MEMORY";
01166 }
01167 }
01168
01169 }