Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2_Queries.php |
— | — | @@ -61,171 +61,166 @@ |
62 | 62 | /** |
63 | 63 | * The new SQL store's implementation of query answering. |
64 | 64 | * |
65 | | - * TODO: we now have sorting even for subquery conditions. Does this work? Is it slow/problematic? |
66 | | - * NOTE: we do not support category wildcards, as they have no useful semantics in OWL/RDFS/LP/whatever |
| 65 | + * NOTE: we do not support category wildcards as they have no useful semantics in OWL/RDFS/LP/Whatever |
67 | 66 | */ |
68 | 67 | public function getQueryResult(SMWQuery $query) { |
69 | 68 | global $smwgQSortingSupport; |
70 | | - $prs = $query->getDescription()->getPrintrequests(); // ignore print requests at deeper levels |
71 | 69 | if ($query->querymode == SMWQuery::MODE_NONE) { // don't query, but return something to printer |
72 | | - $result = new SMWQueryResult($prs, $query, false); |
| 70 | + $result = new SMWQueryResult($query->getDescription()->getPrintrequests(), $query, false); |
73 | 71 | return $result; |
74 | 72 | } |
| 73 | + $this->m_queries = array(); |
| 74 | + $this->m_querylog = array(); |
| 75 | + SMWSQLStore2Query::$qnum = 0; |
75 | 76 | $this->m_sortkeys = $query->sortkeys; |
76 | | - |
| 77 | + // manually make final root query (to retrieve namespace,title): |
| 78 | + $rootid = SMWSQLStore2Query::$qnum; |
| 79 | + $qobj = new SMWSQLStore2Query(); |
| 80 | + $qobj->jointable = 'smw_ids'; |
| 81 | + $qobj->joinfield = "$qobj->alias.smw_id"; |
| 82 | + // build query dependency tree: |
77 | 83 | $qid = $this->compileQueries($query->getDescription()); |
78 | | - $sql_options = array( 'LIMIT' => $query->getLimit() + 1, 'OFFSET' => $query->getOffset() ); |
79 | | - $this->applyOrderConditions($query,$qid,$sql_options); |
80 | | - $qobj = new SMWSQLStore2Query(); |
81 | | - $qobj->components = array($qid => 'ids.smw_id'); |
82 | | - $this->executeQueries($qobj); |
83 | | - |
84 | | - // debug: |
85 | | - $result = ' ' . str_replace('[','[',$query->getDescription()->getQueryString()) . "\n\n"; |
86 | | - $result .= 'SELECT DISTINCT ids.smw_title,ids.smw_namespace FROM smw_ids AS ids' . $qobj->from . (($qobj->where=='')?'':' WHERE ') . $qobj->where . ";"; |
87 | | - foreach ($this->m_querylog as $table => $log) { |
88 | | - $result .= "\n\n<b>Temporary table $table</b>"; |
89 | | - foreach ($log as $q) { |
90 | | - $result .= "\n\n$q"; |
91 | | - } |
| 84 | + if ($qid >= 0) { // append to root |
| 85 | + $qobj->components = array($qid => "$qobj->alias.smw_id"); |
| 86 | + $qobj->sortfields = $this->m_queries[$qid]->sortfields; |
92 | 87 | } |
| 88 | + $this->m_queries[$rootid] = $qobj; |
93 | 89 | |
94 | | - $res = $this->m_dbs->select($this->m_dbs->tableName('smw_ids') . ' AS ids' . $qobj->from, 'DISTINCT ids.smw_title as t,ids.smw_namespace as ns', $qobj->where, 'SMW::getQueryResult', $sql_options); |
95 | | - |
96 | | - // debug: |
97 | | - while ($row = $this->m_dbs->fetchObject($res)) { |
98 | | - $result .= "\n\n$row->t ($row->ns)"; |
| 90 | + $this->applyOrderConditions($query,$rootid); // may extend query if needed for sorting |
| 91 | + $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 | + switch ($query->querymode) { |
| 94 | + case SMWQuery::MODE_DEBUG: |
| 95 | + $result = $this->getDebugQueryResult($query,$rootid); |
| 96 | + break; |
| 97 | + case SMWQuery::MODE_COUNT: |
| 98 | + $result = $this->getCountQueryResult($query,$rootid); |
| 99 | + break; |
| 100 | + default: |
| 101 | + $result = $this->getInstanceQueryResult($query,$rootid); |
| 102 | + break; |
99 | 103 | } |
100 | | - $count=0; |
101 | | -// $result = new SMWQueryResult($prs, $query, ($count > $query->getLimit()) ); |
102 | | - |
103 | 104 | // finally, free temporary tables |
104 | 105 | foreach ($this->m_querylog as $table => $log) { |
105 | 106 | $this->m_dbs->query("DROP TEMPORARY TABLE $table", 'SMW::getQueryResult'); |
106 | 107 | } |
107 | | - |
108 | 108 | return $result; |
| 109 | + } |
109 | 110 | |
110 | | - |
111 | | - /// Old code below, unused now |
112 | | - |
113 | | - // Build main query |
114 | | - $this->m_usedtables = array(); |
115 | | - $this->m_sortkeys = $query->sortkeys; |
116 | | - $this->m_sortfields = array(); |
117 | | - foreach ($this->m_sortkeys as $key => $order) { |
118 | | - $this->m_sortfields[$key] = false; // no field found yet |
| 111 | + /** |
| 112 | + * Using a preprocessed internal query description referenced by $rootid, compute |
| 113 | + * the proper debug output for the given query. |
| 114 | + */ |
| 115 | + protected function getDebugQueryResult($query,$rootid) { |
| 116 | + $qobj = $this->m_queries[$rootid]; |
| 117 | + $sql_options = $this->getSQLOptions($query,$rootid); |
| 118 | + list( $startOpts, $useIndex, $tailOpts ) = $this->m_dbs->makeSelectOptions( $sql_options ); |
| 119 | + $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 />'; |
| 124 | + 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'] . ';'; |
| 130 | + } else { |
| 131 | + $result .= '<b>Empty result, no SQL query created.</b>'; |
119 | 132 | } |
| 133 | + $errors = ''; |
| 134 | + foreach ($query->getErrors() as $error) { |
| 135 | + $errors .= $error . '<br />'; |
| 136 | + } |
| 137 | + $result .= ($errors)?"<br /><b>Errors and warnings:</b><br />$errors":'<br /><b>No errors or warnings.</b>'; |
| 138 | + $auxtables = ''; |
| 139 | + foreach ($this->m_querylog as $table => $log) { |
| 140 | + $auxtables .= "\n\n<b>Temporary table $table</b>"; |
| 141 | + foreach ($log as $q) { |
| 142 | + $auxtables .= "\n\n$q"; |
| 143 | + } |
| 144 | + } |
| 145 | + $result .= ($auxtables)?"<br /><b>Auxilliary tables used:</b><br />$auxtables":'<br /><b>No auxilliary tables used.</b>'; |
| 146 | + $result .= '</div>'; |
| 147 | + return $result; |
| 148 | + } |
120 | 149 | |
121 | | - $pagetable = $db->tableName('page'); |
122 | | - $from = $pagetable; |
123 | | - $where = ''; |
124 | | - $curtables = array('PAGE' => $from); |
125 | | - $this->createSQLQuery($query->getDescription(), $from, $where, $db, $curtables); |
126 | | - |
127 | | - // Prepare SQL options |
128 | | - $sql_options = array(); |
129 | | - $sql_options['LIMIT'] = $query->getLimit() + 1; |
130 | | - $sql_options['OFFSET'] = $query->getOffset(); |
131 | | - if ( $smwgQSortingSupport ) { |
132 | | - |
| 150 | + /** |
| 151 | + * Using a preprocessed internal query description referenced by $rootid, compute |
| 152 | + * the proper counting output for the given query. |
| 153 | + */ |
| 154 | + protected function getCountQueryResult($query,$rootid) { |
| 155 | + $qobj = $this->m_queries[$rootid]; |
| 156 | + if ($qobj->joinfield === '') { // empty result, no query needed |
| 157 | + return 0; |
133 | 158 | } |
| 159 | + $sql_options = array( 'LIMIT' => $query->getLimit() + 1, 'OFFSET' => $query->getOffset() ); |
| 160 | + $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); |
| 161 | + $row = $this->m_dbs->fetchObject($res); |
| 162 | + $count = $row->count; |
| 163 | + $this->m_dbs->freeResult($res); |
| 164 | + return $count; |
| 165 | + } |
134 | 166 | |
135 | | - // Execute query and format result as array |
136 | | - if ($query->querymode == SMWQuery::MODE_COUNT) { |
137 | | - $res = $db->select($from, |
138 | | - "COUNT(DISTINCT $pagetable.page_id) AS count", |
139 | | - $where, |
140 | | - 'SMW::getQueryResult', |
141 | | - $sql_options ); |
142 | | - $row = $db->fetchObject($res); |
143 | | - $count = $row->count; |
144 | | - $db->freeResult($res); |
145 | | - wfProfileOut('SMWSQLStore2::getQueryResult (SMW)'); |
146 | | - return $count; |
147 | | - // TODO: report query errors? |
148 | | - } elseif ($query->querymode == SMWQuery::MODE_DEBUG) { /// TODO: internationalise |
149 | | - list( $startOpts, $useIndex, $tailOpts ) = $db->makeSelectOptions( $sql_options ); |
150 | | - $result = '<div style="border: 1px dotted black; background: #A1FB00; padding: 20px; ">' . |
151 | | - '<b>Generated Wiki-Query</b><br />' . |
152 | | - str_replace('[', '[', $query->getDescription()->getQueryString()) . '<br />' . |
153 | | - '<b>Query-Size: </b>' . $query->getDescription()->getSize() . '<br />' . |
154 | | - '<b>Query-Depth: </b>' . $query->getDescription()->getDepth() . '<br />' . |
155 | | - '<b>SQL-Query</b><br />' . |
156 | | - "SELECT DISTINCT $pagetable.page_title as title, $pagetable.page_namespace as namespace" . |
157 | | - ' FROM ' . $from . ' WHERE ' . $where . $tailOpts . '<br />' . |
158 | | - '<b>SQL-Query options</b><br />'; |
159 | | - foreach ($sql_options as $key => $value) { |
160 | | - $result .= " $key=$value"; |
161 | | - } |
162 | | - $result .= '<br /><b>Errors and Warnings</b><br />'; |
163 | | - foreach ($query->getErrors() as $error) { |
164 | | - $result .= $error . '<br />'; |
165 | | - } |
166 | | - $result .= '<br /><b>Auxilliary tables used</b><br />'; |
167 | | - foreach ($this->m_usedtables as $tablename) { |
168 | | - $result .= $tablename . ': '; |
169 | | - $res = $db->query( "SELECT title FROM $tablename", 'SMW::getQueryResult:DEBUG'); |
170 | | - while ( $row = $db->fetchObject($res) ) { |
171 | | - $result .= $row->title . ', '; |
172 | | - } |
173 | | - $result .= '<br />'; |
174 | | - } |
175 | | - $result .= '</div>'; |
| 167 | + /** |
| 168 | + * Using a preprocessed internal query description referenced by $rootid, compute |
| 169 | + * the proper result instance output for the given query. |
| 170 | + */ |
| 171 | + protected function getInstanceQueryResult($query,$rootid) { |
| 172 | + $qobj = $this->m_queries[$rootid]; |
| 173 | + if ($qobj->joinfield === '') { // empty result, no query needed |
| 174 | + $result = new SMWQueryResult($query->getDescription()->getPrintrequests(), $query, false); |
176 | 175 | return $result; |
177 | | - } // else: continue |
| 176 | + } |
| 177 | + $sql_options = $this->getSQLOptions($query,$rootid); |
| 178 | + $res = $this->m_dbs->select($this->m_dbs->tableName($qobj->jointable) . " AS $qobj->alias" . $qobj->from, "DISTINCT $qobj->alias.smw_title AS t,$qobj->alias.smw_namespace AS ns", $qobj->where, 'SMW::getQueryResult', $sql_options); |
178 | 179 | |
179 | | - $res = $db->select($from, |
180 | | - "DISTINCT $pagetable.page_title as title, $pagetable.page_namespace as namespace, $pagetable.page_id as id", |
181 | | - $where, |
182 | | - 'SMW::getQueryResult', |
183 | | - $sql_options ); |
184 | | - |
185 | 180 | $qr = array(); |
186 | 181 | $count = 0; |
187 | | - while ( ($count<$query->getLimit()) && ($row = $db->fetchObject($res)) ) { |
| 182 | + while ( ($count < $query->getLimit()) && ($row = $this->m_dbs->fetchObject($res)) ) { |
188 | 183 | $count++; |
189 | | - //$qr[] = Title::newFromText($row->title, $row->namespace); |
190 | 184 | $v = SMWDataValueFactory::newTypeIDValue('_wpg'); |
191 | | - $v->setValues($row->title, $row->namespace, $row->id); |
| 185 | + $v->setValues($row->t, $row->ns); |
192 | 186 | $qr[] = $v; |
193 | 187 | } |
194 | | - if ($db->fetchObject($res)) { |
| 188 | + if ($this->m_dbs->fetchObject($res)) { |
195 | 189 | $count++; |
196 | 190 | } |
197 | | - $db->freeResult($res); |
| 191 | + $this->m_dbs->freeResult($res); |
198 | 192 | |
199 | 193 | // Create result by executing print statements for everything that was fetched |
200 | | - ///TODO: use limit (and offset?) values for printouts? |
| 194 | + ///TODO: limit (and offset?) values for printouts? |
| 195 | + $prs = $query->getDescription()->getPrintrequests(); |
201 | 196 | $result = new SMWQueryResult($prs, $query, ($count > $query->getLimit()) ); |
202 | 197 | foreach ($qr as $qt) { |
203 | 198 | $row = array(); |
204 | 199 | foreach ($prs as $pr) { |
205 | 200 | switch ($pr->getMode()) { |
206 | | - case SMW_PRINT_THIS: |
207 | | - $row[] = new SMWResultArray(array($qt), $pr); |
208 | | - break; |
209 | | - case SMW_PRINT_CATS: |
210 | | - $row[] = new SMWResultArray($this->getSpecialValues($qt->getTitle(),SMW_SP_INSTANCE_OF), $pr); |
211 | | - break; |
212 | | - case SMW_PRINT_PROP: |
213 | | - $row[] = new SMWResultArray($this->getPropertyValues($qt->getTitle(),$pr->getTitle(), NULL, $pr->getOutputFormat()), $pr); |
214 | | - break; |
215 | | - case SMW_PRINT_CCAT: |
216 | | - $cats = $this->getSpecialValues($qt->getTitle(),SMW_SP_INSTANCE_OF); |
217 | | - $found = '0'; |
218 | | - foreach ($cats as $cat) { |
219 | | - if ($cat->getDBkey() == $pr->getTitle()->getDBkey()) { |
220 | | - $found = '1'; |
221 | | - break; |
222 | | - } |
| 201 | + case SMW_PRINT_THIS: |
| 202 | + $row[] = new SMWResultArray(array($qt), $pr); |
| 203 | + break; |
| 204 | + case SMW_PRINT_CATS: |
| 205 | + $row[] = new SMWResultArray($this->m_store->getSpecialValues($qt->getTitle(),SMW_SP_INSTANCE_OF), $pr); |
| 206 | + break; |
| 207 | + case SMW_PRINT_PROP: |
| 208 | + $row[] = new SMWResultArray($this->m_store->getPropertyValues($qt->getTitle(),$pr->getTitle(), NULL, $pr->getOutputFormat()), $pr); |
| 209 | + break; |
| 210 | + case SMW_PRINT_CCAT: |
| 211 | + $cats = $this->m_store->getSpecialValues($qt->getTitle(),SMW_SP_INSTANCE_OF); |
| 212 | + $found = '0'; |
| 213 | + foreach ($cats as $cat) { |
| 214 | + if ($cat->getDBkey() == $pr->getTitle()->getDBkey()) { |
| 215 | + $found = '1'; |
| 216 | + break; |
223 | 217 | } |
224 | | - $dv = SMWDataValueFactory::newTypeIDValue('_boo'); |
225 | | - $dv->setOutputFormat($pr->getOutputFormat()); |
226 | | - $dv->setXSDValue($found); |
| 218 | + } |
| 219 | + $dv = SMWDataValueFactory::newTypeIDValue('_boo'); |
| 220 | + $dv->setOutputFormat($pr->getOutputFormat()); |
| 221 | + $dv->setXSDValue($found); |
227 | 222 | // $dv = SMWDataValueFactory::newTypeIDValue('_str',$found . ' Format:' . $pr->getOutputFormat() . '!'); |
228 | | - $row[] = new SMWResultArray(array($dv), $pr); |
229 | | - break; |
| 223 | + $row[] = new SMWResultArray(array($dv), $pr); |
| 224 | + break; |
230 | 225 | } |
231 | 226 | } |
232 | 227 | $result->addRow($row); |
— | — | @@ -266,7 +261,7 @@ |
267 | 262 | $query->where .= " AND $aw"; |
268 | 263 | } |
269 | 264 | if ( array_key_exists($description->getProperty()->getDBkey(), $this->m_sortkeys) ) { |
270 | | - $sortfield = $query->alias . (SMWDataValueFactory::newTypeIDValue($typeid)->isNumeric())?'value_num':'value_xsd'; |
| 265 | + $sortfield = "$query->alias." . (SMWDataValueFactory::newTypeIDValue($typeid)->isNumeric()?'value_num':'value_xsd'); |
271 | 266 | } |
272 | 267 | } |
273 | 268 | if ($sortfield) { |
— | — | @@ -326,6 +321,11 @@ |
327 | 322 | if ($qid >= 0) { |
328 | 323 | $this->m_queries[$qid] = $query; |
329 | 324 | } |
| 325 | + if ($query->type != SMW_SQL2_DISJUNCTION) { // sortkeys are killed by disjunctions (not all parts may have them), preprocessing might try to push disjunctions downwards to safe sortkey |
| 326 | + foreach ($query->components as $cid => $field) { |
| 327 | + $query->sortfields = array_merge($this->m_queries[$cid]->sortfields,$query->sortfields); |
| 328 | + } |
| 329 | + } |
330 | 330 | return $qid; |
331 | 331 | } |
332 | 332 | |
— | — | @@ -373,37 +373,58 @@ |
374 | 374 | */ |
375 | 375 | protected function executeQueries(SMWSQLStore2Query &$query) { |
376 | 376 | switch ($query->type) { |
377 | | - case SMW_SQL2_TABLE: case SMW_SQL2_VALUE: |
| 377 | + case SMW_SQL2_TABLE: // normal query with conjunctive subcondition |
378 | 378 | foreach ($query->components as $qid => $joinfield) { |
379 | 379 | $subquery = $this->m_queries[$qid]; |
380 | 380 | $this->executeQueries($subquery); |
381 | 381 | if ($subquery->jointable != '') { // join with jointable.joinfield |
382 | 382 | $query->from .= ' INNER JOIN ' . $subquery->jointable . " AS $subquery->alias ON $joinfield=" . $subquery->joinfield; |
383 | | - } elseif ($subquery->joinfield != '') { // require joinfield as "value" via WHERE |
| 383 | + } elseif ($subquery->joinfield !== '') { // require joinfield as "value" via WHERE |
384 | 384 | $query->where .= (($query->where == '')?'':' AND ') . "$joinfield=" . $subquery->joinfield; |
385 | | - } // else: no usable output from subquery, ignore |
| 385 | + } else { // interpret empty joinfields as impossible condition (empty result) |
| 386 | + $query->joinfield = ''; // make whole query false |
| 387 | + $query->jointable = ''; |
| 388 | + $query->where = ''; |
| 389 | + $query->from = ''; |
| 390 | + break; |
| 391 | + } |
386 | 392 | if ($subquery->where != '') { |
387 | 393 | $query->where .= (($query->where == '')?'':' AND ') . $subquery->where; |
388 | 394 | } |
389 | | - foreach ($subquery->sortfields as $propkey => $field) { |
390 | | - $query->sortfields[$propkey] = $field; // all fieldnames are kept unchanged and remain available in query result |
391 | | - } |
392 | 395 | $query->from .= $subquery->from; |
393 | 396 | } |
394 | 397 | $query->components = array(); |
395 | 398 | break; |
396 | 399 | case SMW_SQL2_CONJUNCTION: |
397 | | - // pick one subquery as anchor point ... |
| 400 | + // pick one subquery with jointable as anchor point ... |
398 | 401 | reset($query->components); |
399 | | - $key = key($query->components); |
400 | | - $result = $this->m_queries[$key]; |
401 | | - unset($query->components[$key]); |
402 | | - $this->executeQueries($result); // execute it first (may change jointable and joinfield, e.g. when making temporary tables |
403 | | - // ... and append to this query the remaining queries |
404 | | - foreach ($query->components as $qid => $joinfield) { |
405 | | - $result->components[$qid] = $result->joinfield; |
| 402 | + $key = false; |
| 403 | + foreach ($query->components as $qkey => $qid) { |
| 404 | + if ($this->m_queries[$qkey]->jointable != '') { |
| 405 | + $key = $qkey; |
| 406 | + break; |
| 407 | + } |
406 | 408 | } |
407 | | - $this->executeQueries($result); // second execute, now incorporating remaining conditions |
| 409 | + if ($key !== false) { |
| 410 | + $result = $this->m_queries[$key]; |
| 411 | + unset($query->components[$key]); |
| 412 | + $this->executeQueries($result); // execute it first (may change jointable and joinfield, e.g. when making temporary tables) |
| 413 | + // ... and append to this query the remaining queries |
| 414 | + foreach ($query->components as $qid => $joinfield) { |
| 415 | + $result->components[$qid] = $result->joinfield; |
| 416 | + } |
| 417 | + $this->executeQueries($result); // second execute, now incorporating remaining conditions |
| 418 | + } else { // only fixed values in conjunction, make a new value without joining |
| 419 | + $key = $qkey; |
| 420 | + $result = $this->m_queries[$key]; |
| 421 | + unset($query->components[$key]); |
| 422 | + foreach ($query->components as $qid => $joinfield) { |
| 423 | + if ($result->joinfield != $this->m_queries[$qid]->joinfield) { |
| 424 | + $result->joinfield = ''; // all other values should already be '' |
| 425 | + break; |
| 426 | + } |
| 427 | + } |
| 428 | + } |
408 | 429 | $query = $result; |
409 | 430 | break; |
410 | 431 | case SMW_SQL2_DISJUNCTION: |
— | — | @@ -413,14 +434,25 @@ |
414 | 435 | foreach ($query->components as $qid => $joinfield) { |
415 | 436 | $subquery = $this->m_queries[$qid]; |
416 | 437 | $this->executeQueries($subquery); |
417 | | - $sql = "INSERT IGNORE INTO $query->alias SELECT $subquery->joinfield FROM $subquery->jointable AS $subquery->alias $subquery->from WHERE $subquery->where "; |
418 | | - $this->m_querylog[$query->alias][] = $sql; |
419 | | - $this->m_dbs->query($sql , 'SMW::executeQueries'); |
| 438 | + $sql = ''; |
| 439 | + if ($subquery->jointable != '') { |
| 440 | + $sql = "INSERT IGNORE INTO $query->alias SELECT $subquery->joinfield FROM $subquery->jointable AS $subquery->alias $subquery->from WHERE $subquery->where "; |
| 441 | + } elseif ($subquery->joinfield !== '') { |
| 442 | + /// NOTE: this works only for single "unconditional" values without further |
| 443 | + /// 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) . ')'; |
| 445 | + } // else: // interpret empty joinfields as impossible condition (empty result), ignore |
| 446 | + if ($sql) { |
| 447 | + $this->m_querylog[$query->alias][] = $sql; |
| 448 | + $this->m_dbs->query($sql , 'SMW::executeQueries'); |
| 449 | + } |
420 | 450 | } |
421 | 451 | $query->jointable = $query->alias; |
422 | 452 | $query->joinfield = "$query->alias.id"; |
| 453 | + $query->sortfields = array(); // make sure we got no sortfields |
423 | 454 | /// TODO: currently this eliminates sortkeys, possibly keep them (needs different temp table format though, maybe not such a good thing to do) |
424 | 455 | break; |
| 456 | + case SMW_SQL2_VALUE: break; // nothing to do |
425 | 457 | } |
426 | 458 | } |
427 | 459 | |
— | — | @@ -560,53 +592,65 @@ |
561 | 593 | } |
562 | 594 | |
563 | 595 | /** |
564 | | - * This function modifies the given query object ID $qid and array of SQL $options to |
565 | | - * account for the ordering conditions in the SMWQuery $query. |
| 596 | + * This function modifies the given query object at $qid to account for all ordering conditions |
| 597 | + * in the SMWQuery $query. It is always required that $qid is the id of a query that joins with |
| 598 | + * smw_ids so that the field alias.smw_title is $available for default sorting. |
566 | 599 | */ |
567 | | - protected function applyOrderConditions($query, &$qid, &$options) { |
| 600 | + protected function applyOrderConditions($query, $qid) { |
568 | 601 | global $smwgQSortingSupport; |
569 | 602 | if ( !$smwgQSortingSupport ) { |
570 | 603 | return; |
571 | 604 | } |
572 | 605 | $qobj = $this->m_queries[$qid]; |
573 | | -// $extraproperties = array(); // collect required extra property descriptions |
574 | | -// foreach ($this->m_sortkeys as $key => $order) { |
575 | | -// if ($this->m_sortfields[$key] == false) { // find missing property to sort by |
576 | | -// if ($key == '') { // sort by first column (page titles) |
577 | | -// $this->m_sortfields[$key] = "$pagetable.page_title"; |
578 | | -// } else { // try to extend query |
579 | | -// $extrawhere = ''; |
580 | | -// $sorttitle = Title::newFromText($key, SMW_NS_PROPERTY); |
581 | | -// if ($sorttitle !== NULL) { // careful, Title creation might still fail! |
582 | | -// $extraproperties[] = new SMWSomeProperty($sorttitle, new SMWThingDescription()); |
583 | | -// } |
584 | | -// } |
585 | | -// } |
586 | | -// } |
587 | | -// if (count($extraproperties) > 0) { |
588 | | -// if (count($extraproperties) == 1) { |
589 | | -// $desc = end($extraproperties); |
590 | | -// } else { |
591 | | -// $desc = new SMWConjunction($extraproperties); |
592 | | -// } |
593 | | -// $this->createSQLQuery($desc, $from, $extrawhere, $db, $curtables); |
594 | | -// if ($extrawhere != '') { |
595 | | -// if ($where != '') { |
596 | | -// $where = "($where) AND "; |
597 | | -// } |
598 | | -// $where .= "($extrawhere)"; |
599 | | -// } |
600 | | -// } |
| 606 | + // (1) collect required extra property descriptions: |
| 607 | + $extraproperties = array(); |
| 608 | + foreach ($this->m_sortkeys as $propkey => $order) { |
| 609 | + if (!array_key_exists($propkey,$qobj->sortfields)) { // find missing property to sort by |
| 610 | + if ($propkey == '') { // sort by first result column (page titles) |
| 611 | + $qobj->sortfields[$propkey] = "$qobj->alias.smw_title"; |
| 612 | + } else { // try to extend query |
| 613 | + $extrawhere = ''; |
| 614 | + $sorttitle = Title::newFromText($propkey, SMW_NS_PROPERTY); |
| 615 | + if ($sorttitle !== NULL) { // careful, Title creation might still fail! |
| 616 | + $extraproperties[] = new SMWSomeProperty($sorttitle, new SMWThingDescription()); |
| 617 | + } |
| 618 | + } |
| 619 | + } |
| 620 | + } |
| 621 | + // (2) compile according conditions and hack them into $qobj: |
| 622 | + if (count($extraproperties) > 0) { |
| 623 | + $desc = new SMWConjunction($extraproperties); |
| 624 | + $newqid = $this->compileQueries($desc); |
| 625 | + $newqobj = $this->m_queries[$newqid]; // this is always an SMW_SQL2_CONJUNCTION ... |
| 626 | + foreach ($newqobj->components as $cid => $field) { // ... so just re-wire its dependencies |
| 627 | + $qobj->components[$cid] = $qobj->joinfield; |
| 628 | + $qobj->sortfields = array_merge($qobj->sortfields, $this->m_queries[$cid]->sortfields); |
| 629 | + } |
| 630 | + $this->m_queries[$qid] = $qobj; |
| 631 | + } |
| 632 | + } |
| 633 | + |
| 634 | + /** |
| 635 | + * Get a SQL option array for the given query and preprocessed query object at given id. |
| 636 | + */ |
| 637 | + protected function getSQLOptions($query,$rootid) { |
| 638 | + global $smwgQSortingSupport; |
| 639 | + $result = array( 'LIMIT' => $query->getLimit() + 1, 'OFFSET' => $query->getOffset() ); |
| 640 | + // build ORDER BY options using discovered sorting fields: |
| 641 | + if ($smwgQSortingSupport) { |
| 642 | + $qobj = $this->m_queries[$rootid]; |
601 | 643 | foreach ($this->m_sortkeys as $propkey => $order) { |
602 | 644 | if (array_key_exists($propkey,$qobj->sortfields)) { // field successfully added |
603 | | - if (!array_key_exists('ORDER BY', $options)) { |
604 | | - $options['ORDER BY'] = ''; |
| 645 | + if (!array_key_exists('ORDER BY', $result)) { |
| 646 | + $result['ORDER BY'] = ''; |
605 | 647 | } else { |
606 | | - $options['ORDER BY'] .= ', '; |
| 648 | + $result['ORDER BY'] .= ', '; |
607 | 649 | } |
608 | | - $options['ORDER BY'] .= $qobj->sortfields[$propkey] . " $order "; |
| 650 | + $result['ORDER BY'] .= $qobj->sortfields[$propkey] . " $order "; |
609 | 651 | } |
610 | 652 | } |
| 653 | + } |
| 654 | + return $result; |
611 | 655 | } |
612 | 656 | |
613 | 657 | } |
\ No newline at end of file |
Index: trunk/extensions/SemanticMediaWiki/includes/storage/SMW_SQLStore2.php |
— | — | @@ -36,27 +36,6 @@ |
37 | 37 | /// Like SMWSQLStore2::m_semdata, but containing flags indicating completeness of the SMWSemanticData objs |
38 | 38 | protected $m_sdstate = array(); |
39 | 39 | |
40 | | - |
41 | | - /** |
42 | | - * Global counter to prevent clashes between table aliases. |
43 | | - */ |
44 | | -// static protected $m_tablenum = 0; |
45 | | - /** |
46 | | - * Array of names of virtual tables that hold the lower closure of certain |
47 | | - * categories wrt. hierarchy. |
48 | | - */ |
49 | | -// static protected $m_categorytables = array(); |
50 | | - /** |
51 | | - * Array of names of virtual tables that hold the lower closure of certain |
52 | | - * categories wrt. hierarchy. |
53 | | - */ |
54 | | -// static protected $m_propertytables = array(); |
55 | | - /** |
56 | | - * Record all virtual tables used for a single operation (especially query) to produce debug output. |
57 | | - */ |
58 | | -// protected $m_usedtables; |
59 | | - |
60 | | - |
61 | 40 | ///// Reading methods ///// |
62 | 41 | |
63 | 42 | function getSemanticData($subject, $filter = false) { |