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,v 1.10 2006/06/26 12:34:13 tgauss Exp $
  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.  
  227.    $queryResult=array();
  228.  
  229.    if (isset($this->parsedQuery['filters'])) {
  230.  
  231.        while (!$recordSet->EOF{
  232.  
  233.          foreach ($this->parsedQuery['filters'as $filter{
  234.  
  235.            $evalFilterStr $filter['evalFilterStr'];
  236.  
  237.            // evaluate regex equality expressions of each filter
  238.            foreach ($filter['regexEqExprs'as $i => $expr{
  239.                preg_match($expr['regex']$recordSet->fields[$this->rsIndexes[$expr['var']]['value']]$match);
  240.                $op substr($expr['operator']0,1);
  241.                if (($op != '!' && !isset($match[0])) || ($op == '!' && isset($match[0])))
  242.                   $evalFilterStr str_replace("##RegEx_$i##"'FALSE'$evalFilterStr);
  243.                else
  244.                   $evalFilterStr str_replace("##RegEx_$i##"'TRUE'$evalFilterStr);
  245.            }
  246.  
  247.            // evaluate string equality expressions
  248.            foreach ($filter['strEqExprs'as $i => $expr{
  249.  
  250.              $exprBoolVal 'FALSE';
  251.  
  252.              switch ($expr['value_type']{
  253.  
  254.                case 'variable':
  255.                     if (($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] ==
  256.                            $recordSet->fields[$this->rsIndexes[$expr['value']]['value']] &&
  257.                          $expr['operator'== 'eq'||
  258.                         ($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] !=
  259.                            $recordSet->fields[$this->rsIndexes[$expr['value']]['value']] &&
  260.                          $expr['operator'== 'ne'))
  261.                          
  262.                        $exprBoolVal 'TRUE';
  263.                     break;
  264.  
  265.                case 'URI':
  266.  
  267.                       if (isset($this->rsIndexes[$expr['var']]['nType']&&
  268.                            $recordSet->fields[$this->rsIndexes[$expr['var']]['nType']] == 'l'{
  269.  
  270.                          if ($expr['operator'== 'ne')
  271.                             $exprBoolVal 'TRUE';
  272.                          break;
  273.                       }
  274.  
  275.                     if (($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] ==
  276.                            $expr['value'&& $expr['operator'== 'eq'||
  277.                         ($recordSet->fields[$this->rsIndexes[$expr['var']]['value']] !=
  278.                            $expr['value'&& $expr['operator'== 'ne'))
  279.                        $exprBoolVal 'TRUE';
  280.                     break;
  281.  
  282.                case 'Literal':
  283.  
  284.                     if (!isset($this->rsIndexes[$expr['var']]['nType']||
  285.                            $recordSet->fields[$this->rsIndexes[$expr['var']]['nType']] != 'l'{
  286.  
  287.                        if ($expr['operator'== 'ne')
  288.                           $exprBoolVal 'TRUE';
  289.                        break;
  290.                     }
  291.  
  292.                     $filterLiteralnew Literal($expr['value'],$expr['value_lang']);
  293.                     $filterLiteral->setDatatype($expr['value_dtype']);
  294.                     
  295.                     $resultLiteral=new Literal($recordSet->fields[$this->rsIndexes[$expr['var']]['value']]);
  296.                     $resultLiteral->setDatatype($recordSet->fields[$this->rsIndexes[$expr['var']]['l_dtype']]);
  297.                     $resultLiteral->setLanguage($recordSet->fields[$this->rsIndexes[$expr['var']]['l_lang']]);
  298.                      
  299.                     $equal=$resultLiteral->equals($filterLiteral);
  300.                     
  301.                     if (($equal && $expr['operator'== 'eq'||
  302.                         (!$equal && $expr['operator'== 'ne'))
  303.                        $exprBoolVal 'TRUE';
  304.                     else
  305.                        $exprBoolVal 'FALSE';
  306.                         
  307.              }
  308.  
  309.              $evalFilterStr str_replace("##strEqExpr_$i##"$exprBoolVal$evalFilterStr);
  310.           }
  311.  
  312.           // evaluate numerical expressions
  313.           foreach ($filter['numExprVars'as $varName{
  314.             $varValue "'" .$recordSet->fields[$this->rsIndexes[$varName]['value']] ."'";
  315.             $evalFilterStr str_replace($varName$varValue$evalFilterStr);
  316.           }
  317.  
  318.           eval("\$filterBoolVal = $evalFilterStr; \$eval_filter_ok = TRUE;");
  319.           if (!isset($eval_filter_ok))
  320.              trigger_error(RDQL_AND_ERR ."'" .htmlspecialchars($filter['string']."'"E_USER_ERROR);
  321.  
  322.           if (!$filterBoolVal{
  323.              $recordSet->MoveNext();
  324.              continue 2;
  325.           }
  326.  
  327.         }
  328.         $queryResult[$this->_convertRsRowToQueryResultRow($recordSet->fields);
  329.         $recordSet->MoveNext();
  330.       }
  331.         
  332.    }else
  333.       while (!$recordSet->EOF{
  334.         $queryResult[$this->_convertRsRowToQueryResultRow($recordSet->fields);
  335.         $recordSet->MoveNext();
  336.       }
  337.    return $queryResult;
  338.  }
  339.  
  340.  
  341. /**
  342.  * Serialize variable values of $queryResult to string.
  343.  *
  344.  * @param   array  &$queryResult [][?VARNAME]['value']   = string
  345.  *                                            ['nType']   = string
  346.  *                                            ['l_lang']  = string
  347.  *                                            ['l_dtype'] = string
  348.  * @return  array  [][?VARNAME] = string
  349.  * @access    private
  350.  */
  351.  function toString(&$queryResult{
  352.  
  353.    // if a result set is empty return only variable sames
  354.    if (count($queryResult== 0{
  355.       foreach ($this->parsedQuery['selectVars'as $selectVar)
  356.          $res[0][$selectVarNULL;
  357.       return $res;
  358.    }
  359.  
  360.    $res array();
  361.    foreach ($queryResult as $n => $var)
  362.      foreach ($var as $varname => $varProperties)
  363.        if ($varProperties['nType'== 'r' || $varProperties['nType'== 'b')
  364.           $res[$n][$varname'<' .$varProperties['value'.'>';
  365.        else {
  366.           $res[$n][$varname'"' .$varProperties['value'.'"';
  367.           if ($varProperties['l_lang'!= NULL)
  368.              $res[$n][$varname.= ' (xml:lang="' .$varProperties['l_lang'.'")';
  369.           if ($varProperties['l_dtype'!= NULL)
  370.              $res[$n][$varname.= ' (rdf:datatype="' .$varProperties['l_dtype'.'")';
  371.        }
  372.    return $res;
  373.  }
  374.  
  375.  
  376. /**
  377.  * Convert variable values of $queryResult to objects (Node).
  378.  *
  379.  * @param   array  &$queryResult [][?VARNAME]['value']   = string
  380.  *                                            ['nType']   = string
  381.  *                                            ['l_lang']  = string
  382.  *                                            ['l_dtype'] = string
  383.  * @return  array  [][?VARNAME] = object Node
  384.  * @access    private
  385.  */
  386.  function toNodes(&$queryResult{
  387.  
  388.    // if a result set is empty return only variable sames
  389.    if (count($queryResult== 0{
  390.       foreach ($this->parsedQuery['selectVars'as $selectVar)
  391.          $res[0][$selectVarNULL;
  392.       return $res;
  393.    }
  394.  
  395.    $res array();
  396.    foreach ($queryResult as $n => $var)
  397.      foreach ($var as $varname => $varProperties)
  398.        if ($varProperties['nType'== 'r')
  399.           $res[$n][$varnamenew Resource($varProperties['value']);
  400.        elseif ($varProperties['nType'== 'b')
  401.           $res[$n][$varnamenew BlankNode($varProperties['value']);
  402.        else {
  403.           $res[$n][$varnamenew Literal($varProperties['value']$varProperties['l_lang']);
  404.           if ($varProperties['l_dtype'!= NULL)
  405.              $res[$n][$varname]->setDataType($varProperties['l_dtype']);
  406.        }
  407.    return $res;
  408.  }
  409.  
  410.  
  411. /**
  412.  * Generate a piece of an sql select statement for a variable.
  413.  * Look first if the given variable is defined as a pattern object.
  414.  * (So you can select the node type, literal lang and dtype)
  415.  * If not found - look for subjects and select node label and type.
  416.  * If there is no result either go to predicates.
  417.  * Predicates are always resources therefore select only the node label.
  418.  *
  419.  * @param   string $varName 
  420.  * @return  string 
  421.  * @access    private
  422.  */
  423. function _generateSql_SelectVar ($varName&$index{
  424.  
  425.   $sql_select '';
  426.  
  427.   if (array_key_exists($varName$this->rsIndexes))
  428.      return NULL;
  429.  
  430.   foreach ($this->parsedQuery['patterns'as $n => $pattern)
  431.     if ($varName == $pattern['object']['value']{
  432.  
  433.        // select the object label
  434.        $sql_select .= " s" .++$n .".object as _" .ltrim($varName"?"." , ";
  435.        $this->rsIndexes[$varName]['value'$index++;
  436.        // select the node type
  437.        $sql_select .= " s" .$n .".object_is , ";
  438.        $this->rsIndexes[$varName]['nType'$index++;
  439.        // select the object language
  440.        $sql_select .= " s" .$n .".l_language , ";
  441.        $this->rsIndexes[$varName]['l_lang'$index++;
  442.        // select the object dtype
  443.        $sql_select .= " s" .$n .".l_datatype , ";
  444.        $this->rsIndexes[$varName]['l_dtype'$index++;
  445.  
  446.        return $sql_select;
  447.     }
  448.  
  449.   foreach ($this->parsedQuery['patterns'as $n => $pattern)
  450.     if ($varName == $pattern['subject']['value']{
  451.  
  452.        // select the object label
  453.        $sql_select .= " s" .++$n .".subject as _" .ltrim($varName"?"." , ";
  454.        $this->rsIndexes[$varName]['value'$index++;
  455.        // select the node type
  456.        $sql_select .= " s" .$n .".subject_is , ";
  457.        $this->rsIndexes[$varName]['nType'$index++;
  458.  
  459.        return $sql_select;
  460.     }
  461.  
  462.   foreach ($this->parsedQuery['patterns'as $n => $pattern)
  463.     if ($varName == $pattern['predicate']['value']{
  464.  
  465.        // select the object label
  466.        $sql_select .= " s" .++$n .".predicate as _" .ltrim($varName"?"." , ";
  467.        $this->rsIndexes[$varName]['value'$index++;
  468.  
  469.        return $sql_select;
  470.     }
  471.  }
  472.  
  473.  
  474. /**
  475.  * Converts a single row of ADORecordSet->fields array to the format of
  476.  * $queryResult array using pointers to indexes ($this->rsIndexes) in RecordSet->fields.
  477.  *
  478.  * @param   array  &$record [] = string
  479.  * @return  array  [?VARNAME]['value']   = string
  480.  *                            ['nType']   = string
  481.  *                            ['l_lang']  = string
  482.  *                            ['l_dtype'] = string
  483.  * @access    private
  484.  */
  485.  function _convertRsRowToQueryResultRow(&$record{
  486.  
  487.    // return only select variables (without conditional variables from the AND clause)
  488.    foreach ($this->parsedQuery['selectVars'as $selectVar{
  489.      $resultRow[$selectVar]['value'$record[$this->rsIndexes[$selectVar]['value']];
  490.      if (isset($this->rsIndexes[$selectVar]['nType']))
  491.         $resultRow[$selectVar]['nType'$record[$this->rsIndexes[$selectVar]['nType']];
  492.      // is a predicate then
  493.      else
  494.         $resultRow[$selectVar]['nType''r';
  495.         
  496.      if ($resultRow[$selectVar]['nType'== 'l'{
  497.         $resultRow[$selectVar]['l_lang'$record[$this->rsIndexes[$selectVar]['l_lang']];
  498.         $resultRow[$selectVar]['l_dtype'$record[$this->rsIndexes[$selectVar]['l_dtype']];
  499.      }
  500.    }
  501.    return $resultRow;
  502.  }
  503.  
  504. // end: Class RdqlDbEngine
  505.  
  506. ?>

Documentation generated on Mon, 26 Jun 2006 14:25:59 +0200 by phpDocumentor 1.3.0RC6