Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2_Queries.php |
— | — | @@ -6,11 +6,13 @@ |
7 | 7 | * @author Markus Krötzsch |
8 | 8 | */ |
9 | 9 | |
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 |
15 | 17 | |
16 | 18 | /** |
17 | 19 | * Class for representing a single (sub)query description. Simple data |
— | — | @@ -42,6 +44,8 @@ |
43 | 45 | protected $m_dbs; /// TODO: should temporary tables be created on the master DB? |
44 | 46 | /// Parent SMWSQLStore2 |
45 | 47 | protected $m_store; |
| 48 | + /// Query mode copied from given query, some submethods act differently when in SMWQuery::MODE_DEBUG |
| 49 | + protected $m_qmode; |
46 | 50 | /// Array of generated query descriptions |
47 | 51 | protected $m_queries = array(); |
48 | 52 | /// Array of arrays of executed queries, indexed by the temporary table names results were fed into |
— | — | @@ -52,6 +56,8 @@ |
53 | 57 | * conditions). |
54 | 58 | */ |
55 | 59 | protected $m_sortkeys; |
| 60 | + /// Cache of computed hierarchy queries for reuse, cat/prop-value string => tablename |
| 61 | + protected $m_hierarchies = array(); |
56 | 62 | |
57 | 63 | public function __construct(&$parentstore, &$dbslave) { |
58 | 64 | $this->m_store = $parentstore; |
— | — | @@ -69,7 +75,9 @@ |
70 | 76 | $result = new SMWQueryResult($query->getDescription()->getPrintrequests(), $query, false); |
71 | 77 | return $result; |
72 | 78 | } |
| 79 | + $this->m_qmode = $query->querymode; |
73 | 80 | $this->m_queries = array(); |
| 81 | + $this->m_hierarchies = array(); |
74 | 82 | $this->m_querylog = array(); |
75 | 83 | SMWSQLStore2Query::$qnum = 0; |
76 | 84 | $this->m_sortkeys = $query->sortkeys; |
— | — | @@ -88,7 +96,6 @@ |
89 | 97 | |
90 | 98 | $this->applyOrderConditions($query,$rootid); // may extend query if needed for sorting |
91 | 99 | $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 |
93 | 100 | switch ($query->querymode) { |
94 | 101 | case SMWQuery::MODE_DEBUG: |
95 | 102 | $result = $this->getDebugQueryResult($query,$rootid); |
— | — | @@ -101,8 +108,10 @@ |
102 | 109 | break; |
103 | 110 | } |
104 | 111 | // 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 | + } |
107 | 116 | } |
108 | 117 | return $result; |
109 | 118 | } |
— | — | @@ -116,16 +125,17 @@ |
117 | 126 | $sql_options = $this->getSQLOptions($query,$rootid); |
118 | 127 | list( $startOpts, $useIndex, $tailOpts ) = $this->m_dbs->makeSelectOptions( $sql_options ); |
119 | 128 | $result = '<div style="border: 1px dotted black; background: #A1FB00; padding: 20px; ">' . |
120 | | - '<b>Generated Wiki-Query</b><br />' . |
121 | | - str_replace('[', '[', $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('[', '[', $query->getDescription()->getQueryString()) . '</tt><br />' . |
| 132 | + 'Query-Size: ' . $query->getDescription()->getSize() . '<br />' . |
| 133 | + 'Query-Depth: ' . $query->getDescription()->getDepth() . '<br />'; |
124 | 134 | 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>'; |
130 | 140 | } else { |
131 | 141 | $result .= '<b>Empty result, no SQL query created.</b>'; |
132 | 142 | } |
— | — | @@ -133,15 +143,16 @@ |
134 | 144 | foreach ($query->getErrors() as $error) { |
135 | 145 | $errors .= $error . '<br />'; |
136 | 146 | } |
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.'; |
138 | 148 | $auxtables = ''; |
139 | 149 | foreach ($this->m_querylog as $table => $log) { |
140 | | - $auxtables .= "\n\n<b>Temporary table $table</b>"; |
| 150 | + $auxtables .= "<li>Temporary table $table"; |
141 | 151 | foreach ($log as $q) { |
142 | | - $auxtables .= "\n\n$q"; |
| 152 | + $auxtables .= "<br /> <tt>$q</tt>"; |
143 | 153 | } |
| 154 | + $auxtables .= '</li>'; |
144 | 155 | } |
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.'; |
146 | 157 | $result .= '</div>'; |
147 | 158 | return $result; |
148 | 159 | } |
— | — | @@ -241,15 +252,24 @@ |
242 | 253 | $typeid = SMWDataValueFactory::getPropertyObjectTypeID($description->getProperty()); |
243 | 254 | $query->joinfield = "$query->alias.s_id"; |
244 | 255 | $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; |
246 | 262 | $sortfield = ''; // used if we should sort by this property |
247 | 263 | switch ($typeid) { |
248 | 264 | case '_wpg': case '__nry': // subconditions as subqueries (compiled) |
249 | 265 | $query->jointable = 'smw_rels2'; |
250 | 266 | $sub = $this->compileQueries($description->getDescription()); |
251 | 267 | if ($sub >= 0) { |
252 | | - $query->components = array($sub => "$query->alias.o_id"); |
| 268 | + $query->components[$sub] = "$query->alias.o_id"; |
253 | 269 | } |
| 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 | + } |
254 | 274 | break; |
255 | 275 | case '_txt': // no subconditions |
256 | 276 | $query->jointable = 'smw_text2'; |
— | — | @@ -258,7 +278,7 @@ |
259 | 279 | $query->jointable = 'smw_atts2'; |
260 | 280 | $aw = $this->compileAttributeWhere($description->getDescription(),"$query->alias"); |
261 | 281 | if ($aw != '') { |
262 | | - $query->where .= " AND $aw"; |
| 282 | + $query->where .= ($query->where?' AND ':'') . $aw; |
263 | 283 | } |
264 | 284 | if ( array_key_exists($description->getProperty()->getDBkey(), $this->m_sortkeys) ) { |
265 | 285 | $sortfield = "$query->alias." . (SMWDataValueFactory::newTypeIDValue($typeid)->isNumeric()?'value_num':'value_xsd'); |
— | — | @@ -280,17 +300,26 @@ |
281 | 301 | } |
282 | 302 | } |
283 | 303 | } 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(); |
287 | 308 | foreach ($description->getCategories() as $cat) { |
288 | 309 | $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 | + } |
290 | 313 | } |
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; |
293 | 323 | } |
294 | | - $query->where = $where; |
295 | 324 | } elseif ($description instanceof SMWValueList) { |
296 | 325 | $qid = -1; /// TODO |
297 | 326 | } elseif ($description instanceof SMWValueDescription) { // only processsed here for '_wpg' |
— | — | @@ -298,7 +327,7 @@ |
299 | 328 | if ($description->getComparator() == SMW_CMP_EQ) { |
300 | 329 | $query->type = SMW_SQL2_VALUE; |
301 | 330 | $oid = $this->m_store->getSMWPageID($description->getDatavalue()->getDBkey(), $description->getDatavalue()->getNamespace(),''); |
302 | | - $query->joinfield = $oid; |
| 331 | + $query->joinfield = array($oid); |
303 | 332 | } else { // join with smw_ids needed for other comparators (apply to title string) |
304 | 333 | $query->jointable = 'smw_ids'; |
305 | 334 | $query->joinfield = "$query->alias.smw_id"; |
— | — | @@ -378,9 +407,16 @@ |
379 | 408 | $subquery = $this->m_queries[$qid]; |
380 | 409 | $this->executeQueries($subquery); |
381 | 410 | 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; |
383 | 412 | } 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; |
385 | 421 | } else { // interpret empty joinfields as impossible condition (empty result) |
386 | 422 | $query->joinfield = ''; // make whole query false |
387 | 423 | $query->jointable = ''; |
— | — | @@ -428,23 +464,32 @@ |
429 | 465 | $query = $result; |
430 | 466 | break; |
431 | 467 | 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 | + } |
434 | 472 | $this->m_querylog[$query->alias] = array(); |
435 | 473 | foreach ($query->components as $qid => $joinfield) { |
436 | 474 | $subquery = $this->m_queries[$qid]; |
437 | 475 | $this->executeQueries($subquery); |
438 | 476 | $sql = ''; |
439 | 477 | 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":''); |
441 | 480 | } elseif ($subquery->joinfield !== '') { |
442 | 481 | /// NOTE: this works only for single "unconditional" values without further |
443 | 482 | /// 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"; |
445 | 488 | } // else: // interpret empty joinfields as impossible condition (empty result), ignore |
446 | 489 | if ($sql) { |
447 | 490 | $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 | + } |
449 | 494 | } |
450 | 495 | } |
451 | 496 | $query->jointable = $query->alias; |
— | — | @@ -452,146 +497,72 @@ |
453 | 498 | $query->sortfields = array(); // make sure we got no sortfields |
454 | 499 | /// TODO: currently this eliminates sortkeys, possibly keep them (needs different temp table format though, maybe not such a good thing to do) |
455 | 500 | 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' ); |
459 | 536 | |
| 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'); |
460 | 540 | |
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 |
477 | 563 | } |
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; |
532 | 564 | } |
533 | 565 | |
534 | 566 | /** |
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 | | - /** |
596 | 567 | * This function modifies the given query object at $qid to account for all ordering conditions |
597 | 568 | * in the SMWQuery $query. It is always required that $qid is the id of a query that joins with |
598 | 569 | * 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 @@ |
54 | 54 | $result = NULL; |
55 | 55 | } |
56 | 56 | 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" |
58 | 58 | wfProfileOut("SMWSQLStore2::getSemanticData (SMW)"); |
59 | 59 | return isset($stitle)?(new SMWSemanticData($stitle)):NULL; |
60 | 60 | } |
— | — | @@ -67,7 +67,9 @@ |
68 | 68 | case '__nry': $tasks = $tasks | SMW_SQL2_NARY2; break; |
69 | 69 | case SMW_SP_INSTANCE_OF: $tasks = $tasks | SMW_SQL2_INST2; break; |
70 | 70 | 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; |
72 | 74 | default: |
73 | 75 | if (is_numeric($value)) { // some special property |
74 | 76 | $tasks = $tasks | SMW_SQL2_SPEC2; |
— | — | @@ -79,7 +81,7 @@ |
80 | 82 | } else { |
81 | 83 | $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; |
82 | 84 | } |
83 | | - if ($subject->getNamespace() != SMW_NS_PROPERTY) { |
| 85 | + if ( ($subject->getNamespace() != SMW_NS_PROPERTY) && ($subject->getNamespace() != NS_CATEGORY) ) { |
84 | 86 | $tasks = $tasks & ~SMW_SQL2_SUBS2; |
85 | 87 | } |
86 | 88 | |
— | — | @@ -96,25 +98,16 @@ |
97 | 99 | $this->m_sdstate = array($sid => $this->m_sdstate[$sid]); |
98 | 100 | } |
99 | 101 | |
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) { |
116 | 104 | if ( !($tasks & $task) ) continue; |
117 | 105 | $where = 'p_id=smw_id AND s_id=' . $db->addQuotes($sid); |
118 | 106 | 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; |
119 | 112 | case SMW_SQL2_ATTS2: |
120 | 113 | $from = array('smw_atts2','smw_ids'); |
121 | 114 | $select = 'smw_title as prop, value_unit as unit, value_xsd as value'; |
— | — | @@ -132,6 +125,8 @@ |
133 | 126 | $from = array('smw_subs2','smw_ids'); |
134 | 127 | $select = 'smw_title as value'; |
135 | 128 | $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; |
136 | 131 | break; |
137 | 132 | case SMW_SQL2_REDI2: |
138 | 133 | $from = array('smw_redi2','smw_ids'); |
— | — | @@ -147,7 +142,7 @@ |
148 | 143 | } |
149 | 144 | $res = $db->select( $from, $select, $where, 'SMW::getSemanticData' ); |
150 | 145 | 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) ) { |
152 | 147 | $property = Title::makeTitle(SMW_NS_PROPERTY, $row->prop); |
153 | 148 | $dv = SMWDataValueFactory::newPropertyObjectValue($property); |
154 | 149 | } elseif ($task == SMW_SQL2_SPEC2) { |
— | — | @@ -155,7 +150,12 @@ |
156 | 151 | } else { |
157 | 152 | $dv = SMWDataValueFactory::newTypeIDValue('_wpg'); |
158 | 153 | } |
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) { |
160 | 160 | $dv->setXSDValue($row->value, $row->unit); |
161 | 161 | $this->m_semdata[$sid]->addPropertyObjectValue($property, $dv); |
162 | 162 | } elseif ($task == SMW_SQL2_TEXT2) { |
— | — | @@ -165,8 +165,8 @@ |
166 | 166 | $dv->setXSDValue($row->value); |
167 | 167 | $this->m_semdata[$sid]->addSpecialValue($row->prop, $dv); |
168 | 168 | } 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); |
171 | 171 | } elseif ($task == SMW_SQL2_REDI2) { |
172 | 172 | $dv->setValues($row->title, $row->namespace); |
173 | 173 | $this->m_semdata[$sid]->addSpecialValue(SMW_SP_REDIRECTS_TO, $dv); |
— | — | @@ -293,14 +293,16 @@ |
294 | 294 | } |
295 | 295 | $db->freeResult($res); |
296 | 296 | } |
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) ) { |
300 | 302 | $res = $db->select( array('smw_subs2','smw_ids'), 'smw_title', |
301 | 303 | 's_id=smw_id AND o_id=' . $db->addQuotes($oid), |
302 | 304 | 'SMW::getSpecialSubjects', $this->getSQLOptions($requestoptions) ); |
303 | 305 | while($row = $db->fetchObject($res)) { |
304 | | - $result[] = Title::makeTitle(SMW_NS_PROPERTY, $row->smw_title); |
| 306 | + $result[] = Title::makeTitle($namespace, $row->smw_title); |
305 | 307 | } |
306 | 308 | $db->freeResult($res); |
307 | 309 | } |
— | — | @@ -352,7 +354,7 @@ |
353 | 355 | } |
354 | 356 | |
355 | 357 | 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? |
357 | 359 | wfProfileIn("SMWSQLStore2::getPropertySubjects (SMW)"); |
358 | 360 | $result = array(); |
359 | 361 | $pid = $this->getSMWPageID($property->getDBkey(), $property->getNamespace(),''); |
— | — | @@ -595,9 +597,9 @@ |
596 | 598 | } |
597 | 599 | } else { // special property |
598 | 600 | 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; |
601 | 602 | /// TODO: filtering here is bad for fully neglected properties (IMPORTED FROM) |
| 603 | + case SMW_SP_REDIRECTS_TO: // handled by updateRedirects above |
602 | 604 | break; |
603 | 605 | case SMW_SP_INSTANCE_OF: |
604 | 606 | foreach($propertyValueArray as $value) { |
— | — | @@ -608,12 +610,13 @@ |
609 | 611 | } |
610 | 612 | } |
611 | 613 | 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 ) { |
614 | 617 | break; |
615 | 618 | } |
616 | 619 | foreach($propertyValueArray as $value) { |
617 | | - if ( $value->getNamespace() == SMW_NS_PROPERTY ) { |
| 620 | + if ( $value->getNamespace() == $namespace ) { |
618 | 621 | $up_subs2[] = |
619 | 622 | array('s_id' => $this->makeSMWPageID($subject->getDBkey(),$subject->getNamespace(),''), |
620 | 623 | 'o_id' => $this->makeSMWPageID($value->getDBkey(),$value->getNamespace(),'')); |