/** * Enable a NotifyMe with specified id and querystring * * used for inline query only */ static public function enableNotify( $notify_id, $querystring, &$msg = NULL ) { wfProfileIn( 'SMWNotifyProcessor::enableNotify (SMW)' ); $sStore = NMStorage::getDatabase(); global $smwgQDefaultNamespaces; SMWQueryProcessor::processFunctionParams( SMWNotifyProcessor::getQueryRawParams( $querystring ), $querystring, $params, $printouts ); $relatedArticles = array(); foreach ( $printouts as $po ) { $printoutArticles[] = array( 'namespace' => SMW_NS_PROPERTY, 'title' => Title::makeTitle( SMW_NS_PROPERTY, $po->getText( SMW_OUTPUT_WIKI ) )->getDBkey() ); } $qp = new SMWNotifyParser( $notify_id, $printoutArticles ); $qp->setDefaultNamespaces( $smwgQDefaultNamespaces ); $desc = $qp->getQueryDescription( $querystring ); if ( !$qp->m_result ) { $qp->m_errors[] = wfMsg( 'smw_nm_proc_pagenotexist' ); } if ( isset( $msg ) && $qp->hasSubquery() ) { $msg .= "\n" . wfMsg( 'smw_nm_proc_subqueryhint' ); } $query = new SMWQuery( $desc, true, false ); $query->setQueryString( $querystring ); $query->addErrors( $qp->getErrors() ); // keep parsing errors for later output $res = $sStore->getNMQueryResult( $query ); if ( count( $query->getErrors() ) > 0 ) { if ( isset( $msg ) ) { $msg .= "\n\n" . implode( '\n', $query->getErrors() ) . "\n\n" . wfMsg( 'smw_nm_proc_enablehint' ); } $sStore->disableNotifyState( $notify_id ); wfProfileOut( 'SMWNotifyProcessor::enableNotify (SMW)' ); return false; } $sStore->updateNMSql( $notify_id, $res['sql'], $res['tmp_hierarchy'] ); if ( count( $res['page_ids'] ) > 0 ) { $add_monitor = array(); foreach ( $res['page_ids'] as $page_id ) { $add_monitor[] = array( 'notify_id' => $notify_id, 'page_id' => $page_id ); } $sStore->addNotifyMonitor( $add_monitor ); } $sStore->updateNotifyState( $notify_id, 1 ); wfProfileOut( 'SMWNotifyProcessor::enableNotify (SMW)' ); return true; }
public function testEmptyGetQueryResultWhereQueryContainsErrors() { $connection = $this->getMockBuilder('\\SMW\\SPARQLStore\\RepositoryConnection')->disableOriginalConstructor()->getMockForAbstractClass(); $store = $this->getMockBuilder('\\SMW\\Store')->disableOriginalConstructor()->getMockForAbstractClass(); $compoundConditionBuilder = $this->getMockBuilder('\\SMW\\SPARQLStore\\QueryEngine\\CompoundConditionBuilder')->disableOriginalConstructor()->getMock(); $description = $this->getMockForAbstractClass('\\SMW\\Query\\Language\\Description'); $engineOptions = new EngineOptions(); $engineOptions->set('smwgIgnoreQueryErrors', false); $instance = new QueryEngine($connection, $compoundConditionBuilder, new QueryResultFactory($store), $engineOptions); $query = new Query($description); $query->addErrors(array('Foo')); $this->assertInstanceOf('\\SMWQueryResult', $instance->getQueryResult($query)); $this->assertEmpty($instance->getQueryResult($query)->getResults()); }
/** * Parse a query string given in SMW's query language to create * an SMWQuery. Parameters are given as key-value-pairs in the * given array. The parameter $context defines in what context the * query is used, which affects ceretain general settings. * An object of type SMWQuery is returned. * * The format string is used to specify the output format if already * known. Otherwise it will be determined from the parameters when * needed. This parameter is just for optimisation in a common case. * * @param string $queryString * @param array $params These need to be the result of a list fed to getProcessedParams * @param $context * @param string $format * @param array $extraPrintouts * * @return SMWQuery */ public static function createQuery($queryString, array $params, $context = self::INLINE_QUERY, $format = '', array $extraPrintouts = array()) { global $smwgQDefaultNamespaces, $smwgQFeatures, $smwgQConceptFeatures; // parse query: $queryfeatures = $context == self::CONCEPT_DESC ? $smwgQConceptFeatures : $smwgQFeatures; $qp = new SMWQueryParser($queryfeatures); $qp->setDefaultNamespaces($smwgQDefaultNamespaces); $desc = $qp->getQueryDescription($queryString); if ($format === '' || is_null($format)) { $format = $params['format']->getValue(); } if ($format == 'count') { $querymode = SMWQuery::MODE_COUNT; } elseif ($format == 'debug') { $querymode = SMWQuery::MODE_DEBUG; } else { $printer = self::getResultPrinter($format, $context); $querymode = $printer->getQueryMode($context); } $query = new SMWQuery($desc, $context != self::SPECIAL_PAGE, $context == self::CONCEPT_DESC); $query->setQueryString($queryString); $query->setExtraPrintouts($extraPrintouts); $query->setMainLabel($params['mainlabel']->getValue()); $query->addErrors($qp->getErrors()); // keep parsing errors for later output // set mode, limit, and offset: $query->querymode = $querymode; if (array_key_exists('offset', $params) && is_int($params['offset']->getValue() + 0)) { $query->setOffset(max(0, trim($params['offset']->getValue()) + 0)); } if ($query->querymode == SMWQuery::MODE_COUNT) { // largest possible limit for "count", even inline global $smwgQMaxLimit; $query->setOffset(0); $query->setLimit($smwgQMaxLimit, false); } else { if (array_key_exists('limit', $params) && is_int(trim($params['limit']->getValue()) + 0)) { $query->setLimit(max(0, trim($params['limit']->getValue()) + 0)); if (trim($params['limit']->getValue()) + 0 < 0) { // limit < 0: always show further results link only $query->querymode = SMWQuery::MODE_NONE; } } else { global $smwgQDefaultLimit; $query->setLimit($smwgQDefaultLimit); } } $defaultSort = $format === 'rss' ? 'DESC' : 'ASC'; $sort = self::getSortKeys($params['sort']->getValue(), $params['order']->getValue(), $defaultSort); $query->sortkeys = $sort['keys']; $query->addErrors($sort['errors']); $query->sort = count($query->sortkeys) > 0; // TODO: Why would we do this here? return $query; }
/** * Adds an array of erros. * * @param array $errors */ public function addErrors(array $errors) { $this->mQuery->addErrors($errors); }
/** * The new SQL store's implementation of query answering. This function * works in two stages: First, the nested conditions of the given query * object are preprocessed to compute an abstract representation of the * SQL query that is to be executed. Since query conditions correspond to * joins with property tables in most cases, this abstract representation * is essentially graph-like description of how property tables are joined. * Moreover, this graph is tree-shaped, since all query conditions are * tree-shaped. Each part of this abstract query structure is represented * by an SMWSQLStore3Query object in the array m_queries. * * As a second stage of processing, the thus prepared SQL query is actually * executed. Typically, this means that the joins are collapsed into one * SQL query to retrieve results. In some cases, such as in dbug mode, the * execution might be restricted and not actually perform the whole query. * * The two-stage process helps to separate tasks, and it also allows for * better optimisations: it is left to the execution engine how exactly the * query result is to be obtained. For example, one could pre-compute * partial suib-results in temporary tables (or even cache them somewhere), * instead of passing one large join query to the DB (of course, it might * be large only if the configuration of SMW allows it). For some DBMS, a * step-wise execution of the query might lead to better performance, since * it exploits the tree-structure of the joins, which is important for fast * processing -- not all DBMS might be able in seeing this by themselves. * * @param SMWQuery $query * * @return mixed: depends on $query->querymode */ public function getQueryResult(SMWQuery $query) { global $smwgIgnoreQueryErrors, $smwgQSortingSupport; if ((!$smwgIgnoreQueryErrors || $query->getDescription() instanceof SMWThingDescription) && $query->querymode != SMWQuery::MODE_DEBUG && count($query->getErrors()) > 0) { return new SMWQueryResult($query->getDescription()->getPrintrequests(), $query, array(), $this->m_store, false); // NOTE: we check this here to prevent unnecessary work, but we check // it after query processing below again in case more errors occurred. } elseif ($query->querymode == SMWQuery::MODE_NONE) { // don't query, but return something to printer return new SMWQueryResult($query->getDescription()->getPrintrequests(), $query, array(), $this->m_store, true); } $this->m_qmode = $query->querymode; $this->m_queries = array(); $this->m_hierarchies = array(); $this->m_querylog = array(); $this->m_errors = array(); SMWSQLStore3Query::$qnum = 0; $this->m_sortkeys = $query->sortkeys; // *** First compute abstract representation of the query (compilation) ***// wfProfileIn('SMWSQLStore3Queries::compileMainQuery (SMW)'); $qid = $this->compileQueries($query->getDescription()); // compile query, build query "plan" wfProfileOut('SMWSQLStore3Queries::compileMainQuery (SMW)'); if ($qid < 0) { // no valid/supported condition; ensure that at least only proper pages are delivered $qid = SMWSQLStore3Query::$qnum; $q = new SMWSQLStore3Query(); $q->jointable = SMWSql3SmwIds::tableName; $q->joinfield = "{$q->alias}.smw_id"; $q->where = "{$q->alias}.smw_iw!=" . $this->m_dbs->addQuotes(SMW_SQL3_SMWIW_OUTDATED) . " AND {$q->alias}.smw_iw!=" . $this->m_dbs->addQuotes(SMW_SQL3_SMWREDIIW) . " AND {$q->alias}.smw_iw!=" . $this->m_dbs->addQuotes(SMW_SQL3_SMWBORDERIW) . " AND {$q->alias}.smw_iw!=" . $this->m_dbs->addQuotes(SMW_SQL3_SMWINTDEFIW); $this->m_queries[$qid] = $q; } if ($this->m_queries[$qid]->jointable != SMWSql3SmwIds::tableName) { // manually make final root query (to retrieve namespace,title): $rootid = SMWSQLStore3Query::$qnum; $qobj = new SMWSQLStore3Query(); $qobj->jointable = SMWSql3SmwIds::tableName; $qobj->joinfield = "{$qobj->alias}.smw_id"; $qobj->components = array($qid => "{$qobj->alias}.smw_id"); $qobj->sortfields = $this->m_queries[$qid]->sortfields; $this->m_queries[$rootid] = $qobj; } else { // not such a common case, but worth avoiding the additional inner join: $rootid = $qid; } // Include order conditions (may extend query if needed for sorting): if ($smwgQSortingSupport) { $this->applyOrderConditions($rootid); } // Possibly stop if new errors happened: if (!$smwgIgnoreQueryErrors && $query->querymode != SMWQuery::MODE_DEBUG && count($this->m_errors) > 0) { $query->addErrors($this->m_errors); return new SMWQueryResult($query->getDescription()->getPrintrequests(), $query, array(), $this->m_store, false); } // *** Now execute the computed query ***// wfProfileIn('SMWSQLStore3Queries::executeMainQuery (SMW)'); $this->executeQueries($this->m_queries[$rootid]); // execute query tree, resolve all dependencies wfProfileOut('SMWSQLStore3Queries::executeMainQuery (SMW)'); switch ($query->querymode) { case SMWQuery::MODE_DEBUG: $result = $this->getDebugQueryResult($query, $rootid); break; case SMWQuery::MODE_COUNT: $result = $this->getCountQueryResult($query, $rootid); break; default: $result = $this->getInstanceQueryResult($query, $rootid); break; } $this->cleanUp(); $query->addErrors($this->m_errors); return $result; }
/** * The new SQL store's implementation of query answering. This function * works in two stages: First, the nested conditions of the given query * object are preprocessed to compute an abstract representation of the * SQL query that is to be executed. Since query conditions correspond to * joins with property tables in most cases, this abstract representation * is essentially graph-like description of how property tables are joined. * Moreover, this graph is tree-shaped, since all query conditions are * tree-shaped. Each part of this abstract query structure is represented * by an QuerySegment object in the array m_queries. * * As a second stage of processing, the thus prepared SQL query is actually * executed. Typically, this means that the joins are collapsed into one * SQL query to retrieve results. In some cases, such as in dbug mode, the * execution might be restricted and not actually perform the whole query. * * The two-stage process helps to separate tasks, and it also allows for * better optimisations: it is left to the execution engine how exactly the * query result is to be obtained. For example, one could pre-compute * partial suib-results in temporary tables (or even cache them somewhere), * instead of passing one large join query to the DB (of course, it might * be large only if the configuration of SMW allows it). For some DBMS, a * step-wise execution of the query might lead to better performance, since * it exploits the tree-structure of the joins, which is important for fast * processing -- not all DBMS might be able in seeing this by themselves. * * @param Query $query * * @return mixed depends on $query->querymode */ public function getQueryResult(Query $query) { if ((!$this->engineOptions->get('smwgIgnoreQueryErrors') || $query->getDescription() instanceof ThingDescription) && $query->querymode != Query::MODE_DEBUG && count($query->getErrors()) > 0) { return new QueryResult($query->getDescription()->getPrintrequests(), $query, array(), $this->store, false); // NOTE: we check this here to prevent unnecessary work, but we check // it after query processing below again in case more errors occurred. } elseif ($query->querymode == Query::MODE_NONE) { // don't query, but return something to printer return new QueryResult($query->getDescription()->getPrintrequests(), $query, array(), $this->store, true); } $db = $this->store->getConnection('mw.db'); $this->queryMode = $query->querymode; $this->querySegments = array(); $this->errors = array(); QuerySegment::$qnum = 0; $this->sortKeys = $query->sortkeys; // *** First compute abstract representation of the query (compilation) ***// $this->queryBuilder->setSortKeys($this->sortKeys); $this->queryBuilder->buildQuerySegmentFor($query->getDescription()); // compile query, build query "plan" $qid = $this->queryBuilder->getLastQuerySegmentId(); $this->querySegments = $this->queryBuilder->getQuerySegments(); $this->errors = $this->queryBuilder->getErrors(); if ($qid < 0) { // no valid/supported condition; ensure that at least only proper pages are delivered $qid = QuerySegment::$qnum; $q = new QuerySegment(); $q->joinTable = SMWSql3SmwIds::tableName; $q->joinfield = "{$q->alias}.smw_id"; $q->where = "{$q->alias}.smw_iw!=" . $db->addQuotes(SMW_SQL3_SMWIW_OUTDATED) . " AND {$q->alias}.smw_iw!=" . $db->addQuotes(SMW_SQL3_SMWREDIIW) . " AND {$q->alias}.smw_iw!=" . $db->addQuotes(SMW_SQL3_SMWBORDERIW) . " AND {$q->alias}.smw_iw!=" . $db->addQuotes(SMW_SQL3_SMWINTDEFIW); $this->querySegments[$qid] = $q; } if ($this->querySegments[$qid]->joinTable != SMWSql3SmwIds::tableName) { // manually make final root query (to retrieve namespace,title): $rootid = QuerySegment::$qnum; $qobj = new QuerySegment(); $qobj->joinTable = SMWSql3SmwIds::tableName; $qobj->joinfield = "{$qobj->alias}.smw_id"; $qobj->components = array($qid => "{$qobj->alias}.smw_id"); $qobj->sortfields = $this->querySegments[$qid]->sortfields; $this->querySegments[$rootid] = $qobj; } else { // not such a common case, but worth avoiding the additional inner join: $rootid = $qid; } // Include order conditions (may extend query if needed for sorting): if ($this->engineOptions->get('smwgQSortingSupport')) { $this->applyOrderConditions($rootid); } // Possibly stop if new errors happened: if (!$this->engineOptions->get('smwgIgnoreQueryErrors') && $query->querymode != Query::MODE_DEBUG && count($this->errors) > 0) { $query->addErrors($this->errors); return new QueryResult($query->getDescription()->getPrintrequests(), $query, array(), $this->store, false); } // *** Now execute the computed query ***// $this->querySegmentListResolver->setQueryMode($this->queryMode); $this->querySegmentListResolver->setQuerySegmentList($this->querySegments); // execute query tree, resolve all dependencies $this->querySegmentListResolver->resolveForSegmentId($rootid); switch ($query->querymode) { case Query::MODE_DEBUG: $result = $this->getDebugQueryResult($query, $rootid); break; case Query::MODE_COUNT: $result = $this->getCountQueryResult($query, $rootid); break; default: $result = $this->getInstanceQueryResult($query, $rootid); break; } $this->querySegmentListResolver->cleanUp(); $query->addErrors($this->errors); return $result; }
/** * Get the output string for a query in debugging mode. * * @param Query $query * * @return string */ public function getDebugQueryResult(Query $query) { $this->sortkeys = $query->sortkeys; $this->compoundConditionBuilder->setSortKeys($this->sortkeys); $sparqlCondition = $this->compoundConditionBuilder->buildCondition($query->getDescription()); $query->addErrors($this->compoundConditionBuilder->getErrors()); $entries = array(); if ($sparqlCondition instanceof SingletonCondition) { if ($sparqlCondition->condition === '') { // all URIs exist, no querying $sparql = 'None (no conditions).'; } else { $condition = $this->compoundConditionBuilder->convertConditionToString($sparqlCondition); $namespaces = $sparqlCondition->namespaces; $sparql = $this->connection->getSparqlForAsk($condition, $namespaces); } } elseif ($sparqlCondition instanceof FalseCondition) { $sparql = 'None (conditions can not be satisfied by anything).'; } else { $condition = $this->compoundConditionBuilder->convertConditionToString($sparqlCondition); $namespaces = $sparqlCondition->namespaces; $options = $this->getOptions($query, $sparqlCondition); $options['DISTINCT'] = true; $sparql = $this->connection->getSparqlForSelect('?' . self::RESULT_VARIABLE, $condition, $options, $namespaces); } $sparql = str_replace(array('[', ':', ' '), array('[', ':', ' '), $sparql); $entries['SPARQL Query'] = '<div class="smwpre">' . $sparql . '</div>'; return DebugOutputFormatter::formatOutputFor('SPARQLStore', $entries, $query); }