Source for file RdqlDbEngine.php

Documentation is available at RdqlDbEngine.php

  1. <?php
  2.  
  3. // ----------------------------------------------------------------------------------
  4. // Class: RdqlDbEngine
  5. // ----------------------------------------------------------------------------------
  6.  
  7. /**
  8.  * This class performs as RDQL query on a DbModel.
  9.  *
  10.  * Provided an rdql query parsed into an array of php variables and constraints
  11.  * at first the engine generates an sql statement and queries the database for
  12.  * tuples matching all patterns from the WHERE clause of the given RDQL query.
  13.  * Subsequently the result set is is filtered with evaluated boolean expressions
  14.  * from the AND clause of the given RDQL query.
  15.  *
  16.  * @version  $Id: fsource_rdql__rdqlRdqlDbEngine.php.html 443 2007-06-01 16:25:38Z cax $
  17.  * @author   Radoslaw Oldakowski <radol@gmx.de>
  18.  *
  19.  * @package rdql
  20.  * @access public
  21.  */
  22.  
  23. Class RdqlDbEngine extends RdqlEngine {
  24.  
  25.  
  26. /**
  27.  * Parsed query variables and constraints.
  28.  *
  29.  * @var     array   ['selectVars'][] = ?VARNAME
  30.  *                   ['sources'][] = URI
  31.  *                   ['patterns'][]['subject']['value'] = VARorURI
  32.  *                                 ['predicate']['value'] = VARorURI
  33.  *                                 ['object']['value'] = VARorURIorLiterl
  34.  *                                           ['is_literal'] = boolean
  35.  *                                           ['l_lang'] = string
  36.  *                                           ['l_dtype'] = string
  37.  *                   ['filters'][]['string'] = string
  38.  *                                ['evalFilterStr'] = string
  39.  *                                ['reqexEqExprs'][]['var'] = ?VARNAME
  40.  *                                                  ['operator'] = (eq | ne)
  41.  *                                                  ['regex'] = string
  42.  *                                ['strEqExprs'][]['var'] = ?VARNAME
  43.  *                                                ['operator'] = (eq | ne)
  44.  *                                                ['value'] = string
  45.  *                                                ['value_type'] = ('variable' | 'URI' | 'Literal')
  46.  *                                                ['value_lang'] = string
  47.  *                                                ['value_dtype'] = string
  48.  *                                ['numExpr']['vars'][] = ?VARNAME
  49.  *                          ( [] stands for an integer index - 0..N )
  50.  * @access    private
  51.  */
  52.  var $parsedQuery;
  53.  
  54.  
  55. /**
  56.  * When an RDQL query is performed on a DbModel, in first step the engine searches
  57.  * in database for triples matching the Rdql-WHERE clause. A recordSet is returned.
  58.  * $rsIndexes maps select and filter variables to their corresponding indexes
  59.  * in the returned recordSet.
  60.  *
  61.  * @var     array [?VARNAME]['value'] = integer
  62.  *                           ['nType'] = integer
  63.  *                           ['l_lang'] = integer
  64.  *                           ['l_dtype'] = integer
  65.  * @access    private
  66.  */
  67.  var $rsIndexes;
  68.  
  69.  
  70.  /**
  71.  * Perform an RDQL Query on the given DbModel.
  72.  *
  73.  * @param   object  DbModel $dbModel 
  74.  * @param   array   &$parsedQuery  (the same format as $this->parsedQuery)
  75.  * @param   boolean $returnNodes 
  76.  * @return  array   [][?VARNAME] = object Node  (if $returnNodes = TRUE)
  77.  *       OR  array   [][?VARNAME] = string
  78.  * @access  public
  79.  */
  80.  function queryModel(&$dbModel&$parsedQuery$returnNodes TRUE{
  81.  
  82.    $this->parsedQuery &$parsedQuery;
  83.  
  84.    $sql $this->generateSql($dbModel->modelID);
  85.    $recordSet =$dbModel->dbConn->execute($sql);
  86.    $queryResult $this->filterQueryResult($recordSet);
  87.  
  88.    if ($returnNodes)
  89.       return $this->toNodes($queryResult);
  90.    else
  91.       return $this->toString($queryResult);
  92.  }
  93.  
  94.  
  95.  /**
  96.  * Generate an SQL string to query the database for tuples matching all patterns
  97.  * of $parsedQuery.
  98.  *
  99.  * @param   integer $modelID 
  100.  * @return  string 
  101.  * @access    private
  102.  */
  103.  function generateSql($modelID{
  104.  
  105.    $sql  $this->generateSql_SelectClause();
  106.    $sql .= $this->generateSql_FromClause();
  107.    $sql .= $this->generateSql_WhereClause($modelID);
  108.    return $sql;
  109.  }
  110.  
  111.  
  112. /**
  113.  * Generate SQL SELECT clause.
  114.  *
  115.  * @return  string 
  116.  * @throws  PHPError
  117.  * @access    private
  118.  */
  119.  function generateSql_SelectClause({
  120.  
  121.    $sql_select 'SELECT';
  122.    $index 0;
  123.    $this->rsIndexes array();
  124.  
  125.    foreach ($this->parsedQuery['selectVars'as $var)
  126.      $sql_select .= $this->_generateSql_SelectVar($var$index);
  127.  
  128.    if (isset($this->parsedQuery['filters'])) {
  129.        foreach ($this->parsedQuery['filters'as $n => $filter{
  130.  
  131.          // variables from numeric expressions
  132.          foreach ($filter['numExprVars'as $numVar)
  133.            $sql_select .= $this->_generateSql_SelectVar($numVar$index);
  134.  
  135.          // variables from regex equality expressions
  136.          foreach ($filter['regexEqExprs'as $regexEqExpr)
  137.            $sql_select .= $this->_generateSql_SelectVar($regexEqExpr['var']$index);
  138.  
  139.          // variables from string equality expressions
  140.          foreach ($filter['strEqExprs'as $strEqVar)
  141.              $sql_select .= $this->_generateSql_SelectVar($strEqVar['var']$index);
  142.        }
  143.    }
  144.  
  145.    return rtrim($sql_select" , ");
  146.  }
  147.  
  148.  
  149. /**
  150.  * Generate SQL FROM clause
  151.  *
  152.  * @return  string 
  153.  * @access    private
  154.  */
  155.  function generateSql_FromClause({
  156.  
  157.    $sql_from ' FROM';
  158.    foreach ($this->parsedQuery['patterns'as $n => $v)
  159.      $sql_from .= ' statements s' .($n+1.' , ';
  160.  
  161.    return rtrim($sql_from' , ');
  162.  }
  163.  
  164.  
  165. /**
  166.  * Generate an SQL WHERE clause
  167.  *
  168.  * @param   integer $modelID 
  169.  * @return  string 
  170.  * @access    private
  171.  */
  172.  function generateSql_WhereClause($modelID{
  173.  
  174.    $sql_where ' WHERE';
  175.    $count_patterns count($this->parsedQuery['patterns']);
  176.    foreach ($this->parsedQuery['patterns'as $n => $pattern{
  177.      $sql_where .= ' s' .($n+1.'.modelID=' .$modelID .' AND';
  178.      foreach ($pattern as $key => $val_1)
  179.        if ($val_1['value'&& $val_1['value']{0}=='?'{
  180.          $sql_tmp ' s' .($n+1.'.' .$key .'=';
  181.          // find internal bindings
  182.          switch ($key{
  183.            case 'subject':
  184.                     if ($pattern['subject']['value'== $pattern['predicate']['value'])
  185.                        $sql_where .= $sql_tmp .'s' .($n+1.'.predicate AND';
  186.                     elseif ($pattern['subject']['value'== $pattern['object']['value'])
  187.                        $sql_where .= $sql_tmp .'s' .($n+1.'.object AND';
  188.                     break;
  189.            case 'predicate':
  190.                     if ($pattern['predicate']['value'== $pattern['object']['value'])
  191.                        $sql_where .= $sql_tmp .'s' .($n+1.'.object AND';
  192.          }
  193.          // find external bindings
  194.          for ($i=$n+1$i<$count_patterns$i++)
  195.              foreach ($this->parsedQuery['patterns'][$ias $key2 => $val_2)
  196.                if ($val_1['value']==$val_2['value']{
  197.                   $sql_where .= $sql_tmp .'s' .($i+1.'.' .$key2 .' AND';
  198.                   break 2;
  199.                }
  200.        }else {
  201.           $sql_where .= ' s' .($n+1.'.' .$key ."='" .$val_1['value'."' AND";
  202.           if ($key == 'object' && isset($val_1['is_literal'])) {
  203.               $sql_where .= ' s' .($n+1.".object_is='l' AND";
  204.               $sql_where .= ' s' .($n+1.".l_datatype='" .$val_1['l_dtype'."' AND";
  205.               $sql_where .= ' s' .($n+1.".l_language='" .$val_1['l_lang'."' AND";
  206.           }
  207.        }
  208.    }
  209.    return rtrim($sql_where' AND');
  210.  }
  211.  
  212.  
  213. /**
  214.  * Filter tuples containing variables matching all patterns from the WHERE clause
  215.  * of an RDQL query. As a result of a database query using ADOdb these tuples
  216.  * are returned as an ADORecordSet object, which is then passed to this function.
  217.  *
  218.  * @param   object ADORecordSet &$recordSet 
  219.  * @return  array  [][?VARNAME]['value']   = string
  220.  *                              ['nType']   = string
  221.  *                              ['l_lang']  = string
  222.  *                              ['l_dtype'] = string
  223.  * @access    private
  224.  */
  225.  function filterQueryResult(&$recordSet{
  226.    $queryResult=array();
  227.  
  228.    if (isset($this->parsedQuery['filters'])) {
  229.  
  230.        while (!$recordSet->EOF{
  231.  
  232.          foreach ($this->parsedQuery['filters'as $filter{
  233.  
  234.            $evalFilterStr $filter['evalFilterStr'];
  235.  
  236.            // evaluate regex equality expressions of each filter
  237.            foreach ($filter['regexEqExprs'as $i => $expr{
  238.                preg_match($expr['regex']$recordSet->fields[$this->rsIndexes[$expr['var']]['value']]$match);
  239.                $op substr($expr['operator']0,1);
  240.                if (($op != '!' && !isset($match[0])) || ($op == '!' && isset($match[0])))
  241.                   $evalFilterStr str_replace("##RegEx_$i##"'FALSE'$evalFilterStr);
  242.                else
  243.                   $evalFilterStr str_replace("##RegEx_$i##"'TRUE'$evalFilterStr);
  244.            }
  245.  
  246.            // evaluate string equality expressions
  247.            foreach ($filter['strEqExprs'as $i => $expr{
  248.  
  249.              $exprBoolVal 'FALSE';
  250.  
  251.              switch ($expr['value_type']{
  252.  
  253.                case 'variable':
  254.                     if (($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] ==
  255.                            $recordSet->fields[$this->rsIndexes[$expr['value']]['value']] &&
  256.                          $expr['operator'== 'eq'||
  257.                         ($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] !=
  258.                            $recordSet->fields[$this->rsIndexes[$expr['value']]['value']] &&
  259.                          $expr['operator'== 'ne'))
  260.  
  261.                        $exprBoolVal 'TRUE';
  262.                     break;
  263.  
  264.                case 'URI':
  265.  
  266.                       if (isset($this->rsIndexes[$expr['var']]['nType']&&
  267.                            $recordSet->fields[$this->rsIndexes[$expr['var']]['nType']] == 'l'{
  268.  
  269.                          if ($expr['operator'== 'ne')
  270.                             $exprBoolVal 'TRUE';
  271.                          break;
  272.                       }
  273.  
  274.                     if (($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] ==
  275.                            $expr['value'&& $expr['operator'== 'eq'||
  276.                         ($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] !=
  277.                            $expr['value'&& $expr['operator'== 'ne'))
  278.                        $exprBoolVal 'TRUE';
  279.                     break;
  280.  
  281.                case 'Literal':
  282.  
  283.                     if (!isset($this->rsIndexes[$expr['var']]['nType']||
  284.                            $recordSet->fields[$this->rsIndexes[$expr['var']]['nType']] != 'l'{
  285.  
  286.                        if ($expr['operator'== 'ne')
  287.                           $exprBoolVal 'TRUE';
  288.                        break;
  289.                     }
  290.  
  291.                     $filterLiteralnew Literal($expr['value'],$expr['value_lang']);
  292.                     $filterLiteral->setDatatype($expr['value_dtype']);
  293.  
  294.                     $resultLiteral=new Literal($recordSet->fields[$this->rsIndexes[$expr['var']]['value']]);
  295.                     $resultLiteral->setDatatype($recordSet->fields[$this->rsIndexes[$expr['var']]['l_dtype']]);
  296.                     $resultLiteral->setLanguage($recordSet->fields[$this->rsIndexes[$expr['var']]['l_lang']]);
  297.  
  298.                     $equal=$resultLiteral->equals($filterLiteral);
  299.  
  300.                     if (($equal && $expr['operator'== 'eq'||
  301.                         (!$equal && $expr['operator'== 'ne'))
  302.                        $exprBoolVal 'TRUE';
  303.                     else
  304.                        $exprBoolVal 'FALSE';
  305.  
  306.              }
  307.  
  308.              $evalFilterStr str_replace("##strEqExpr_$i##"$exprBoolVal$evalFilterStr);
  309.           }
  310.  
  311.           // evaluate numerical expressions
  312.           foreach ($filter['numExprVars'as $varName{
  313.             $varValue "'" .$recordSet->fields[$this->rsIndexes[$varName]['value']] ."'";
  314.             $evalFilterStr str_replace($varName$varValue$evalFilterStr);
  315.           }
  316.  
  317.           eval("\$filterBoolVal = $evalFilterStr; \$eval_filter_ok = TRUE;");
  318.           if (!isset($eval_filter_ok))
  319.              trigger_error(RDQL_AND_ERR ."'" .htmlspecialchars($filter['string']."'"E_USER_ERROR);
  320.  
  321.           if (!$filterBoolVal{
  322.              $recordSet->MoveNext();
  323.              continue 2;
  324.           }
  325.  
  326.         }
  327.         $queryResult[$this->_convertRsRowToQueryResultRow($recordSet->fields);
  328.         $recordSet->MoveNext();
  329.       }
  330.  
  331.    }else
  332.       while (!$recordSet->EOF{
  333.         $queryResult[$this->_convertRsRowToQueryResultRow($recordSet->fields);
  334.         $recordSet->MoveNext();
  335.       }
  336.    return $queryResult;
  337.  }
  338.  
  339.  
  340. /**
  341.  * Serialize variable values of $queryResult to string.
  342.  *
  343.  * @param   array  &$queryResult [][?VARNAME]['value']   = string
  344.  *                                            ['nType']   = string
  345.  *                                            ['l_lang']  = string
  346.  *                                            ['l_dtype'] = string
  347.  * @return  array  [][?VARNAME] = string
  348.  * @access    private
  349.  */
  350.  function toString(&$queryResult{
  351.  
  352.    // if a result set is empty return only variable sames
  353.    if (count($queryResult== 0{
  354.       foreach ($this->parsedQuery['selectVars'as $selectVar)
  355.          $res[0][$selectVarNULL;
  356.       return $res;
  357.    }
  358.  
  359.    $res array();
  360.    foreach ($queryResult as $n => $var)
  361.      foreach ($var as $varname => $varProperties)
  362.        if ($varProperties['nType'== 'r' || $varProperties['nType'== 'b')
  363.           $res[$n][$varname'<' .$varProperties['value'.'>';
  364.        else {
  365.           $res[$n][$varname'"' .$varProperties['value'.'"';
  366.           if ($varProperties['l_lang'!= NULL)
  367.              $res[$n][$varname.= ' (xml:lang="' .$varProperties['l_lang'.'")';
  368.           if ($varProperties['l_dtype'!= NULL)
  369.              $res[$n][$varname.= ' (rdf:datatype="' .$varProperties['l_dtype'.'")';
  370.        }
  371.    return $res;
  372.  }
  373.  
  374.  
  375. /**
  376.  * Convert variable values of $queryResult to objects (Node).
  377.  *
  378.  * @param   array  &$queryResult [][?VARNAME]['value']   = string
  379.  *                                            ['nType']   = string
  380.  *                                            ['l_lang']  = string
  381.  *                                            ['l_dtype'] = string
  382.  * @return  array  [][?VARNAME] = object Node
  383.  * @access    private
  384.  */
  385.  function toNodes(&$queryResult{
  386.  
  387.    // if a result set is empty return only variable sames
  388.    if (count($queryResult== 0{
  389.       foreach ($this->parsedQuery['selectVars'as $selectVar)
  390.          $res[0][$selectVarNULL;
  391.       return $res;
  392.    }
  393.  
  394.    $res array();
  395.    foreach ($queryResult as $n => $var)
  396.      foreach ($var as $varname => $varProperties)
  397.        if ($varProperties['nType'== 'r')
  398.           $res[$n][$varnamenew Resource($varProperties['value']);
  399.        elseif ($varProperties['nType'== 'b')
  400.           $res[$n][$varnamenew BlankNode($varProperties['value']);
  401.        else {
  402.           $res[$n][$varnamenew Literal($varProperties['value']$varProperties['l_lang']);
  403.           if ($varProperties['l_dtype'!= NULL)
  404.              $res[$n][$varname]->setDataType($varProperties['l_dtype']);
  405.        }
  406.    return $res;
  407.  }
  408.  
  409.  
  410. /**
  411.  * Generate a piece of an sql select statement for a variable.
  412.  * Look first if the given variable is defined as a pattern object.
  413.  * (So you can select the node type, literal lang and dtype)
  414.  * If not found - look for subjects and select node label and type.
  415.  * If there is no result either go to predicates.
  416.  * Predicates are always resources therefore select only the node label.
  417.  *
  418.  * @param   string $varName 
  419.  * @return  string 
  420.  * @access    private
  421.  */
  422. function _generateSql_SelectVar ($varName&$index{
  423.  
  424.   $sql_select '';
  425.  
  426.   if (array_key_exists($varName$this->rsIndexes))
  427.      return NULL;
  428.  
  429.   foreach ($this->parsedQuery['patterns'as $n => $pattern)
  430.     if ($varName == $pattern['object']['value']{
  431.  
  432.        // select the object label
  433.        $sql_select .= " s" .++$n .".object as _" .ltrim($varName"?"." , ";
  434.        $this->rsIndexes[$varName]['value'$index++;
  435.        // select the node type
  436.        $sql_select .= " s" .$n .".object_is , ";
  437.        $this->rsIndexes[$varName]['nType'$index++;
  438.        // select the object language
  439.        $sql_select .= " s" .$n .".l_language , ";
  440.        $this->rsIndexes[$varName]['l_lang'$index++;
  441.        // select the object dtype
  442.        $sql_select .= " s" .$n .".l_datatype , ";
  443.        $this->rsIndexes[$varName]['l_dtype'$index++;
  444.  
  445.        return $sql_select;
  446.     }
  447.  
  448.   foreach ($this->parsedQuery['patterns'as $n => $pattern)
  449.     if ($varName == $pattern['subject']['value']{
  450.  
  451.        // select the object label
  452.        $sql_select .= " s" .++$n .".subject as _" .ltrim($varName"?"." , ";
  453.        $this->rsIndexes[$varName]['value'$index++;
  454.        // select the node type
  455.        $sql_select .= " s" .$n .".subject_is , ";
  456.        $this->rsIndexes[$varName]['nType'$index++;
  457.  
  458.        return $sql_select;
  459.     }
  460.  
  461.   foreach ($this->parsedQuery['patterns'as $n => $pattern)
  462.     if ($varName == $pattern['predicate']['value']{
  463.  
  464.        // select the object label
  465.        $sql_select .= " s" .++$n .".predicate as _" .ltrim($varName"?"." , ";
  466.        $this->rsIndexes[$varName]['value'$index++;
  467.  
  468.        return $sql_select;
  469.     }
  470.  }
  471.  
  472.  
  473. /**
  474.  * Converts a single row of ADORecordSet->fields array to the format of
  475.  * $queryResult array using pointers to indexes ($this->rsIndexes) in RecordSet->fields.
  476.  *
  477.  * @param   array  &$record [] = string
  478.  * @return  array  [?VARNAME]['value']   = string
  479.  *                            ['nType']   = string
  480.  *                            ['l_lang']  = string
  481.  *                            ['l_dtype'] = string
  482.  * @access    private
  483.  */
  484.  function _convertRsRowToQueryResultRow(&$record{
  485.  
  486.    // return only select variables (without conditional variables from the AND clause)
  487.    foreach ($this->parsedQuery['selectVars'as $selectVar{
  488.      $resultRow[$selectVar]['value'$record[$this->rsIndexes[$selectVar]['value']];
  489.      if (isset($this->rsIndexes[$selectVar]['nType']))
  490.         $resultRow[$selectVar]['nType'$record[$this->rsIndexes[$selectVar]['nType']];
  491.      // is a predicate then
  492.      else
  493.         $resultRow[$selectVar]['nType''r';
  494.  
  495.      if ($resultRow[$selectVar]['nType'== 'l'{
  496.         $resultRow[$selectVar]['l_lang'$record[$this->rsIndexes[$selectVar]['l_lang']];
  497.         $resultRow[$selectVar]['l_dtype'$record[$this->rsIndexes[$selectVar]['l_dtype']];
  498.      }
  499.    }
  500.    return $resultRow;
  501.  }
  502.  
  503. // end: Class RdqlDbEngine
  504.  
  505. ?>

Documentation generated on Fri, 1 Jun 2007 16:51:04 +0200 by phpDocumentor 1.3.2