/** * Create a new SMWSQLStore2Query object that can be used to obtain results * for the given description. The result is stored in $this->m_queries * using a numeric key that is returned as a result of the function. * Returns -1 if no query was created. * @todo The case of nominal classes (top-level SMWValueDescription) still * makes some assumptions about the table structure, especially about the * name of the joinfield (o_id). Better extend compileAttributeWhere to * deal with this case. * * @param SMWDescription $description * * @return integer */ protected function compileQueries(SMWDescription $description) { $qid = SMWSQLStore2Query::$qnum; $query = new SMWSQLStore2Query(); if ($description instanceof SMWSomeProperty) { $this->compilePropertyCondition($query, $description->getProperty(), $description->getDescription()); // Compilation has set type to NOQUERY: drop condition. if ($query->type == SMW_SQL2_NOQUERY) { $qid = -1; } } elseif ($description instanceof SMWNamespaceDescription) { // TODO: One instance of smw_ids on s_id always suffices (swm_id is KEY)! Doable in execution ... (PERFORMANCE) $query->jointable = 'smw_ids'; $query->joinfield = "{$query->alias}.smw_id"; $query->where = "{$query->alias}.smw_namespace=" . $this->m_dbs->addQuotes($description->getNamespace()); } elseif ($description instanceof SMWConjunction || $description instanceof SMWDisjunction) { $query->type = $description instanceof SMWConjunction ? SMW_SQL2_CONJUNCTION : SMW_SQL2_DISJUNCTION; foreach ($description->getDescriptions() as $subdesc) { $sub = $this->compileQueries($subdesc); if ($sub >= 0) { $query->components[$sub] = true; } } // All subconditions failed, drop this as well. if (count($query->components) == 0) { $qid = -1; } } elseif ($description instanceof SMWClassDescription) { $cqid = SMWSQLStore2Query::$qnum; $cquery = new SMWSQLStore2Query(); $cquery->type = SMW_SQL2_CLASS_HIERARCHY; $cquery->joinfield = array(); foreach ($description->getCategories() as $cat) { $cid = $this->m_store->getSMWPageID($cat->getDBkey(), NS_CATEGORY, $cat->getInterwiki()); if ($cid != 0) { $cquery->joinfield[] = $cid; } } if (count($cquery->joinfield) == 0) { // Empty result. $query->type = SMW_SQL2_VALUE; $query->jointable = ''; $query->joinfield = ''; } else { // Instance query with dicjunction of classes (categories) $query->jointable = 'smw_inst2'; $query->joinfield = "{$query->alias}.s_id"; $query->components[$cqid] = "{$query->alias}.o_id"; $this->m_queries[$cqid] = $cquery; } } elseif ($description instanceof SMWValueDescription) { // Only type '_wpg' objects can appear on query level (essentially as nominal classes). if ($description->getDatavalue()->getTypeID() == '_wpg') { if ($description->getComparator() == SMW_CMP_EQ) { $query->type = SMW_SQL2_VALUE; $oid = $this->m_store->getSMWPageID($description->getDatavalue()->getDBkey(), $description->getDatavalue()->getNamespace(), $description->getDatavalue()->getInterwiki()); $query->joinfield = array($oid); } else { // Join with smw_ids needed for other comparators (apply to title string). $query->jointable = 'smw_ids'; $query->joinfield = "{$query->alias}.smw_id"; $value = $description->getDatavalue()->getSortkey(); switch ($description->getComparator()) { case SMW_CMP_LEQ: $comp = '<='; break; case SMW_CMP_GEQ: $comp = '>='; break; case SMW_CMP_NEQ: $comp = '!='; break; case SMW_CMP_LIKE: case SMW_CMP_NLKE: $comp = ' LIKE '; if ($description->getComparator() == SMW_CMP_NLKE) { $comp = " NOT{$comp}"; } $value = str_replace(array('%', '_', '*', '?'), array('\\%', '\\_', '%', '_'), $value); break; } $query->where = "{$query->alias}.smw_sortkey{$comp}" . $this->m_dbs->addQuotes($value); } } } elseif ($description instanceof SMWConceptDescription) { // fetch concept definition and insert it here $cid = $this->m_store->getSMWPageID($description->getConcept()->getDBkey(), SMW_NS_CONCEPT, ''); $row = $this->m_dbs->selectRow('smw_conc2', array('concept_txt', 'concept_features', 'concept_size', 'concept_depth', 'cache_date'), array('s_id' => $cid), 'SMWSQLStore2Queries::compileQueries'); if ($row === false) { // No description found, concept does not exist. // keep the above query object, it yields an empty result // TODO: announce an error here? (maybe not, since the query processor can check for // non-existing concept pages which is probably the main reason for finding nothing here) } else { global $smwgQConceptCaching, $smwgQMaxSize, $smwgQMaxDepth, $smwgQFeatures, $smwgQConceptCacheLifetime; $may_be_computed = $smwgQConceptCaching == CONCEPT_CACHE_NONE || $smwgQConceptCaching == CONCEPT_CACHE_HARD && ~(~($row->concept_features + 0) | $smwgQFeatures) == 0 && $smwgQMaxSize >= $row->concept_size && $smwgQMaxDepth >= $row->concept_depth; if ($row->cache_date && ($row->cache_date > strtotime("now") - $smwgQConceptCacheLifetime * 60 || !$may_be_computed)) { // Cached concept, use cache unless it is dead and can be revived. $query->jointable = 'smw_conccache'; $query->joinfield = "{$query->alias}.s_id"; $query->where = "{$query->alias}.o_id=" . $this->m_dbs->addQuotes($cid); } elseif ($row->concept_txt) { // Parse description and process it recursively. if ($may_be_computed) { $qp = new SMWQueryParser(); // No defaultnamespaces here; If any, these are already in the concept. $desc = $qp->getQueryDescription($row->concept_txt); $qid = $this->compileQueries($desc); if ($qid != -1) { $query = $this->m_queries[$qid]; } else { // somehow the concept query is no longer valid; maybe some syntax changed (upgrade) or global settings were modified since storing it smwfLoadExtensionMessages('SemanticMediaWiki'); $this->m_errors[] = wfMsg('smw_emptysubquery'); // not quite the right message, but this case is very rare; let us not make detailed messages for this } } else { smwfLoadExtensionMessages('SemanticMediaWiki'); $this->m_errors[] = wfMsg('smw_concept_cache_miss', $description->getConcept()->getText()); } } // else: no cache, no description (this may happen); treat like empty concept } } else { // (e.g. SMWThingDescription) $qid = -1; // no condition } if ($qid >= 0) { // Success, keep query object, propagate sortkeys from subqueries. $this->m_queries[$qid] = $query; if ($query->type != SMW_SQL2_DISJUNCTION) { // Sortkeys are killed by disjunctions (not all parts may have them), // NOTE: preprocessing might try to push disjunctions downwards to safe sortkey, but this seems to be minor foreach ($query->components as $cid => $field) { $query->sortfields = array_merge($this->m_queries[$cid]->sortfields, $query->sortfields); } } } return $qid; }
/** * Given an SMWDescription that is just a conjunction or disjunction of * SMWValueDescription objects, create a plain WHERE condition string for it. */ protected function compileAttributeWhere(SMWDescription $description, $jointable) { if ($description instanceof SMWValueDescription) { $dv = $description->getDatavalue(); if (SMWSQLStore2::getStorageMode($dv->getTypeID()) == SMW_SQL2_SPEC2) { $keys = $dv->getDBkeys(); $value = $keys[0]; $field = "{$jointable}.value_string"; } else { // should be SMW_SQL2_ATTS2 if ($dv->isNumeric()) { $value = $dv->getNumericValue(); $field = "{$jointable}.value_num"; } else { $keys = $dv->getDBkeys(); $value = $keys[0]; $field = "{$jointable}.value_xsd"; } } switch ($description->getComparator()) { case SMW_CMP_LEQ: $comp = '<='; break; case SMW_CMP_GEQ: $comp = '>='; break; case SMW_CMP_NEQ: $comp = '!='; break; case SMW_CMP_LIKE: if ($dv->getTypeID() == '_str') { $comp = ' LIKE '; $value = str_replace(array('%', '_', '*', '?'), array('\\%', '\\_', '%', '_'), $value); } else { // LIKE only supported for strings $comp = '='; } break; case SMW_CMP_EQ: default: $comp = '='; break; } $result = "{$field}{$comp}" . $this->m_dbs->addQuotes($value); } elseif ($description instanceof SMWConjunction || $description instanceof SMWDisjunction) { $op = $description instanceof SMWConjunction ? ' AND ' : ' OR '; $result = ''; foreach ($description->getDescriptions() as $subdesc) { $result = $result . ($result != '' ? $op : '') . $this->compileAttributeWhere($subdesc, $jointable); } $result = "({$result})"; } else { $result = ''; } return $result; }