r35866 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r35865‎ | r35866 | r35867 >
Date:16:05, 4 June 2008
Author:mkroetzsch
Status:old
Tags:
Comment:
update for new storage engine: full support for category and property hierarchies in queries; new store
almost ready now ...
Modified paths:
  • /trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2.php (modified) (history)
  • /trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2_Queries.php (modified) (history)

Diff [purge]

Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2_Queries.php
@@ -6,11 +6,13 @@
77 * @author Markus Krötzsch
88 */
99
10 -// Types for query descriptions
11 -define('SMW_SQL2_TABLE',1);
12 -define('SMW_SQL2_VALUE',2);
13 -define('SMW_SQL2_DISJUNCTION',7);
14 -define('SMW_SQL2_CONJUNCTION',8);
 10+/// Types for query descriptions (comments refer to SMWSQLStore2Query class below):
 11+define('SMW_SQL2_TABLE',1); // jointable: internal table name, joinfield, components, where use alias.fields, from uses external table names, components interpreted conjunctively (JOIN)
 12+define('SMW_SQL2_VALUE',2); // joinfield (disjunctive) array of unquoted values, jointable empty, components empty
 13+define('SMW_SQL2_DISJUNCTION',3); // joinfield, jointable empty, only components relevant
 14+define('SMW_SQL2_CONJUNCTION',4); // joinfield, jointable empty, only components relevant
 15+define('SMW_SQL2_CLASS_HIERARCHY',5); // only joinfield relevant: (disjunctive) array of unquoted values
 16+define('SMW_SQL2_PROP_HIERARCHY',6); // only joinfield relevant: (disjunctive) array of unquoted values
1517
1618 /**
1719 * Class for representing a single (sub)query description. Simple data
@@ -42,6 +44,8 @@
4345 protected $m_dbs; /// TODO: should temporary tables be created on the master DB?
4446 /// Parent SMWSQLStore2
4547 protected $m_store;
 48+ /// Query mode copied from given query, some submethods act differently when in SMWQuery::MODE_DEBUG
 49+ protected $m_qmode;
4650 /// Array of generated query descriptions
4751 protected $m_queries = array();
4852 /// Array of arrays of executed queries, indexed by the temporary table names results were fed into
@@ -52,6 +56,8 @@
5357 * conditions).
5458 */
5559 protected $m_sortkeys;
 60+ /// Cache of computed hierarchy queries for reuse, cat/prop-value string => tablename
 61+ protected $m_hierarchies = array();
5662
5763 public function __construct(&$parentstore, &$dbslave) {
5864 $this->m_store = $parentstore;
@@ -69,7 +75,9 @@
7076 $result = new SMWQueryResult($query->getDescription()->getPrintrequests(), $query, false);
7177 return $result;
7278 }
 79+ $this->m_qmode = $query->querymode;
7380 $this->m_queries = array();
 81+ $this->m_hierarchies = array();
7482 $this->m_querylog = array();
7583 SMWSQLStore2Query::$qnum = 0;
7684 $this->m_sortkeys = $query->sortkeys;
@@ -88,7 +96,6 @@
8997
9098 $this->applyOrderConditions($query,$rootid); // may extend query if needed for sorting
9199 $this->executeQueries($this->m_queries[$rootid]); // execute query tree, resolve all dependencies
92 - /// TODO: the above needs to know whether we are in debug mode or not
93100 switch ($query->querymode) {
94101 case SMWQuery::MODE_DEBUG:
95102 $result = $this->getDebugQueryResult($query,$rootid);
@@ -101,8 +108,10 @@
102109 break;
103110 }
104111 // finally, free temporary tables
105 - foreach ($this->m_querylog as $table => $log) {
106 - $this->m_dbs->query("DROP TEMPORARY TABLE $table", 'SMW::getQueryResult');
 112+ if ($this->m_qmode !== SMWQuery::MODE_DEBUG) {
 113+ foreach ($this->m_querylog as $table => $log) {
 114+ $this->m_dbs->query("DROP TEMPORARY TABLE $table", 'SMW::getQueryResult');
 115+ }
107116 }
108117 return $result;
109118 }
@@ -116,16 +125,17 @@
117126 $sql_options = $this->getSQLOptions($query,$rootid);
118127 list( $startOpts, $useIndex, $tailOpts ) = $this->m_dbs->makeSelectOptions( $sql_options );
119128 $result = '<div style="border: 1px dotted black; background: #A1FB00; padding: 20px; ">' .
120 - '<b>Generated Wiki-Query</b><br />' .
121 - str_replace('[', '&#x005B;', $query->getDescription()->getQueryString()) . '<br />' .
122 - '<b>Query-Size: </b>' . $query->getDescription()->getSize() . '<br />' .
123 - '<b>Query-Depth: </b>' . $query->getDescription()->getDepth() . '<br />';
 129+ '<b>Debug output by SMWSQLStore2</b><br />' .
 130+ 'Generated Wiki-Query<br /><tt>' .
 131+ str_replace('[', '&#x005B;', $query->getDescription()->getQueryString()) . '</tt><br />' .
 132+ 'Query-Size: ' . $query->getDescription()->getSize() . '<br />' .
 133+ 'Query-Depth: ' . $query->getDescription()->getDepth() . '<br />';
124134 if ($qobj->joinfield !== '') {
125 - $result .= '<b>SQL query</b><br />' .
126 - "SELECT DISTINCT $qobj->alias.smw_title AS t,$qobj->alias.smw_namespace AS ns FROM " .
127 - "$qobj->jointable AS $qobj->alias" . $qobj->from . (($qobj->where=='')?'':' WHERE ') .
128 - $qobj->where . "$tailOpts LIMIT " . $sql_options['LIMIT'] . ' OFFSET ' .
129 - $sql_options['OFFSET'] . ';';
 135+ $result .= 'SQL query<br />' .
 136+ "<tt>SELECT DISTINCT $qobj->alias.smw_title AS t,$qobj->alias.smw_namespace AS ns FROM " .
 137+ $this->m_dbs->tableName($qobj->jointable) . " AS $qobj->alias" . $qobj->from .
 138+ (($qobj->where=='')?'':' WHERE ') . $qobj->where . "$tailOpts LIMIT " .
 139+ $sql_options['LIMIT'] . ' OFFSET ' . $sql_options['OFFSET'] . ';</tt>';
130140 } else {
131141 $result .= '<b>Empty result, no SQL query created.</b>';
132142 }
@@ -133,15 +143,16 @@
134144 foreach ($query->getErrors() as $error) {
135145 $errors .= $error . '<br />';
136146 }
137 - $result .= ($errors)?"<br /><b>Errors and warnings:</b><br />$errors":'<br /><b>No errors or warnings.</b>';
 147+ $result .= ($errors)?"<br />Errors and warnings:<br />$errors":'<br />No errors or warnings.';
138148 $auxtables = '';
139149 foreach ($this->m_querylog as $table => $log) {
140 - $auxtables .= "\n\n<b>Temporary table $table</b>";
 150+ $auxtables .= "<li>Temporary table $table";
141151 foreach ($log as $q) {
142 - $auxtables .= "\n\n$q";
 152+ $auxtables .= "<br />&nbsp;&nbsp;<tt>$q</tt>";
143153 }
 154+ $auxtables .= '</li>';
144155 }
145 - $result .= ($auxtables)?"<br /><b>Auxilliary tables used:</b><br />$auxtables":'<br /><b>No auxilliary tables used.</b>';
 156+ $result .= ($auxtables)?"<br />Auxilliary tables used:<ul>$auxtables</ul>":'<br />No auxilliary tables used.';
146157 $result .= '</div>';
147158 return $result;
148159 }
@@ -241,15 +252,24 @@
242253 $typeid = SMWDataValueFactory::getPropertyObjectTypeID($description->getProperty());
243254 $query->joinfield = "$query->alias.s_id";
244255 $pid = $this->m_store->getSMWPageID($description->getProperty()->getDBkey(), $description->getProperty()->getNamespace(),'');
245 - $query->where = "$query->alias.p_id=" . $this->m_dbs->addQuotes($pid);
 256+ $pqid = SMWSQLStore2Query::$qnum;
 257+ $pquery = new SMWSQLStore2Query();
 258+ $pquery->type = SMW_SQL2_PROP_HIERARCHY;
 259+ $pquery->joinfield = array($pid);
 260+ $query->components[$pqid] = "$query->alias.p_id";
 261+ $this->m_queries[$pqid] = $pquery;
246262 $sortfield = ''; // used if we should sort by this property
247263 switch ($typeid) {
248264 case '_wpg': case '__nry': // subconditions as subqueries (compiled)
249265 $query->jointable = 'smw_rels2';
250266 $sub = $this->compileQueries($description->getDescription());
251267 if ($sub >= 0) {
252 - $query->components = array($sub => "$query->alias.o_id");
 268+ $query->components[$sub] = "$query->alias.o_id";
253269 }
 270+ if ( array_key_exists($description->getProperty()->getDBkey(), $this->m_sortkeys) ) {
 271+ $query->from = ' INNER JOIN ' . $this->m_dbs->tableName('smw_ids') . " AS ids$query->alias ON ids$query->alias.smw_id=$query->alias.o_id";
 272+ $sortfield = "ids$query->alias.smw_title"; /// TODO: as below, smw_ids here is possibly duplicated! Can we prevent that? (PERFORMANCE)
 273+ }
254274 break;
255275 case '_txt': // no subconditions
256276 $query->jointable = 'smw_text2';
@@ -258,7 +278,7 @@
259279 $query->jointable = 'smw_atts2';
260280 $aw = $this->compileAttributeWhere($description->getDescription(),"$query->alias");
261281 if ($aw != '') {
262 - $query->where .= " AND $aw";
 282+ $query->where .= ($query->where?' AND ':'') . $aw;
263283 }
264284 if ( array_key_exists($description->getProperty()->getDBkey(), $this->m_sortkeys) ) {
265285 $sortfield = "$query->alias." . (SMWDataValueFactory::newTypeIDValue($typeid)->isNumeric()?'value_num':'value_xsd');
@@ -280,17 +300,26 @@
281301 }
282302 }
283303 } elseif ($description instanceof SMWClassDescription) {
284 - $query->jointable = 'smw_inst2';
285 - $query->joinfield = "$query->alias.s_id";
286 - $where = '';
 304+ $cqid = SMWSQLStore2Query::$qnum;
 305+ $cquery = new SMWSQLStore2Query();
 306+ $cquery->type = SMW_SQL2_CLASS_HIERARCHY;
 307+ $cquery->joinfield = array();
287308 foreach ($description->getCategories() as $cat) {
288309 $cid = $this->m_store->getSMWPageID($cat->getDBkey(), NS_CATEGORY, '');
289 - $where .= ($where == ''?'':' OR ') . "$query->alias.o_id=" . $this->m_dbs->addQuotes($cid);
 310+ if ($cid != 0) {
 311+ $cquery->joinfield[] = $cid;
 312+ }
290313 }
291 - if (count($description->getCategories()) > 1) {
292 - $where = "($where)";
 314+ if (count($cquery->joinfield) == 0) { // empty result
 315+ $query->type = SMW_SQL2_VALUE;
 316+ $query->jointable = '';
 317+ $query->joinfield = '';
 318+ } else { // instance query with dicjunction of classes (categories)
 319+ $query->jointable = 'smw_inst2';
 320+ $query->joinfield = "$query->alias.s_id";
 321+ $query->components[$cqid] = "$query->alias.o_id";
 322+ $this->m_queries[$cqid] = $cquery;
293323 }
294 - $query->where = $where;
295324 } elseif ($description instanceof SMWValueList) {
296325 $qid = -1; /// TODO
297326 } elseif ($description instanceof SMWValueDescription) { // only processsed here for '_wpg'
@@ -298,7 +327,7 @@
299328 if ($description->getComparator() == SMW_CMP_EQ) {
300329 $query->type = SMW_SQL2_VALUE;
301330 $oid = $this->m_store->getSMWPageID($description->getDatavalue()->getDBkey(), $description->getDatavalue()->getNamespace(),'');
302 - $query->joinfield = $oid;
 331+ $query->joinfield = array($oid);
303332 } else { // join with smw_ids needed for other comparators (apply to title string)
304333 $query->jointable = 'smw_ids';
305334 $query->joinfield = "$query->alias.smw_id";
@@ -378,9 +407,16 @@
379408 $subquery = $this->m_queries[$qid];
380409 $this->executeQueries($subquery);
381410 if ($subquery->jointable != '') { // join with jointable.joinfield
382 - $query->from .= ' INNER JOIN ' . $subquery->jointable . " AS $subquery->alias ON $joinfield=" . $subquery->joinfield;
 411+ $query->from .= ' INNER JOIN ' . $this->m_dbs->tableName($subquery->jointable) . " AS $subquery->alias ON $joinfield=" . $subquery->joinfield;
383412 } elseif ($subquery->joinfield !== '') { // require joinfield as "value" via WHERE
384 - $query->where .= (($query->where == '')?'':' AND ') . "$joinfield=" . $subquery->joinfield;
 413+ $condition = '';
 414+ foreach ($subquery->joinfield as $value) {
 415+ $condition .= ($condition?' OR ':'') . "$joinfield=" . $this->m_dbs->addQuotes($value);
 416+ }
 417+ if (count($subquery->joinfield) > 1) {
 418+ $condition = "($condition)";
 419+ }
 420+ $query->where .= (($query->where == '')?'':' AND ') . $condition;
385421 } else { // interpret empty joinfields as impossible condition (empty result)
386422 $query->joinfield = ''; // make whole query false
387423 $query->jointable = '';
@@ -428,23 +464,32 @@
429465 $query = $result;
430466 break;
431467 case SMW_SQL2_DISJUNCTION:
432 - $this->m_dbs->query( "CREATE TEMPORARY TABLE $query->alias" .
433 - ' ( id INT UNSIGNED KEY ) TYPE=MEMORY', 'SMW::executeQueries' );
 468+ if ($this->m_qmode !== SMWQuery::MODE_DEBUG) {
 469+ $this->m_dbs->query( "CREATE TEMPORARY TABLE " . $this->m_dbs->tableName($query->alias) .
 470+ ' ( id INT UNSIGNED KEY ) TYPE=MEMORY', 'SMW::executeQueries' );
 471+ }
434472 $this->m_querylog[$query->alias] = array();
435473 foreach ($query->components as $qid => $joinfield) {
436474 $subquery = $this->m_queries[$qid];
437475 $this->executeQueries($subquery);
438476 $sql = '';
439477 if ($subquery->jointable != '') {
440 - $sql = "INSERT IGNORE INTO $query->alias SELECT $subquery->joinfield FROM $subquery->jointable AS $subquery->alias $subquery->from WHERE $subquery->where ";
 478+ $sql = "INSERT IGNORE INTO $query->alias SELECT $subquery->joinfield FROM " .
 479+ $this->m_dbs->tableName($subquery->jointable) . " AS $subquery->alias $subquery->from" . ($subquery->where?" WHERE $subquery->where":'');
441480 } elseif ($subquery->joinfield !== '') {
442481 /// NOTE: this works only for single "unconditional" values without further
443482 /// WHERE or FROM. The execution must take care of not creating any others.
444 - $sql = "INSERT IGNORE INTO $query->alias (id) VALUES (" . $this->m_dbs->addQuotes($subquery->joinfield) . ')';
 483+ $values = '';
 484+ foreach ($subquery->joinfield as $value) {
 485+ $values .= ($values?',':'') . '(' . $this->m_dbs->addQuotes($value) . ')';
 486+ }
 487+ $sql = "INSERT IGNORE INTO $query->alias (id) VALUES $values";
445488 } // else: // interpret empty joinfields as impossible condition (empty result), ignore
446489 if ($sql) {
447490 $this->m_querylog[$query->alias][] = $sql;
448 - $this->m_dbs->query($sql , 'SMW::executeQueries');
 491+ if ($this->m_qmode !== SMWQuery::MODE_DEBUG) {
 492+ $this->m_dbs->query($sql , 'SMW::executeQueries');
 493+ }
449494 }
450495 }
451496 $query->jointable = $query->alias;
@@ -452,146 +497,72 @@
453498 $query->sortfields = array(); // make sure we got no sortfields
454499 /// TODO: currently this eliminates sortkeys, possibly keep them (needs different temp table format though, maybe not such a good thing to do)
455500 break;
456 - case SMW_SQL2_VALUE: break; // nothing to do
457 - }
458 - }
 501+ case SMW_SQL2_PROP_HIERARCHY: case SMW_SQL2_CLASS_HIERARCHY: // make a saturated hierarchy
 502+ global $smwgQSubpropertyDepth, $smwgQSubcategoryDepth;
 503+ $depth = ($query->type == SMW_SQL2_PROP_HIERARCHY)?$smwgQSubpropertyDepth:$smwgQSubcategoryDepth;
 504+ if ($depth <= 0) { // treat as value, no recursion
 505+ $query->type = SMW_SQL2_VALUE;
 506+ } else {
 507+ $values = '';
 508+ foreach ($query->joinfield as $value) {
 509+ $values .= ($values?',':'') . '(' . $this->m_dbs->addQuotes($value) . ')';
 510+ }
 511+ $tablename = $this->m_dbs->tableName($query->alias);
 512+ $this->m_querylog[$query->alias] = array("Recursively computed hierarchy for element(s) $values.");
 513+ $query->jointable = $query->alias;
 514+ $query->joinfield = "$query->alias.id";
 515+ if ($this->m_qmode == SMWQuery::MODE_DEBUG) {
 516+ break; // no real queries in debug mode
 517+ }
 518+ $this->m_dbs->query( "CREATE TEMPORARY TABLE $query->alias" .
 519+ ' ( id INT UNSIGNED KEY ) TYPE=MEMORY', 'SMW::executeQueries' );
 520+ if (array_key_exists($values, $this->m_hierarchies)) { // just copy known result
 521+ $this->m_dbs->query("INSERT INTO $tablename (id) SELECT id" .
 522+ ' FROM ' . $this->m_hierarchies[$values],
 523+ 'SMW::executeQueries');
 524+ break;
 525+ }
 526+ /// NOTE: we use two helper tables. One holds the results of each new iteration, one holds the
 527+ /// results of the previous iteration. One could of course do with only the above result table,
 528+ /// but then every iteration would use all elements of this table, while only the new ones
 529+ /// obtained in the previous step are relevant. So this is a performance measure.
 530+ $tmpnew = 'smw_new';
 531+ $tmpres = 'smw_res';
 532+ $this->m_dbs->query( "CREATE TEMPORARY TABLE $tmpnew " .
 533+ '( id INT UNSIGNED NOT NULL ) TYPE=MEMORY', 'SMW::executeQueries' );
 534+ $this->m_dbs->query( "CREATE TEMPORARY TABLE $tmpres " .
 535+ '( id INT UNSIGNED NOT NULL ) TYPE=MEMORY', 'SMW::executeQueries' );
459536
 537+ $smw_subs2 = $this->m_dbs->tableName('smw_subs2');
 538+ $this->m_dbs->query("INSERT IGNORE INTO $tablename (id) VALUES $values", 'SMW::executeQueries');
 539+ $this->m_dbs->query("INSERT IGNORE INTO $tmpnew (id) VALUES $values", 'SMW::executeQueries');
460540
461 - /**
462 - * Make a (temporary) table that contains the lower closure of the given category
463 - * wrt. the category table.
464 - */
465 - protected function getCategoryTable($cats, &$db) {
466 - wfProfileIn("SMWSQLStore2::getCategoryTable (SMW)");
467 - global $smwgQSubcategoryDepth;
468 -
469 - $sqlvalues = '';
470 - $hashkey = '';
471 - foreach ($cats as $cat) {
472 - if ($sqlvalues != '') {
473 - $sqlvalues .= ', ';
474 - }
475 - $sqlvalues .= '(' . $db->addQuotes($cat->getDBkey()) . ')';
476 - $hashkey .= ']' . $cat->getDBkey();
 541+ for ($i=0; $i<$depth; $i++) {
 542+ $this->m_dbs->query("INSERT INTO $tmpres (id) SELECT s_id FROM $smw_subs2,$tmpnew WHERE o_id=id",
 543+ 'SMW::executeQueries');
 544+ if ($this->m_dbs->affectedRows() == 0) { // no change, exit loop
 545+ break;
 546+ }
 547+ $this->m_dbs->query("INSERT IGNORE INTO $tablename (id) SELECT $tmpres.id FROM $tmpres",
 548+ 'SMW::executeQueries');
 549+ if ($this->m_dbs->affectedRows() == 0) { // no change, exit loop
 550+ break;
 551+ }
 552+ $this->m_dbs->query('TRUNCATE TABLE ' . $tmpnew, 'SMW::executeQueries'); // empty "new" table
 553+ $tmpname = $tmpnew;
 554+ $tmpnew = $tmpres;
 555+ $tmpres = $tmpname;
 556+ }
 557+ $this->m_hierarchies[$values] = $tablename;
 558+ $this->m_dbs->query('DROP TEMPORARY TABLE smw_new', 'SMW::executeQueries');
 559+ $this->m_dbs->query('DROP TEMPORARY TABLE smw_res', 'SMW::executeQueries');
 560+ }
 561+ break;
 562+ case SMW_SQL2_VALUE: break; // nothing to do
477563 }
478 -
479 - $tablename = 'cats' . SMWSQLStore2::$m_tablenum++;
480 - $this->m_usedtables[] = $tablename;
481 - // TODO: unclear why this commit is needed -- is it a MySQL 4.x problem?
482 - $db->query("COMMIT");
483 - $db->query( 'CREATE TEMPORARY TABLE ' . $tablename .
484 - '( title VARCHAR(255) binary NOT NULL PRIMARY KEY)
485 - TYPE=MEMORY', 'SMW::getCategoryTable' );
486 - if (array_key_exists($hashkey, SMWSQLStore2::$m_categorytables)) { // just copy known result
487 - $db->query("INSERT INTO $tablename (title) SELECT " .
488 - SMWSQLStore2::$m_categorytables[$hashkey] .
489 - '.title FROM ' . SMWSQLStore2::$m_categorytables[$hashkey],
490 - 'SMW::getCategoryTable');
491 - wfProfileOut("SMWSQLStore2::getCategoryTable (SMW)");
492 - return $tablename;
493 - }
494 -
495 - // Create multiple temporary tables for recursive computation
496 - $db->query( 'CREATE TEMPORARY TABLE smw_newcats
497 - ( title VARCHAR(255) binary NOT NULL )
498 - TYPE=MEMORY', 'SMW::getCategoryTable' );
499 - $db->query( 'CREATE TEMPORARY TABLE smw_rescats
500 - ( title VARCHAR(255) binary NOT NULL )
501 - TYPE=MEMORY', 'SMW::getCategoryTable' );
502 - $tmpnew = 'smw_newcats';
503 - $tmpres = 'smw_rescats';
504 -
505 - $pagetable = $db->tableName('page');
506 - $cltable = $db->tableName('categorylinks');
507 - $db->query("INSERT INTO $tablename (title) VALUES " . $sqlvalues, 'SMW::getCategoryTable');
508 - $db->query("INSERT INTO $tmpnew (title) VALUES " . $sqlvalues, 'SMW::getCategoryTable');
509 -
510 - for ($i=0; $i<$smwgQSubcategoryDepth; $i++) {
511 - $db->query("INSERT INTO $tmpres (title) SELECT $pagetable.page_title
512 - FROM $cltable,$pagetable,$tmpnew WHERE
513 - $cltable.cl_to=$tmpnew.title AND
514 - $pagetable.page_namespace=" . NS_CATEGORY . " AND
515 - $pagetable.page_id=$cltable.cl_from", 'SMW::getCategoryTable');
516 - $db->query("INSERT IGNORE INTO $tablename (title) SELECT $tmpres.title
517 - FROM $tmpres", 'SMW::getCategoryTable');
518 - if ($db->affectedRows() == 0) { // no change, exit loop
519 - break;
520 - }
521 - $db->query('TRUNCATE TABLE ' . $tmpnew, 'SMW::getCategoryTable'); // empty "new" table
522 - $tmpname = $tmpnew;
523 - $tmpnew = $tmpres;
524 - $tmpres = $tmpname;
525 - }
526 -
527 - SMWSQLStore2::$m_categorytables[$hashkey] = $tablename;
528 - $db->query('DROP TEMPORARY TABLE smw_newcats', 'SMW::getCategoryTable');
529 - $db->query('DROP TEMPORARY TABLE smw_rescats', 'SMW::getCategoryTable');
530 - wfProfileOut("SMWSQLStore2::getCategoryTable (SMW)");
531 - return $tablename;
532564 }
533565
534566 /**
535 - * Make a (temporary) table that contains the lower closure of the given property
536 - * wrt. the subproperty relation.
537 - */
538 - protected function getPropertyTable($propname, &$db) {
539 - wfProfileIn("SMWSQLStore2::getPropertyTable (SMW)");
540 - global $smwgQSubpropertyDepth;
541 -
542 - $tablename = 'prop' . SMWSQLStore2::$m_tablenum++;
543 - $this->m_usedtables[] = $tablename;
544 - $db->query( 'CREATE TEMPORARY TABLE ' . $tablename .
545 - '( title VARCHAR(255) binary NOT NULL PRIMARY KEY)
546 - TYPE=MEMORY', 'SMW::getPropertyTable' );
547 - if (array_key_exists($propname, SMWSQLStore2::$m_propertytables)) { // just copy known result
548 - $db->query("INSERT INTO $tablename (title) SELECT " .
549 - SMWSQLStore2::$m_propertytables[$propname] .
550 - '.title FROM ' . SMWSQLStore2::$m_propertytables[$propname],
551 - 'SMW::getPropertyTable');
552 - wfProfileOut("SMWSQLStore2::getPropertyTable (SMW)");
553 - return $tablename;
554 - }
555 -
556 - // Create multiple temporary tables for recursive computation
557 - $db->query( 'CREATE TEMPORARY TABLE smw_new
558 - ( title VARCHAR(255) binary NOT NULL )
559 - TYPE=MEMORY', 'SMW::getPropertyTable' );
560 - $db->query( 'CREATE TEMPORARY TABLE smw_res
561 - ( title VARCHAR(255) binary NOT NULL )
562 - TYPE=MEMORY', 'SMW::getPropertyTable' );
563 - $tmpnew = 'smw_new';
564 - $tmpres = 'smw_res';
565 -
566 - $sptable = $db->tableName('smw_subprops');
567 - $db->query("INSERT INTO $tablename (title) VALUES (" . $db->addQuotes($propname) . ')', 'SMW::getPropertyTable');
568 - $db->query("INSERT INTO $tmpnew (title) VALUES (" . $db->addQuotes($propname) . ')', 'SMW::getPropertyTable');
569 -
570 - for ($i=0; $i<$smwgQSubpropertyDepth; $i++) {
571 - $db->query("INSERT INTO $tmpres (title) SELECT $sptable.subject_title
572 - FROM $sptable,$tmpnew WHERE
573 - $sptable.object_title=$tmpnew.title", 'SMW::getPropertyTable');
574 - if ($db->affectedRows() == 0) { // no change, exit loop
575 - break;
576 - }
577 - $db->query("INSERT IGNORE INTO $tablename (title) SELECT $tmpres.title
578 - FROM $tmpres", 'SMW::getPropertyTable');
579 - if ($db->affectedRows() == 0) { // no change, exit loop
580 - break;
581 - }
582 - $db->query('TRUNCATE TABLE ' . $tmpnew, 'SMW::getPropertyTable'); // empty "new" table
583 - $tmpname = $tmpnew;
584 - $tmpnew = $tmpres;
585 - $tmpres = $tmpname;
586 - }
587 -
588 - SMWSQLStore2::$m_propertytables[$propname] = $tablename;
589 - $db->query('DROP TEMPORARY TABLE smw_new', 'SMW::getPropertyTable');
590 - $db->query('DROP TEMPORARY TABLE smw_res', 'SMW::getPropertyTable');
591 - wfProfileOut("SMWSQLStore2::getPropertyTable (SMW)");
592 - return $tablename;
593 - }
594 -
595 - /**
596567 * This function modifies the given query object at $qid to account for all ordering conditions
597568 * in the SMWQuery $query. It is always required that $qid is the id of a query that joins with
598569 * smw_ids so that the field alias.smw_title is $available for default sorting.
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2.php
@@ -53,7 +53,7 @@
5454 $result = NULL;
5555 }
5656 if ($sid == 0) { // no data, save our time
57 - // NOTE: we consider redirects for getting $sid, so $sid == 0 also means "no redirects"
 57+ /// NOTE: we consider redirects for getting $sid, so $sid == 0 also means "no redirects"
5858 wfProfileOut("SMWSQLStore2::getSemanticData (SMW)");
5959 return isset($stitle)?(new SMWSemanticData($stitle)):NULL;
6060 }
@@ -67,7 +67,9 @@
6868 case '__nry': $tasks = $tasks | SMW_SQL2_NARY2; break;
6969 case SMW_SP_INSTANCE_OF: $tasks = $tasks | SMW_SQL2_INST2; break;
7070 case SMW_SP_REDIRECTS_TO: $tasks = $tasks | SMW_SQL2_REDI2; break;
71 - case SMW_SP_SUBPROPERTY_OF: $tasks = $tasks | SMW_SQL2_SUBS2; break;
 71+ case SMW_SP_SUBPROPERTY_OF: case SMW_SP_SUBCLASS_OF:
 72+ $tasks = $tasks | SMW_SQL2_SUBS2;
 73+ break;
7274 default:
7375 if (is_numeric($value)) { // some special property
7476 $tasks = $tasks | SMW_SQL2_SPEC2;
@@ -79,7 +81,7 @@
8082 } else {
8183 $tasks = SMW_SQL2_RELS2 | SMW_SQL2_ATTS2 | SMW_SQL2_TEXT2| SMW_SQL2_SPEC2 | SMW_SQL2_NARY2 | SMW_SQL2_SUBS2 | SMW_SQL2_INST2 | SMW_SQL2_REDI2;
8284 }
83 - if ($subject->getNamespace() != SMW_NS_PROPERTY) {
 85+ if ( ($subject->getNamespace() != SMW_NS_PROPERTY) && ($subject->getNamespace() != NS_CATEGORY) ) {
8486 $tasks = $tasks & ~SMW_SQL2_SUBS2;
8587 }
8688
@@ -96,25 +98,16 @@
9799 $this->m_sdstate = array($sid => $this->m_sdstate[$sid]);
98100 }
99101
100 - // relations need a different kind of DB call
101 - if ($tasks & SMW_SQL2_RELS2) {
102 - // Sorry, no DB wrapper method supports "AS", using query()
103 - $res = $db->query( 'SELECT p.smw_title AS ptitle, o.smw_title AS otitle, o.smw_namespace AS onamespace FROM ' . $db->tableName('smw_rels2') . ' INNER JOIN ' . $db->tableName('smw_ids') . ' AS p ON p_id=p.smw_id INNER JOIN ' . $db->tableName('smw_ids') . ' AS o ON o_id=o.smw_id WHERE s_id=' . $db->addQuotes($sid), 'SMW::getSemanticData' );
104 - while($row = $db->fetchObject($res)) {
105 - $property = Title::makeTitle(SMW_NS_PROPERTY, $row->ptitle);
106 - $dv = SMWDataValueFactory::newPropertyObjectValue($property);
107 - if ($dv instanceof SMWWikiPagevalue) { // may fail if type was changed!
108 - $dv->setValues($row->otitle, $row->onamespace);
109 - $this->m_semdata[$sid]->addPropertyObjectValue($property, $dv);
110 - }
111 - }
112 - $db->freeResult($res);
113 - }
114 - // most other types of data suggest rather similar code
115 - foreach (array(SMW_SQL2_ATTS2, SMW_SQL2_TEXT2, SMW_SQL2_INST2, SMW_SQL2_SUBS2, SMW_SQL2_SPEC2, SMW_SQL2_REDI2) as $task) {
 102+ // most types of data suggest rather similar code
 103+ foreach (array(SMW_SQL2_RELS2, SMW_SQL2_ATTS2, SMW_SQL2_TEXT2, SMW_SQL2_INST2, SMW_SQL2_SUBS2, SMW_SQL2_SPEC2, SMW_SQL2_REDI2) as $task) {
116104 if ( !($tasks & $task) ) continue;
117105 $where = 'p_id=smw_id AND s_id=' . $db->addQuotes($sid);
118106 switch ($task) {
 107+ case SMW_SQL2_RELS2:
 108+ $from = $db->tableName('smw_rels2') . ' INNER JOIN ' . $db->tableName('smw_ids') . ' AS p ON p_id=p.smw_id INNER JOIN ' . $db->tableName('smw_ids') . ' AS o ON o_id=o.smw_id';
 109+ $select = 'p.smw_title as prop, o.smw_title as title, o.smw_namespace as namespace, o.smw_iw as iw';
 110+ $where = 's_id=' . $db->addQuotes($sid);
 111+ break;
119112 case SMW_SQL2_ATTS2:
120113 $from = array('smw_atts2','smw_ids');
121114 $select = 'smw_title as prop, value_unit as unit, value_xsd as value';
@@ -132,6 +125,8 @@
133126 $from = array('smw_subs2','smw_ids');
134127 $select = 'smw_title as value';
135128 $where = 'o_id=smw_id AND s_id=' . $db->addQuotes($sid);
 129+ $namespace = $subject->getNamespace();
 130+ $specprop = ($namespace==NS_CATEGORY)?SMW_SP_SUBCLASS_OF:SMW_SP_SUBPROPERTY_OF;
136131 break;
137132 case SMW_SQL2_REDI2:
138133 $from = array('smw_redi2','smw_ids');
@@ -147,7 +142,7 @@
148143 }
149144 $res = $db->select( $from, $select, $where, 'SMW::getSemanticData' );
150145 while($row = $db->fetchObject($res)) {
151 - if ($task & (SMW_SQL2_ATTS2 | SMW_SQL2_TEXT2) ) {
 146+ if ($task & (SMW_SQL2_RELS2 | SMW_SQL2_ATTS2 | SMW_SQL2_TEXT2) ) {
152147 $property = Title::makeTitle(SMW_NS_PROPERTY, $row->prop);
153148 $dv = SMWDataValueFactory::newPropertyObjectValue($property);
154149 } elseif ($task == SMW_SQL2_SPEC2) {
@@ -155,7 +150,12 @@
156151 } else {
157152 $dv = SMWDataValueFactory::newTypeIDValue('_wpg');
158153 }
159 - if ($task == SMW_SQL2_ATTS2) {
 154+ if ($task == SMW_SQL2_RELS2) {
 155+ if ($dv instanceof SMWWikiPagevalue) { // may fail if type was changed!
 156+ $dv->setValues($row->title, $row->namespace);
 157+ $this->m_semdata[$sid]->addPropertyObjectValue($property, $dv);
 158+ }
 159+ } elseif ($task == SMW_SQL2_ATTS2) {
160160 $dv->setXSDValue($row->value, $row->unit);
161161 $this->m_semdata[$sid]->addPropertyObjectValue($property, $dv);
162162 } elseif ($task == SMW_SQL2_TEXT2) {
@@ -165,8 +165,8 @@
166166 $dv->setXSDValue($row->value);
167167 $this->m_semdata[$sid]->addSpecialValue($row->prop, $dv);
168168 } elseif ($task == SMW_SQL2_SUBS2) {
169 - $dv->setValues($row->value, SMW_NS_PROPERTY);
170 - $this->m_semdata[$sid]->addSpecialValue(SMW_SP_SUBPROPERTY_OF, $dv);
 169+ $dv->setValues($row->value, $namespace);
 170+ $this->m_semdata[$sid]->addSpecialValue($specprop, $dv);
171171 } elseif ($task == SMW_SQL2_REDI2) {
172172 $dv->setValues($row->title, $row->namespace);
173173 $this->m_semdata[$sid]->addSpecialValue(SMW_SP_REDIRECTS_TO, $dv);
@@ -293,14 +293,16 @@
294294 }
295295 $db->freeResult($res);
296296 }
297 - } elseif ($specialprop === SMW_SP_SUBPROPERTY_OF) { // subproperties
298 - $oid = $this->getSMWPageID($value->getDBkey(),SMW_NS_PROPERTY,'');
299 - if ( ($oid != 0) && ($value->getNamespace() == SMW_NS_PROPERTY) ) {
 297+ } elseif ( ($specialprop === SMW_SP_SUBPROPERTY_OF) || ($specialprop === SMW_SP_CLASS_OF) ) {
 298+ // subproperties/subclasses
 299+ $namespace = ($specialprop === SMW_SP_CLASS_OF)?NS_CATEGORY:SMW_NS_PROPERTY;
 300+ $oid = $this->getSMWPageID($value->getDBkey(),$namespace,'');
 301+ if ( ($oid != 0) && ($value->getNamespace() == $namespace) ) {
300302 $res = $db->select( array('smw_subs2','smw_ids'), 'smw_title',
301303 's_id=smw_id AND o_id=' . $db->addQuotes($oid),
302304 'SMW::getSpecialSubjects', $this->getSQLOptions($requestoptions) );
303305 while($row = $db->fetchObject($res)) {
304 - $result[] = Title::makeTitle(SMW_NS_PROPERTY, $row->smw_title);
 306+ $result[] = Title::makeTitle($namespace, $row->smw_title);
305307 }
306308 $db->freeResult($res);
307309 }
@@ -352,7 +354,7 @@
353355 }
354356
355357 function getPropertySubjects(Title $property, $value, $requestoptions = NULL) {
356 - /// TODO: could we share code with #ask query computation here? Just use queries?
 358+ /// TODO: should we share code with #ask query computation here? Just use queries?
357359 wfProfileIn("SMWSQLStore2::getPropertySubjects (SMW)");
358360 $result = array();
359361 $pid = $this->getSMWPageID($property->getDBkey(), $property->getNamespace(),'');
@@ -595,9 +597,9 @@
596598 }
597599 } else { // special property
598600 switch ($property) {
599 - case SMW_SP_IMPORTED_FROM: case SMW_SP_REDIRECTS_TO:
600 - // don't store this, just used for display;
 601+ case SMW_SP_IMPORTED_FROM: // don't store this, just used for display;
601602 /// TODO: filtering here is bad for fully neglected properties (IMPORTED FROM)
 603+ case SMW_SP_REDIRECTS_TO: // handled by updateRedirects above
602604 break;
603605 case SMW_SP_INSTANCE_OF:
604606 foreach($propertyValueArray as $value) {
@@ -608,12 +610,13 @@
609611 }
610612 }
611613 break;
612 - case SMW_SP_SUBPROPERTY_OF:
613 - if ( $subject->getNamespace() != SMW_NS_PROPERTY ) {
 614+ case SMW_SP_SUBPROPERTY_OF: case SMW_SP_SUBCLASS_OF:
 615+ $namespace = ($property==SMW_SP_SUBPROPERTY_OF)?SMW_NS_PROPERTY:NS_CATEGORY;
 616+ if ( $subject->getNamespace() != $namespace ) {
614617 break;
615618 }
616619 foreach($propertyValueArray as $value) {
617 - if ( $value->getNamespace() == SMW_NS_PROPERTY ) {
 620+ if ( $value->getNamespace() == $namespace ) {
618621 $up_subs2[] =
619622 array('s_id' => $this->makeSMWPageID($subject->getDBkey(),$subject->getNamespace(),''),
620623 'o_id' => $this->makeSMWPageID($value->getDBkey(),$value->getNamespace(),''));

Status & tagging log