Documentation is available at RdqlDbEngine.php
- <?php
- // ----------------------------------------------------------------------------------
- // Class: RdqlDbEngine
- // ----------------------------------------------------------------------------------
- /**
- * This class performs as RDQL query on a DbModel.
- *
- * Provided an rdql query parsed into an array of php variables and constraints
- * at first the engine generates an sql statement and queries the database for
- * tuples matching all patterns from the WHERE clause of the given RDQL query.
- * Subsequently the result set is is filtered with evaluated boolean expressions
- * from the AND clause of the given RDQL query.
- *
- * <BR><BR>History:<UL>
- * <LI>09-27-2004 : Multiple queries bug fixed</LI>
- * <LI>05-12-2004 : Bug in the handling of empty Literals fixed.</LI>
- * <LI>08-29-2003 : Function filterTuples(): some bugs fixed:
- * - strEqExpr with NE operator
- * - regExExpr combined with other expr. and negation (!)
- * e.g. !(?x ~~ "/sth/" && ?x > 5)
- * <LI>07-27-2003 : First version of this class</LI>
- *
- * @version V0.9.1
- * @author Radoslaw Oldakowski <radol@gmx.de>
- *
- * @package rdql
- * @access public
- */
- Class RdqlDbEngine extends RdqlEngine {
- /**
- * Parsed query variables and constraints.
- *
- * @var array ['selectVars'][] = ?VARNAME
- * ['sources'][] = URI
- * ['patterns'][]['subject']['value'] = VARorURI
- * ['predicate']['value'] = VARorURI
- * ['object']['value'] = VARorURIorLiterl
- * ['is_literal'] = boolean
- * ['l_lang'] = string
- * ['l_dtype'] = string
- * ['filters'][]['string'] = string
- * ['evalFilterStr'] = string
- * ['reqexEqExprs'][]['var'] = ?VARNAME
- * ['operator'] = (eq | ne)
- * ['regex'] = string
- * ['strEqExprs'][]['var'] = ?VARNAME
- * ['operator'] = (eq | ne)
- * ['value'] = string
- * ['value_type'] = ('variable' | 'URI' | 'Literal')
- * ['value_lang'] = string
- * ['value_dtype'] = string
- * ['numExpr']['vars'][] = ?VARNAME
- * ( [] stands for an integer index - 0..N )
- * @access private
- */
- var $parsedQuery;
- /**
- * When an RDQL query is performed on a DbModel, in first step the engine searches
- * in database for triples matching the Rdql-WHERE clause. A recordSet is returned.
- * $rsIndexes maps select and filter variables to their corresponding indexes
- * in the returned recordSet.
- *
- * @var array [?VARNAME]['value'] = integer
- * ['nType'] = integer
- * ['l_lang'] = integer
- * ['l_dtype'] = integer
- * @access private
- */
- var $rsIndexes;
- /**
- * Perform an RDQL Query on the given DbModel.
- *
- * @param object DbModel $dbModel
- * @param array &$parsedQuery (the same format as $this->parsedQuery)
- * @param boolean $returnNodes
- * @return array [][?VARNAME] = object Node (if $returnNodes = TRUE)
- * OR array [][?VARNAME] = string
- * @access public
- */
- function & queryModel(&$dbModel, &$parsedQuery, $returnNodes = TRUE) {
- $this->parsedQuery = &$parsedQuery;
- $sql = $this->generateSql($dbModel->modelID);
- $recordSet =& $dbModel->dbConn->execute($sql);
- $queryResult = $this->filterQueryResult($recordSet);
- if ($returnNodes)
- return $this->toNodes($queryResult);
- else
- return $this->toString($queryResult);
- }
- /**
- * Generate an SQL string to query the database for tuples matching all patterns
- * of $parsedQuery.
- *
- * @param integer $modelID
- * @return string
- * @access private
- */
- function generateSql($modelID) {
- $sql = $this->generateSql_SelectClause();
- $sql .= $this->generateSql_FromClause();
- $sql .= $this->generateSql_WhereClause($modelID);
- return $sql;
- }
- /**
- * Generate SQL SELECT clause.
- *
- * @return string
- * @throws PHPError
- * @access private
- */
- function generateSql_SelectClause() {
- $sql_select = 'SELECT';
- $index = 0;
- $this->rsIndexes = array();
- foreach ($this->parsedQuery['selectVars'] as $var)
- $sql_select .= $this->_generateSql_SelectVar($var, $index);
- if (isset($this->parsedQuery['filters'])) {
- foreach ($this->parsedQuery['filters'] as $n => $filter) {
- // variables from numeric expressions
- foreach ($filter['numExprVars'] as $numVar)
- $sql_select .= $this->_generateSql_SelectVar($numVar, $index);
- // variables from regex equality expressions
- foreach ($filter['regexEqExprs'] as $regexEqExpr)
- $sql_select .= $this->_generateSql_SelectVar($regexEqExpr['var'], $index);
- // variables from string equality expressions
- foreach ($filter['strEqExprs'] as $strEqVar)
- $sql_select .= $this->_generateSql_SelectVar($strEqVar['var'], $index);
- }
- }
- return rtrim($sql_select, " , ");
- }
- /**
- * Generate SQL FROM clause
- *
- * @return string
- * @access private
- */
- function generateSql_FromClause() {
- $sql_from = ' FROM';
- foreach ($this->parsedQuery['patterns'] as $n => $v)
- $sql_from .= ' statements s' .($n+1) .' , ';
- return rtrim($sql_from, ' , ');
- }
- /**
- * Generate an SQL WHERE clause
- *
- * @param integer $modelID
- * @return string
- * @access private
- */
- function generateSql_WhereClause($modelID) {
- $sql_where = ' WHERE';
- $count_patterns = count($this->parsedQuery['patterns']);
- foreach ($this->parsedQuery['patterns'] as $n => $pattern) {
- $sql_where .= ' s' .($n+1) .'.modelID=' .$modelID .' AND';
- foreach ($pattern as $key => $val_1)
- if ($val_1['value'] && $val_1['value']{0}=='?') {
- $sql_tmp = ' s' .($n+1) .'.' .$key .'=';
- // find internal bindings
- switch ($key) {
- case 'subject':
- if ($pattern['subject']['value'] == $pattern['predicate']['value'])
- $sql_where .= $sql_tmp .'s' .($n+1) .'.predicate AND';
- elseif ($pattern['subject']['value'] == $pattern['object']['value'])
- $sql_where .= $sql_tmp .'s' .($n+1) .'.object AND';
- break;
- case 'predicate':
- if ($pattern['predicate']['value'] == $pattern['object']['value'])
- $sql_where .= $sql_tmp .'s' .($n+1) .'.object AND';
- }
- // find external bindings
- for ($i=$n+1; $i<$count_patterns; $i++)
- foreach ($this->parsedQuery['patterns'][$i] as $key2 => $val_2)
- if ($val_1['value']==$val_2['value']) {
- $sql_where .= $sql_tmp .'s' .($i+1) .'.' .$key2 .' AND';
- break 2;
- }
- }else {
- $sql_where .= ' s' .($n+1) .'.' .$key ."='" .$val_1['value'] ."' AND";
- if ($key == 'object' && isset($val_1['is_literal'])) {
- $sql_where .= ' s' .($n+1) .".object_is='l' AND";
- $sql_where .= ' s' .($n+1) .".l_datatype='" .$val_1['l_dtype'] ."' AND";
- $sql_where .= ' s' .($n+1) .".l_language='" .$val_1['l_lang'] ."' AND";
- }
- }
- }
- return rtrim($sql_where, ' AND');
- }
- /**
- * Filter tuples containing variables matching all patterns from the WHERE clause
- * of an RDQL query. As a result of a database query using ADOdb these tuples
- * are returned as an ADORecordSet object, which is then passed to this function.
- *
- * @param object ADORecordSet &$recordSet
- * @return array [][?VARNAME]['value'] = string
- * ['nType'] = string
- * ['l_lang'] = string
- * ['l_dtype'] = string
- * @access private
- */
- function filterQueryResult(&$recordSet) {
- $queryResult=array();
- if (isset($this->parsedQuery['filters'])) {
- while (!$recordSet->EOF) {
- foreach ($this->parsedQuery['filters'] as $filter) {
- $evalFilterStr = $filter['evalFilterStr'];
- // evaluate regex equality expressions of each filter
- foreach ($filter['regexEqExprs'] as $i => $expr) {
- preg_match($expr['regex'], $recordSet->fields[$this->rsIndexes[$expr['var']]['value']], $match);
- $op = substr($expr['operator'], 0,1);
- if (($op != '!' && !isset($match[0])) || ($op == '!' && isset($match[0])))
- $evalFilterStr = str_replace("##RegEx_$i##", 'FALSE', $evalFilterStr);
- else
- $evalFilterStr = str_replace("##RegEx_$i##", 'TRUE', $evalFilterStr);
- }
- // evaluate string equality expressions
- foreach ($filter['strEqExprs'] as $i => $expr) {
- $exprBoolVal = 'FALSE';
- switch ($expr['value_type']) {
- case 'variable':
- if (($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] ==
- $recordSet->fields[$this->rsIndexes[$expr['value']]['value']] &&
- $expr['operator'] == 'eq') ||
- ($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] !=
- $recordSet->fields[$this->rsIndexes[$expr['value']]['value']] &&
- $expr['operator'] == 'ne'))
- $exprBoolVal = 'TRUE';
- break;
- case 'URI':
- if (isset($this->rsIndexes[$expr['var']]['nType']) &&
- $recordSet->fields[$this->rsIndexes[$expr['var']]['nType']] == 'l') {
- if ($expr['operator'] == 'ne')
- $exprBoolVal = 'TRUE';
- break;
- }
- if (($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] ==
- $expr['value'] && $expr['operator'] == 'eq') ||
- ($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] !=
- $expr['value'] && $expr['operator'] == 'ne'))
- $exprBoolVal = 'TRUE';
- break;
- case 'Literal':
- if (!isset($this->rsIndexes[$expr['var']]['nType']) ||
- $recordSet->fields[$this->rsIndexes[$expr['var']]['nType']] != 'l') {
- if ($expr['operator'] == 'ne')
- $exprBoolVal = 'TRUE';
- break;
- }
- $filterLiteral= new Literal($expr['value'],$expr['value_lang']);
- $filterLiteral->setDatatype($expr['value_dtype']);
- $resultLiteral=new Literal($recordSet->fields[$this->rsIndexes[$expr['var']]['value']]);
- $resultLiteral->setDatatype($recordSet->fields[$this->rsIndexes[$expr['var']]['l_dtype']]);
- $resultLiteral->setLanguage($recordSet->fields[$this->rsIndexes[$expr['var']]['l_lang']]);
- $equal=$resultLiteral->equals($filterLiteral);
- if (($equal && $expr['operator'] == 'eq') ||
- (!$equal && $expr['operator'] == 'ne'))
- $exprBoolVal = 'TRUE';
- else
- $exprBoolVal = 'FALSE';
- }
- $evalFilterStr = str_replace("##strEqExpr_$i##", $exprBoolVal, $evalFilterStr);
- }
- // evaluate numerical expressions
- foreach ($filter['numExprVars'] as $varName) {
- $varValue = "'" .$recordSet->fields[$this->rsIndexes[$varName]['value']] ."'";
- $evalFilterStr = str_replace($varName, $varValue, $evalFilterStr);
- }
- eval("\$filterBoolVal = $evalFilterStr; \$eval_filter_ok = TRUE;");
- if (!isset($eval_filter_ok))
- trigger_error(RDQL_AND_ERR ."'" .htmlspecialchars($filter['string']) ."'", E_USER_ERROR);
- if (!$filterBoolVal) {
- $recordSet->MoveNext();
- continue 2;
- }
- }
- $queryResult[] = $this->_convertRsRowToQueryResultRow($recordSet->fields);
- $recordSet->MoveNext();
- }
- }else
- while (!$recordSet->EOF) {
- $queryResult[] = $this->_convertRsRowToQueryResultRow($recordSet->fields);
- $recordSet->MoveNext();
- }
- return $queryResult;
- }
- /**
- * Serialize variable values of $queryResult to string.
- *
- * @param array &$queryResult [][?VARNAME]['value'] = string
- * ['nType'] = string
- * ['l_lang'] = string
- * ['l_dtype'] = string
- * @return array [][?VARNAME] = string
- * @access private
- */
- function toString(&$queryResult) {
- // if a result set is empty return only variable sames
- if (count($queryResult) == 0) {
- foreach ($this->parsedQuery['selectVars'] as $selectVar)
- $res[0][$selectVar] = NULL;
- return $res;
- }
- $res = array();
- foreach ($queryResult as $n => $var)
- foreach ($var as $varname => $varProperties)
- if ($varProperties['nType'] == 'r' || $varProperties['nType'] == 'b')
- $res[$n][$varname] = '<' .$varProperties['value'] .'>';
- else {
- $res[$n][$varname] = '"' .$varProperties['value'] .'"';
- if ($varProperties['l_lang'] != NULL)
- $res[$n][$varname] .= ' (xml:lang="' .$varProperties['l_lang'] .'")';
- if ($varProperties['l_dtype'] != NULL)
- $res[$n][$varname] .= ' (rdf:datatype="' .$varProperties['l_dtype'] .'")';
- }
- return $res;
- }
- /**
- * Convert variable values of $queryResult to objects (Node).
- *
- * @param array &$queryResult [][?VARNAME]['value'] = string
- * ['nType'] = string
- * ['l_lang'] = string
- * ['l_dtype'] = string
- * @return array [][?VARNAME] = object Node
- * @access private
- */
- function toNodes(&$queryResult) {
- // if a result set is empty return only variable sames
- if (count($queryResult) == 0) {
- foreach ($this->parsedQuery['selectVars'] as $selectVar)
- $res[0][$selectVar] = NULL;
- return $res;
- }
- $res = array();
- foreach ($queryResult as $n => $var)
- foreach ($var as $varname => $varProperties)
- if ($varProperties['nType'] == 'r')
- $res[$n][$varname] = new Resource($varProperties['value']);
- elseif ($varProperties['nType'] == 'b')
- $res[$n][$varname] = new BlankNode($varProperties['value']);
- else {
- $res[$n][$varname] = new Literal($varProperties['value'], $varProperties['l_lang']);
- if ($varProperties['l_dtype'] != NULL)
- $res[$n][$varname]->setDataType($varProperties['l_dtype']);
- }
- return $res;
- }
- /**
- * Generate a piece of an sql select statement for a variable.
- * Look first if the given variable is defined as a pattern object.
- * (So you can select the node type, literal lang and dtype)
- * If not found - look for subjects and select node label and type.
- * If there is no result either go to predicates.
- * Predicates are always resources therefore select only the node label.
- *
- * @param string $varName
- * @return string
- * @access private
- */
- function _generateSql_SelectVar ($varName, &$index) {
- $sql_select = '';
- if (array_key_exists($varName, $this->rsIndexes))
- return NULL;
- foreach ($this->parsedQuery['patterns'] as $n => $pattern)
- if ($varName == $pattern['object']['value']) {
- // select the object label
- $sql_select .= " s" .++$n .".object as _" .ltrim($varName, "?") ." , ";
- $this->rsIndexes[$varName]['value'] = $index++;
- // select the node type
- $sql_select .= " s" .$n .".object_is , ";
- $this->rsIndexes[$varName]['nType'] = $index++;
- // select the object language
- $sql_select .= " s" .$n .".l_language , ";
- $this->rsIndexes[$varName]['l_lang'] = $index++;
- // select the object dtype
- $sql_select .= " s" .$n .".l_datatype , ";
- $this->rsIndexes[$varName]['l_dtype'] = $index++;
- return $sql_select;
- }
- foreach ($this->parsedQuery['patterns'] as $n => $pattern)
- if ($varName == $pattern['subject']['value']) {
- // select the object label
- $sql_select .= " s" .++$n .".subject as _" .ltrim($varName, "?") ." , ";
- $this->rsIndexes[$varName]['value'] = $index++;
- // select the node type
- $sql_select .= " s" .$n .".subject_is , ";
- $this->rsIndexes[$varName]['nType'] = $index++;
- return $sql_select;
- }
- foreach ($this->parsedQuery['patterns'] as $n => $pattern)
- if ($varName == $pattern['predicate']['value']) {
- // select the object label
- $sql_select .= " s" .++$n .".predicate as _" .ltrim($varName, "?") ." , ";
- $this->rsIndexes[$varName]['value'] = $index++;
- return $sql_select;
- }
- }
- /**
- * Converts a single row of ADORecordSet->fields array to the format of
- * $queryResult array using pointers to indexes ($this->rsIndexes) in RecordSet->fields.
- *
- * @param array &$record [] = string
- * @return array [?VARNAME]['value'] = string
- * ['nType'] = string
- * ['l_lang'] = string
- * ['l_dtype'] = string
- * @access private
- */
- function _convertRsRowToQueryResultRow(&$record) {
- // return only select variables (without conditional variables from the AND clause)
- foreach ($this->parsedQuery['selectVars'] as $selectVar) {
- $resultRow[$selectVar]['value'] = $record[$this->rsIndexes[$selectVar]['value']];
- if (isset($this->rsIndexes[$selectVar]['nType']))
- $resultRow[$selectVar]['nType'] = $record[$this->rsIndexes[$selectVar]['nType']];
- // is a predicate then
- else
- $resultRow[$selectVar]['nType'] = 'r';
- if ($resultRow[$selectVar]['nType'] == 'l') {
- $resultRow[$selectVar]['l_lang'] = $record[$this->rsIndexes[$selectVar]['l_lang']];
- $resultRow[$selectVar]['l_dtype'] = $record[$this->rsIndexes[$selectVar]['l_dtype']];
- }
- }
- return $resultRow;
- }
- } // end: Class RdqlDbEngine
- ?>
Documentation generated on Fri, 17 Dec 2004 16:17:27 +0100 by phpDocumentor 1.3.0RC3