/** * This method sends a SPARQL query to the store. * * @param string $query The SPARQL query to send to the store. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return Result Returns result of the query. Its type depends on the type of the query. * @throws \Exception If query is no string, is malformed or an execution error occured. */ public function query($query, array $options = array()) { $queryObject = $this->queryFactory->createInstanceByQueryString($query); if ('updateQuery' == AbstractQuery::getQueryType($query)) { /* * INSERT or DELETE query */ $firstPart = substr($queryObject->getSubType(), 0, 6); // DELETE DATA query if ('delete' == $firstPart) { $statements = $this->getStatements($queryObject); // if it goes into the loop, there is more than one triple pattern in the DELETE query, // which is not allowed. $i = 0; foreach ($statements as $statement) { if (1 <= $i++) { throw new \Exception('DELETE query can not contain more than one triple pattern: ' . $query); } // if only one Statement is in the list, no exception will be thrown and in // $statement is that Statement later on and can be used. } return $this->deleteMatchingStatements($statement); // INSERT DATA or INSERT INTO query } elseif ('insert' == $firstPart) { return $this->addStatements($this->getStatements($queryObject)); // TODO Support // WITH ... DELETE ... WHERE queries // WITH ... DELETE ... INSERT ... WHERE queries } else { throw new \Exception('Not yet implemented: WITH-DELETE-WHERE and WITH-DELETE-INSERT-WHERE queries are not ' . 'supported yet.'); } } elseif ('askQuery' == AbstractQuery::getQueryType($query)) { /* * ASK query */ $statement = $this->getStatement($queryObject); return $this->hasMatchingStatement($statement); } elseif ('selectQuery' == AbstractQuery::getQueryType($query)) { /* * SELECT query */ $statement = $this->getStatement($queryObject); return $this->getMatchingStatements($statement); } else { /* * Unsupported query */ throw new \Exception('Unsupported query was given: ' . $query); } }
/** * This method sends a SPARQL query to the store. * * @param string $query The SPARQL query to send to the store. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return \Saft\Sparql\Result Returns result of the query. Depending on the query type, it returns either * an instance of ResultIterator, StatementIterator, or ResultValue * @throws \Exception If query is no string. * @throws \Exception If query is malformed. * @throws \Exception If $options[resultType] = is neither extended nor array */ public function query($query, array $options = array()) { // log it $this->addToLog(array('method' => 'query', 'parameter' => array('query' => $query, 'options' => $options))); /** * run command by myself and check, if the cache already contains the result to this query. */ $queryCacheContainer = $this->cache->load($query); // if a cache entry was found. usually at the beginning, no cache entry is available. so ask the // successor and save its result as query result in the cache. the next call of this function will // lead to reuse of cache entry. if (null !== $queryCacheContainer) { $result = $queryCacheContainer['result']; // no cache entry was found } else { // if successor is set, ask it and remember its result if ($this->successor instanceof Store) { $result = $this->successor->query($query, $options); $this->saveResult($this->queryFactory->createInstanceByQueryString($query), $result); // if successor is not set, throw exception } else { throw new \Exception('QueryCache does not support querying, only by successor.'); } } return $result; }
/** * This method sends a SPARQL query to the store. * * @param string $query The SPARQL query to send to the store. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return Result Returns result of the query. Its type depends on the type of the query. * @throws \Exception If query is no string. * @throws \Exception If query is malformed. * @throws \Exception If query is a DELETE query and contains quads, where the graph of one quad is of type var * @throws \Exception If a non-graph query contains no triples and quads. * @todo handle multiple graphs in FROM clause */ public function query($query, array $options = array()) { $queryObject = $this->queryFactory->createInstanceByQueryString($query); $queryParts = $queryObject->getQueryParts(); // if a non-graph query was given, we assume triples or quads. If neither quads nor triples were found, // throw an exception. if (false === $queryObject->isGraphQuery() && false === isset($queryParts['triple_pattern']) && false === isset($queryParts['quad_pattern'])) { throw new \Exception('Non-graph queries must have triples or quads.'); } // execute query on the store $result = $this->store->query($query); /* * special case: if you execute a SELECT COUNT(*) query, ARC2 will return the number of triples instead of a result set */ $countCheck = preg_match('/selectcount\\([a-z*]\\)(from|where)/si', preg_replace('/\\s+/', '', $query)); if (1 == $countCheck) { $variable = 'callret-0'; // build a set result, because the user expects it as result type because a SELECT query // was sent. $setResult = $this->resultFactory->createSetResult(array(array($variable => $this->nodeFactory->createLiteral($result, 'http://www.w3.org/2001/XMLSchema#int')))); $setResult->setVariables(array($variable)); return $setResult; /* * ARC2 does not support quads, especially not in DELETE queries. The following code construct * tries to close that gap by transforming the query in a one which ARC2 can understand. * * This part transform queries of the kind: * * DELETE WHERE { * Graph <http://localhost/Saft/TestGraph/> { * ?s ?p ?o . * } * } * * to SPARQL+ ones: * * DELETE FROM <http://localhost/Saft/TestGraph/> { * ?s ?p ?o . * } * WHERE { * ?s ?p ?o . * } * * * IMPORTANT: Please adapt * https://github.com/SaftIng/safting.github.io/blob/master/doc/phpframework/addition/ARC2.md * if you change the support for SPARQL 1.0/1.1 here! */ } elseif ($queryObject->isUpdateQuery() && isset($queryParts['quad_pattern']) && 'deleteWhere' === $queryParts['sub_type']) { foreach ($queryParts['quad_pattern'] as $quad) { if ('uri' != $quad['g_type']) { throw new \Exception('The graph of a quad must be an URI here.'); } // subject $s = NodeUtils::createNodeInstance($this->nodeFactory, $quad['s'], $quad['s_type']); $s = SparqlUtils::getNodeInSparqlFormat($s); // predicate $p = NodeUtils::createNodeInstance($this->nodeFactory, $quad['p'], $quad['p_type']); $p = SparqlUtils::getNodeInSparqlFormat($p); // object $o = NodeUtils::createNodeInstance($this->nodeFactory, $quad['o'], $quad['o_type'], $quad['o_datatype'], $quad['o_lang']); $o = SparqlUtils::getNodeInSparqlFormat($o); return $this->query('DELETE FROM <' . $quad['g'] . '> {' . $s . ' ' . $p . ' ' . $o . ' } WHERE {' . $s . ' ' . $p . ' ' . $o . ' }'); } /* * SELECT query */ } elseif ('selectQuery' === AbstractQuery::getQueryType($query)) { /* * For a SELECT query the result looks like: * * array( * 'query_time' => 0.2 * 'query_type' => 'select', * 'result' => array( * 'variables' => array('s', 'o') * 'rows' => * array( * 's' => "http://s/" * 's type' => 'uri', * 'o' => '42', * 'o type' => "literal" * 'o datatype' => "http://www.w3.org/2001/XMLSchema#string" * ) */ $entries = array(); // go through all rows foreach ($result['result']['rows'] as $row) { $newEntry = array(); foreach ($result['result']['variables'] as $variable) { // checks for variable type // example: $row['s type'] switch ($row[$variable . ' type']) { // ARC2 does not differenciate between typed literal and literal, like Virtuoso does // for instance. You have to check for lang and datatype key by yourself. case 'literal': // if language is set if (isset($row[$variable . ' lang'])) { $newEntry[$variable] = $this->nodeFactory->createLiteral($row[$variable], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString', $row[$variable . ' lang']); // if datatype is set } elseif (isset($row[$variable . ' datatype'])) { $newEntry[$variable] = $this->nodeFactory->createLiteral($row[$variable], $row[$variable . ' datatype']); // if neither one is set, we assume its a string and use xsd:string as datatype } else { $newEntry[$variable] = $this->nodeFactory->createLiteral($row[$variable], 'http://www.w3.org/2001/XMLSchema#string'); } break; case 'uri': $newEntry[$variable] = $this->nodeFactory->createNamedNode($row[$variable]); break; } } $entries[] = $newEntry; } // Create and fill SetResult instance $setResult = $this->resultFactory->createSetResult($entries); $setResult->setVariables($result['result']['variables']); return $setResult; } else { if ('askQuery' === AbstractQuery::getQueryType($query)) { return $this->resultFactory->createValueResult($result['result']); } else { return $this->resultFactory->createEmptyResult(); } } }
/** * This method sends a SPARQL query to the store. * * @param string $query The SPARQL query to send to the store. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return Result Returns result of the query. Its type depends on the type of the query. * @throws \Exception If query is no string. * @throws \Exception If query is malformed. * @throws \Exception If PDO query is false. * @todo handle multiple graphs in FROM clause */ public function query($query, array $options = array()) { $queryObject = $this->queryFactory->createInstanceByQueryString($query); $queryParts = $queryObject->getQueryParts(); // if a non-graph query was given, we assume triples or quads. If neither quads nor triples were found, // throw an exception. if (false === $queryObject->isGraphQuery() && false === isset($queryParts['triple_pattern']) && false === isset($queryParts['quad_pattern'])) { throw new \Exception('Non-graph queries must have triples or quads.'); } /** * SPARQL query (usually to fetch data) */ if ('selectQuery' === AbstractQuery::getQueryType($query)) { // force extended result to have detailed information about given result entries, such as datatype and // language information. $sparqlQuery = 'define output:format "JSON"' . PHP_EOL . $query; // escape characters that delimit the query within the query using addcslashes $graphUri = 'NULL'; $graphSpec = ''; // escape characters that delimit the query within the query $sparqlQuery = $graphSpec . 'CALL DB.DBA.SPARQL_EVAL(\'' . addcslashes($sparqlQuery, '\'\\') . '\', ' . '\'' . $graphUri . '\', 0)'; // execute query try { $pdoQuery = $this->connection->prepare($sparqlQuery, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY)); $pdoQuery->execute(); } catch (\PDOException $e) { throw new \Exception($e->getMessage()); } $entries = array(); // transform result to array in case we fired a non-UPDATE query if (false !== $pdoQuery) { $resultArray = json_decode(current(current($pdoQuery->fetchAll(\PDO::FETCH_ASSOC))), true); $variables = $resultArray['head']['vars']; // in case the result was empty, Virtuoso does not return a list of variables, which are // usually located in the SELECT part. so we try to extract the variables by ourselves. if (0 == count($variables)) { $variables = $queryParts['variables']; } /** * go through all bindings and create according objects for SetResult instance. * * $bindingParts will look like: * * array( * 's' => array( * 'type' => 'uri', * 'value' => '...' * ), ... * ) */ foreach ($resultArray['results']['bindings'] as $bindingParts) { $newEntry = array(); /** * A part looks like: * array( * 'type' => 'uri', * 'value' => '...' * ) */ foreach ($bindingParts as $variable => $part) { switch ($part['type']) { /** * Literal (language'd) */ case 'literal': $newEntry[$variable] = $this->nodeFactory->createLiteral($part['value'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString', $part['xml:lang']); break; /** * Typed-Literal */ /** * Typed-Literal */ case 'typed-literal': $newEntry[$variable] = $this->nodeFactory->createLiteral($part['value'], $part['datatype']); break; /** * NamedNode */ /** * NamedNode */ case 'uri': $newEntry[$variable] = $this->nodeFactory->createNamedNode($part['value']); break; default: throw new \Exception('Unknown type given.'); break; } } $entries[] = $newEntry; } $setResult = $this->resultFactory->createSetResult(new \ArrayIterator($entries)); $setResult->setVariables($variables); return $setResult; } else { throw new \Exception('PDO query is false.'); } /** * SPARPQL Update query */ } else { $sparqlQuery = 'SPARQL ' . $query; // execute query try { $pdoQuery = $this->connection->prepare($sparqlQuery, array(\PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY)); $pdoQuery->execute(); } catch (\PDOException $e) { throw new \Exception($e->getMessage()); } // ask result if ('askQuery' === AbstractQuery::getQueryType($query)) { $pdoResult = $pdoQuery->fetchAll(\PDO::FETCH_ASSOC); return $this->resultFactory->createValueResult(true !== empty($pdoResult)); } else { return $this->resultFactory->createEmptyResult(); } } }
/** * This method sends a SPARQL query to the store. * * @param string $query The SPARQL query to send to the store. * @param array $options optional It contains key-value pairs and should provide additional * introductions for the store and/or its adapter(s). * @return Result Returns result of the query. Its type depends on the type of the query. * @throws \Exception If query is no string. * @throws \Exception If query is malformed. * @throws StoreException If server returned an error. * @todo add support for DESCRIBE queries */ public function query($query, array $options = array()) { $queryObject = $this->queryFactory->createInstanceByQueryString($query); $queryParts = $queryObject->getQueryParts(); /** * SPARQL query (usually to fetch data) */ if ('selectQuery' == AbstractQuery::getQueryType($query)) { $resultArray = json_decode($this->client->sendSparqlSelectQuery($query), true); $entries = array(); /** * go through all bindings and create according objects for SetResult instance. * * $bindingParts will look like: * * array( * 's' => array( * 'type' => 'uri', * 'value' => '...' * ), ... * ) */ foreach ($resultArray['results']['bindings'] as $bindingParts) { $newEntry = array(); /** * A part looks like: * array( * 'type' => 'uri', * 'value' => '...' * ) */ foreach ($bindingParts as $variable => $part) { switch ($part['type']) { /** * Literal (language'd) */ case 'literal': $lang = null; if (isset($part['xml:lang'])) { $lang = $part['xml:lang']; } $newEntry[$variable] = $this->nodeFactory->createLiteral($part['value'], 'http://www.w3.org/1999/02/22-rdf-syntax-ns#langString', $lang); break; /** * Typed-Literal */ /** * Typed-Literal */ case 'typed-literal': $newEntry[$variable] = $this->nodeFactory->createLiteral($part['value'], $part['datatype']); break; /** * NamedNode */ /** * NamedNode */ case 'uri': $newEntry[$variable] = $this->nodeFactory->createNamedNode($part['value']); break; /** * BlankNode */ /** * BlankNode */ case 'bnode': $newEntry[$variable] = $this->nodeFactory->createBlankNode($part['value']); break; default: throw new \Exception('Unknown type given.'); break; } } $entries[] = $newEntry; } $return = $this->resultFactory->createSetResult($entries); $return->setVariables($resultArray['head']['vars']); /** * SPARPQL Update query */ } else { $result = $this->client->sendSparqlUpdateQuery($query); $decodedResult = json_decode($result, true); if ('askQuery' === AbstractQuery::getQueryType($query)) { $askResult = json_decode($result, true); if (true === isset($askResult['boolean'])) { $return = $this->resultFactory->createValueResult($askResult['boolean']); // assumption here is, if a string was returned, something went wrong. } elseif (0 < strlen($result)) { throw new \Exception($result); } else { $return = $this->resultFactory->createEmptyResult(); } // usually a SPARQL result does not return a string. if it does anyway, assume there is an error. } elseif (null === $decodedResult && 0 < strlen($result)) { throw new \Exception($result); } else { $return = $this->resultFactory->createEmptyResult(); } } return $return; }