Source for file TypeSorter.php

Documentation is available at TypeSorter.php

  1. <?php
  2. /**
  3. *   Sorts database types as the Sparql specs want it.
  4. *
  5. *   @author Christian Weiske <cweiske@cweiske.de>
  6. *
  7. *   @package sparql
  8. */
  9. {
  10.     /**
  11.     *   Defines the sort order for value types in the database,
  12.     *   so that they get sorted correctly as the specs want it.
  13.     *   @var array 
  14.     */
  15.     public static $arTypeNumbers array(
  16.         null    => 0,//empty
  17.         'b'     => 1,//blank
  18.         'r'     => 2,//resource
  19.         'l'     => 3,//literal
  20.     );
  21.  
  22.     /**
  23.     *   SQL types to cast XSD schema types to, so that they will get
  24.     *   sorted correctly.
  25.     *   @var array 
  26.     */
  27.     public static $arCastTypes array(
  28.         'http://www.w3.org/2001/XMLSchema#integer' => 'SIGNED INTEGER'
  29.     );
  30.  
  31.  
  32.  
  33.     public function __construct(Query $queryADOConnection $dbConn)
  34.     {
  35.         $this->dbConn   $dbConn;
  36.         $this->query    $query;
  37.     }
  38.  
  39.  
  40.     /**
  41.     *   Needs to be executed before getOrderifiedSqls and willBeDataDependent
  42.     *
  43.     *   @param SparqlEngineDb_SqlGenerator $sg  SQL generator object
  44.     */
  45.     public function setData(SparqlEngineDb_SqlGenerator $sg)
  46.     {
  47.         $this->arUsedVarTypes       $sg->getUsedVarTypes();
  48.         $this->arUsedVarAssignments $sg->getUsedVarAssignments();
  49.         $this->arVarAssignments     $sg->getVarAssignments();
  50.     }//public function setData(SparqlEngineDb_SqlGenerator $sg)
  51.  
  52.  
  53.  
  54.     /**
  55.     *   Returns an array of sql statements that need to be executed
  56.     *   and deliver the full result set when combined.
  57.     *
  58.     *   Execute setData() before this.
  59.     *
  60.     *   @internal It is not possible (ok, it is - but it would really complicate
  61.     *    the queries and the code here) to use UNION to combine the sqls to
  62.     *    a single one - UNION merges the results and does not keep the order
  63.     *    of the rows. Citing the mysql manual:
  64.     *        Use of ORDER BY for individual SELECT statements implies nothing
  65.     *        about the order in which the rows appear in the final result because
  66.     *        UNION by default produces an unordered set of rows. If ORDER BY
  67.     *        appears with LIMIT, it is used to determine the subset of the
  68.     *        selected rows to retrieve for the SELECT, but does not necessarily
  69.     *        affect the order of those rows in the final UNION result.
  70.     *        If ORDER BY appears without LIMIT in a SELECT, it is
  71.     *        optimized away because it will have no effect anyway.
  72.     *
  73.     *   @param string $strSelect    SELECT clause
  74.     *   @param string $strFrom      FROM clause
  75.     *   @param string $strWhere     WHERE clause
  76.     *   @return array   Array of arrays. Imploding an array will give
  77.     *                     a complete sql statement. The array will have the keys
  78.     *                     select/from/where/order.
  79.     */
  80.     public function getOrderifiedSqls($arSqls)
  81.     {
  82.         if (count($arSqls== 0{
  83.             return $arSqls;
  84.         }
  85.  
  86.         $strSelect $arSqls[0]['select'];
  87.         $strFrom   $arSqls[0]['from'];
  88.         $strWhere  $arSqls[0]['where'];
  89.  
  90.         $strResultForm $this->query->getResultForm();
  91.         if ($strResultForm == 'ask' || $strResultForm == 'count'{
  92.             return array(
  93.                 $arSqls
  94.             );
  95.         }
  96.  
  97.         $arSpecial $this->getSpecialOrderVariables();
  98.         if (count($arSpecial== 0{
  99.             $strOrder $this->getSqlOrderBy();
  100.             foreach ($arSqls as &$arSql{
  101.                 $arSql['order'$strOrder;
  102.             }
  103.             return array(
  104.                 $arSqls
  105.             );
  106.         }
  107.  
  108.         $arTypeSets $this->orderTypeSets(
  109.             $this->getTypeSets($arSpecial$strFrom$strWhere)
  110.         );
  111.  
  112.         $arNewSqls array();
  113.         foreach ($arTypeSets as $arTypeSet{
  114.             $arSqlParts array();
  115.             foreach ($arSqls as $arSql{
  116.                 $arSql['where'.= $this->getTypesetWhereClause($arTypeSet);
  117.                 $arSql['order']  $this->getSqlOrderBy($arTypeSet);
  118.                 $arSqlParts[$arSql;
  119.             }
  120.             $arNewSqls[$arSqlParts;
  121.         }
  122.  
  123.         return $arNewSqls;
  124.     }//public function getOrderifiedSqls($arUsedVarAssignments, $strSelect, $strFrom, $strWhere)
  125.  
  126.  
  127.  
  128.     /**
  129.     *   Returns wether the returned queries will depend on the data or not.
  130.     *   If the queries depend on the data, they cannot be prepare()d and
  131.     *   thus won't be that fast when executing.
  132.     *
  133.     *   Execute setData() before this.
  134.     *
  135.     *   @return boolean 
  136.     */
  137.     public function willBeDataDependent()
  138.     {
  139.         $strResultForm $this->query->getResultForm();
  140.         return !(
  141.             $strResultForm == 'ask' || $strResultForm == 'count'
  142.             || count($this->getSpecialOrderVariables()) == 0
  143.         );
  144.     }//public function willBeDataDependent()
  145.  
  146.  
  147.  
  148.     /**
  149.     *   Returns an array of variables that the result is going to be
  150.     *   ordered by and that need to be sorted multiple times
  151.     *   (because they may contain different data types)
  152.     *
  153.     *   @return array Array of sparql variable names
  154.     */
  155.     protected function getSpecialOrderVariables()
  156.     {
  157.         $arSM $this->query->getSolutionModifier();
  158.         if ($arSM['order by'=== null{
  159.             return array();
  160.         }
  161.  
  162.         $arSpecial array();
  163.         foreach ($arSM['order by'as $arVar{
  164.             if ($this->isSpecialOrderVariable($arVar['val'])) {
  165.                 $arSpecial[$arVar['val'];
  166.             }
  167.         }
  168.         return $arSpecial;
  169.     }//protected function getSpecialOrderVariables()
  170.  
  171.  
  172.  
  173.     /**
  174.     *   Checks if a given variable name is a variable that
  175.     *   needs special care when used in ORDER BY statements.
  176.     *
  177.     *   @param string $strVar SPARQL variable name
  178.     *   @return boolean true if the variable needs special care
  179.     */
  180.     protected function isSpecialOrderVariable($strVar)
  181.     {
  182.         return !isset($this->arUsedVarTypes[$strVar]['s'])
  183.             && !isset($this->arUsedVarTypes[$strVar]['p']);
  184.     }//protected function isSpecialOrderVariable($arVar)
  185.  
  186.  
  187.  
  188.     /**
  189.     *   Determines the type sets in the query results.
  190.     *   A type set is a distinct set of variables and their types,
  191.     *   e.g. the variable and its type (r/b/l) and its datatype
  192.     *   if its an object.
  193.     *
  194.     *   @param array $arSpecialVars     Special variables as returned by
  195.     *                                    getSpecialOrderVariables()
  196.     *   @param string $strFrom          FROM part of the sql query
  197.     *   @param string $strWhere         WHERE part of the sql query
  198.     *
  199.     *   @return array   Key is the sparql variable name, value is an
  200.     *                    array. This one has one key 'type' with a
  201.     *                    value of b/l/r. It might have another key
  202.     *                    'datatype' with the resource's datatype.
  203.     */
  204.     protected function getTypeSets($arSpecialVars$strFrom$strWhere)
  205.     {
  206.         $arSel array();
  207.         foreach ($arSpecialVars as $strSparqlVar{
  208.             if ($this->arVarAssignments[$strSparqlVar][1== 'o'{
  209.                 $arSel[$this->arVarAssignments[$strSparqlVar][0'.l_datatype'
  210.                     . ' as "' $strSparqlVar '-datatype"';
  211.             }
  212.             $strTypeCol $this->arVarAssignments[$strSparqlVar][1== 'o'
  213.                 ? 'object_is' 'subject_is';
  214.             $this->arVarAssignments[$strSparqlVar][2$strTypeCol;
  215.             $arSel[$this->arVarAssignments[$strSparqlVar][0'.' $strTypeCol
  216.                 . ' as "' $strSparqlVar '-type"';
  217.         }
  218.  
  219.         $oldmode $this->dbConn->SetFetchMode(ADODB_FETCH_ASSOC);
  220.         $arResult $this->dbConn->execute(
  221.             'SELECT DISTINCT ' implode(', '$arSel' ' $strFrom $strWhere
  222.         );
  223.         $this->dbConn->SetFetchMode($oldmode);
  224.  
  225.         $arTypes array();
  226.         foreach ($arResult as $arRow{
  227.             $nLine count($arTypes);
  228.             foreach ($arRow as $key => $value{
  229.                 list($strSparqlVar$strTypeexplode('-'$key);
  230.                 $arTypes[$nLine][$strSparqlVar][$strType$value;
  231.             }
  232.         }
  233.  
  234.         return $arTypes;
  235.     }//protected function getTypeSets($arSpecialVars, $strFrom, $strWhere)
  236.  
  237.  
  238.  
  239.     /**
  240.     *   Takes an array of type sets (as returned by getTypeSets()) and
  241.     *   sorts the variables according to the SPARQL specs.
  242.     *
  243.     *   @param array $arTypes Array of type sets
  244.     *   @return array Ordered array of type sets
  245.     */
  246.     protected function orderTypeSets($arTypes)
  247.     {
  248.         $arSM $this->query->getSolutionModifier();
  249.         if ($arSM['order by'!== null{
  250.             $this->arDirection array();
  251.             foreach ($arSM['order by'as $arVar{
  252.                 $this->arDirection[$arVar['val']] =
  253.                     (strtoupper($arVar['type']== 'ASC' : -1);
  254.             }
  255.         }
  256.  
  257.         usort($arTypesarray($this'compareTypeSet'));
  258.  
  259.         return $arTypes;
  260.     }//protected function orderTypeSets($arTypes)
  261.  
  262.  
  263.  
  264.     /**
  265.     *   Compares two type sets. Works like a normal comparision
  266.     *   method that returns -1/0/1 for use in usort()
  267.     *
  268.     *   @param array $arTs1     First typeset
  269.     *   @param array $arTs2     Second typeset
  270.     *   @return int Comparison value
  271.     */
  272.     public function compareTypeSet($arTs1$arTs2)
  273.     {
  274.         foreach ($arTs1 as $strSparqlVar => $arType1{
  275.             //compare types
  276.             $n self::$arTypeNumbers[$arType1['type']]
  277.                 - self::$arTypeNumbers[$arTs2[$strSparqlVar]['type']];
  278.             if ($n != 0{
  279.                 //and take ASC/DESC into account
  280.                 return $n $this->arDirection[$strSparqlVar];
  281.             }
  282.  
  283.             //compare the datatypes
  284.             $n self::compareXsdType(
  285.                 $arType1['datatype'],
  286.                 $arTs2[$strSparqlVar]['datatype']
  287.             );
  288.             //if they are not equal, return the value
  289.             if ($n != 0{
  290.                 //and take ASC/DESC into account
  291.                 return $n $this->arDirection[$strSparqlVar];
  292.             }
  293.         }
  294.  
  295.         //they are equal
  296.         return 0;
  297.     }//public function compareTypeSet($arTs1, $arTs2)
  298.  
  299.  
  300.  
  301.     /**
  302.     *   Compares two XSD data types and returns the comparison number.
  303.     *
  304.     *   @param string $strType1     First data type
  305.     *   @param string $strType2     Second data type
  306.     *   @return int     Comparison number (<0 if $strType1 is less than
  307.     *                    $strType2, 0 if equal, >0 if type2 is greater)
  308.     */
  309.     public static function compareXsdType($strType1$strType2)
  310.     {
  311.         return self::getXsdTypeNumber($strType1self::getXsdTypeNumber($strType2);
  312.     }//public static function compareXsdType($strType1, $strType2)
  313.  
  314.  
  315.  
  316.     /**
  317.     *   Returns the type number for an xsd data type.
  318.     *
  319.     *   @param string $strType  XSD data type
  320.     *   @return int  Some integer to compare two types.
  321.     */
  322.     public static function getXsdTypeNumber($strType)
  323.     {
  324.         if ($strType === null{
  325.             return 0;
  326.         else if ($strType == ''{
  327.             return 1;
  328.         else {
  329.             return 2;
  330.         }
  331.     }//public static function getXsdTypeNumber($strType)
  332.  
  333.  
  334.  
  335.     /**
  336.     *   Returns the ORDER BY sql query string
  337.     *   if neccessary for the query. The returned string
  338.     *   already has ORDER BY prefixed.
  339.     *
  340.     *   @internal Also takes care of casting the data if the type
  341.     *    is listed in $arCastTypes. See getCastMethod() for more info.
  342.     *
  343.     *   @param arrray $arTypeSet Single typeset
  344.     *   @return string      ORDER BY ... string or empty string
  345.     */
  346.     function getSqlOrderBy($arTypeSet array())
  347.     {
  348.         $arSM $this->query->getSolutionModifier();
  349.         if ($arSM['order by'=== null{
  350.             return '';
  351.         }
  352.  
  353.         $sqlOrder array();
  354.         foreach ($arSM['order by'as $arVar{
  355.             $strSparqlVar $arVar['val'];
  356.             if (isset($this->arUsedVarAssignments[$strSparqlVar])) {
  357.                 if (!isset($arTypeSet[$strSparqlVar]['datatype'])
  358.                   || $arTypeSet[$strSparqlVar]['datatype'== ''{
  359.                     $sqlOrder[$this->arUsedVarAssignments[$strSparqlVar' ' strtoupper($arVar['type']);
  360.                 else {
  361.                     $sqlOrder[self::getCastMethod(
  362.                         $arTypeSet[$strSparqlVar]['datatype'],
  363.                         $this->arUsedVarAssignments[$strSparqlVar]
  364.                     ' ' strtoupper($arVar['type']);
  365.                 }
  366.             }
  367.         }
  368.  
  369.         return ' ORDER BY ' implode(', '$sqlOrder);
  370.     }//function getSqlOrderBy($arTypeSet = array())
  371.  
  372.  
  373.  
  374.     /**
  375.     *   Returns the SQL statement needed to case the given variable to
  376.     *   the given type.
  377.     *
  378.     *   @param string $strType      XML data type
  379.     *   @param string $strSqlVar    SQL variable name
  380.     *   @return string  SQL command to cast the variable
  381.     */
  382.     protected static function getCastMethod($strType$strSqlVar)
  383.     {
  384.         if (isset(self::$arCastTypes[$strType])) {
  385.             return 'CAST(' $strSqlVar ' as ' self::$arCastTypes[$strType')';
  386.         else {
  387.             //unsupported type!
  388.             throw new Exception('Unsupported cast type in order by: ' $strType);
  389.             return $strSqlVar;
  390.         }
  391.     }//protected static function getCastMethod($strType, $strSqlVar)
  392.  
  393.  
  394.  
  395.     /**
  396.     *   Creates and returns the SQL WHERE clauses needed to get only
  397.     *   data in the given typeset.
  398.     *
  399.     *   @param array $arTypeSet     Typeset
  400.     *   @return string  Clauses for the WHERE part in an SQL query
  401.     */
  402.     protected function getTypesetWhereClause($arTypeSet)
  403.     {
  404.         $strWhereTypes '';
  405.         foreach ($arTypeSet as $strSparqlVar => $arType{
  406.             //check type
  407.             $strWhereTypes .= ' AND ' $this->arVarAssignments[$strSparqlVar][0]
  408.                 . '.' $this->arVarAssignments[$strSparqlVar][2]
  409.                 . $this->getStringNullComparison($arType['type']);
  410.             //check datatype
  411.             if (isset($arType['datatype'])) {
  412.                 $strWhereTypes .= ' AND ' $this->arVarAssignments[$strSparqlVar][0]
  413.                     . '.l_datatype '
  414.                     . $this->getStringNullComparison($arType['datatype']);
  415.             }
  416.         }
  417.         return $strWhereTypes;
  418.     }//protected function getTypesetWhereClause($arTypeSet)
  419.  
  420.  
  421.  
  422.     /**
  423.     *   Returns the correct sql string comparison method.
  424.     *
  425.     *   @internal If the string is NULL, an "IS NULL" sql statment
  426.     *    will be returned, and a normal "= 'something'" if it is
  427.     *    a normal string.
  428.     */
  429.     protected function getStringNullComparison($str)
  430.     {
  431.         if ($str === null{
  432.             return ' IS NULL';
  433.         else {
  434.             return ' = ' $this->dbConn->qstr($str'';
  435.         }
  436.     }//protected function getStringNullComparison($str)
  437.  
  438. }//class SparqlEngineDb_TypeSorter
  439. ?>

Documentation generated on Fri, 1 Jun 2007 16:52:35 +0200 by phpDocumentor 1.3.2