source: Dev/trunk/rdfapi/sparql/SparqlEngineDb/SqlGenerator.php @ 12

Last change on this file since 12 was 12, checked in by basvannuland, 14 years ago

Added RAP RDF API
Added RDF reader writer for save and load survey

File size: 38.1 KB
Line 
1<?php
2require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlVariable.php';
3require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/TypeSorter.php';
4require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/FilterGenerator.php';
5require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/SqlGeneratorException.php';
6
7/**
8*   Creates sql statements from a Query object
9*
10*   @author Christian Weiske <cweiske@cweiske.de>
11*   @license http://www.gnu.org/licenses/lgpl.html LGPL
12*
13*   @package sparql
14*/
15class SparqlEngineDb_SqlGenerator
16{
17    public $query = null;
18
19    /**
20    *   Determines which variables can be found
21    *   in which SQL result column.
22    *   @example
23    *   array(
24    *       '?person' => array('t1', 's'),
25    '       '?p'      => array('t1', 'p'),
26    *       '?mbox'   => array('t2', 'o')
27    *   )
28    *   Would express that variable ?person is the subject
29    *   in result table t1, and ?mbox is the object in
30    *   table t2.
31    *
32    *   @see $arCreationMethods
33    *
34    *   @internal The array is created in createSql()
35    *   and used in convertFromDbResult().
36    *
37    *   @var array
38    */
39    public $arVarAssignments = array();
40
41    /**
42    *   Array of variable name => table.col assignments
43    *   for all variables used in the query not only
44    *   the ones that shall be returned.
45    *
46    *   @example
47    *   array(
48    *       '?person'   => 't0.subject'
49    *   )
50    *
51    *   @var array
52    */
53    public $arUsedVarAssignments = array();
54
55    /**
56    *   Array of arrays that contain all variable names
57    *   which are to be found in the result of
58    *   an sql statement in a union.
59    *
60    *   @example
61    *   array(
62    *       0 => array(
63    *           '?person' => 's',
64    *           '?p' => 'o'
65    *       ),
66    *       1 => array(
67    *           '?person' => 's',
68    *           '?mbox' => 'o'
69    *       )
70    *   )
71    *
72    *   @var array
73    */
74    public $arUnionVarAssignments = array();
75
76    /**
77    *   Which variables have been used as which type?
78    *   key is variable name, value is an array of
79    *   max. three keys (s, p, o)
80    *
81    *   @example
82    *   array(
83    *       '?person' => array(
84    *           's' => true
85    *       ),
86    *       '?mbox' => array(
87    *           'o' => true
88    *       )
89    *   )
90    *
91    *   @var array
92    */
93    protected $arUsedVarTypes = array();
94
95    /**
96    *   Array with placeholders of prepared statements variables.
97    *   key is the variable name (without "??"), value is the
98    *   placeholder.
99    *   @var array
100    */
101    protected $arPlaceholders = array();
102
103    /**
104    *   Column names for subjects, predicates and
105    *   objects for easy access via their character
106    *   names (spo).
107    *
108    *   @var array
109    */
110    public static $arTableColumnNames = array(
111        's' => array(
112            'value' => 'subject',
113            'is'    => 'subject_is'
114        ),
115        'p' => array(
116            'value' => 'predicate'
117        ),
118        'o' => array(
119            'value' => 'object',
120            'is'    => 'object_is'
121        )
122    );
123
124    /**
125    *   Current UNION part number
126    *   @var int
127    */
128    protected $nUnionCount = 0;
129
130    protected $nSqlVariableNameCount = 0;
131
132    /**
133    *   Name of the statements table
134    */
135    protected $tblStatements = 'statements';
136
137
138
139    public function __construct(Query $query, ADOConnection $dbConn, $arModelIds)
140    {
141        $this->query      = $query;
142        $this->dbConn     = $dbConn;
143        $this->arModelIds = $arModelIds;
144    }//public function __construct(Query $query, ADOConnection $dbConn, $arModelIds)
145
146
147
148    /**
149    *   Creates an SQL query string from the given Sparql query object.
150    *
151    *   @internal uses $query variable
152    *
153    *   @return array       Array of arrays of SQL query string parts: select, from and where
154    *
155    *   @throws SparqlEngineDb_SqlGeneratorException   If there is no variable in the result set.
156    */
157    function createSql()
158    {
159//var_dump($this->query);
160        $arSelect   = array();
161        $arFrom     = array();
162        $arWhere    = array();
163
164        $strResultForm = $this->query->getResultForm();
165        $filterGen     = new SparqlEngineDb_FilterGenerator($this);
166        switch ($strResultForm) {
167            case 'construct':
168                $arResultVars = $this->query->getConstructPatternVariables();
169                break;
170            default:
171                $arResultVars = $this->query->getResultVars();
172                break;
173        }
174
175        $this->nTableId                 = 0;
176        $this->nGraphPatternCount       = 0;
177        $this->nUnionCount              = 0;
178        $this->nUnionTriplePatternCount = 0;
179        $this->arUnionVarAssignments[0] = array();
180
181        foreach ($this->query->getResultPart() as $graphPattern) {
182            if ($graphPattern->isEmpty()) {
183                continue;
184            }
185            if ($graphPattern->getUnion() !== null) {
186                ++$this->nUnionCount;
187                $this->nTableId                 = 0;
188                $this->nUnionTriplePatternCount = 0;
189                $this->nGraphPatternCount = 0;
190                $this->arUnionVarAssignments[$this->nUnionCount] = array();
191            }
192            $this->nTriplePatternCount = 0;
193            $arTriplePattern = $graphPattern->getTriplePatterns();
194            if ($arTriplePattern != null) {
195                foreach ($arTriplePattern as $triplePattern) {
196                    list (
197                        $arSelect[$this->nUnionCount][],
198                        $arFrom  [$this->nUnionCount][],
199                        $arWhere [$this->nUnionCount][]
200                    ) =
201                        $this->getTripleSql(
202                            $triplePattern,
203                            $graphPattern,
204                            $arResultVars
205                        );
206                    ++$this->nTableId;
207                    ++$this->nTriplePatternCount;
208                    ++$this->nUnionTriplePatternCount;
209                }
210            }
211            ++$this->nGraphPatternCount;
212
213        }
214
215        //constraints extra. needed, since OPTIONAL parts are put after
216        // the current pattern while the constraint already refers to variables
217        // defined in there
218        $this->nGraphPatternCount       = 0;
219        $this->nUnionCount              = 0;
220        foreach ($this->query->getResultPart() as $graphPattern) {
221            if ($graphPattern->getUnion() !== null) {
222                ++$this->nUnionCount;
223            }
224            $arConstraints = $graphPattern->getConstraints();
225            if ($arConstraints != null) {
226                foreach ($arConstraints as $constraint) {
227                    $arWhere[$this->nUnionCount][count($arWhere[$this->nUnionCount]) - 1]
228                     .= $filterGen->createFilterSql(
229                        $constraint->getTree(),
230                        $graphPattern->getOptional() !== null,
231                        $this->nUnionCount
232                    );
233                }
234            }
235            ++$this->nGraphPatternCount;
236        }
237
238        $arSelect    = $this->createEqualSelects($arSelect);
239        $arStrSelect = array();
240
241        switch ($strResultForm) {
242            case 'construct':
243            case 'describe':
244                $strSelectType = 'SELECT';
245            case 'select':
246            case 'select distinct':
247                if (!isset($strSelectType)) {
248                    $strSelectType = $strResultForm;
249                }
250                foreach ($arSelect as $nUnionCount => $arSelectPart) {
251                    $arSelectPart = self::removeNull($arSelectPart);
252                    if (count($arSelectPart) == 0
253                    || (count($arSelectPart) == 1 && $arSelectPart[0] == '')) {
254                        //test "test-1-07" suggests we return no rows in this case
255                        //throw new SparqlEngineDb_SqlGeneratorException('No variable that could be returned.');
256                    } else {
257                        $arStrSelect[$nUnionCount] = strtoupper($strSelectType) . ' ' . implode(', '   , $arSelectPart);
258                    }
259                }
260                break;
261
262            case 'ask':
263            case 'count':
264                $arStrSelect = array('SELECT COUNT(*) as count');
265                break;
266
267            default:
268                throw new SparqlEngineDb_SqlGeneratorException('Unsupported query type "' . $strResultForm . '"');
269                break;
270        }
271
272        $arSqls = array();
273        foreach ($arStrSelect as $nUnionCount => $arSelectPart) {
274            $arSqls[] = array(
275                'select'    => $arStrSelect[$nUnionCount],
276                'from'      => ' FROM '  . implode(' '    , self::removeNull($arFrom[$nUnionCount])),
277                'where'     => ' WHERE ' . self::fixWhere(
278                            implode(' '  , self::removeNull($arWhere[$nUnionCount]))
279                )
280            );
281        }
282        return $arSqls;
283    }//function createSql()
284
285
286
287    /**
288    *   Creates some SQL statements from the given triple pattern
289    *   array.
290    *
291    *   @param QueryTriple  $triple                 Array containing subject, predicate and object
292    *   @param GraphPattern $graphPattern           Graph pattern object
293    *
294    *   @return array   Array consisting of on array and two string values:
295    *                   SELECT, FROM and WHERE part
296    */
297    function getTripleSql(QueryTriple $triple, GraphPattern $graphPattern, $arResultVars)
298    {
299        $arSelect  = array();
300        $strFrom    = null;
301        $strWhere   = null;
302        $strWhereEquality        = '';
303        $bWhereEqualitySubject   = false;
304        $bWhereEqualityPredicate = false;
305        $bWhereEqualityObject    = false;
306
307        $subject    = $triple->getSubject();
308        $predicate  = $triple->getPredicate();
309        $object     = $triple->getObject();
310
311        $arRefVars      = array();
312        $strTablePrefix = 't' . $this->nTableId;
313
314        /**
315        *   SELECT part
316        *   We do select only the columns we need for variables
317        */
318        if (SparqlVariable::isVariable($subject)) {
319            if (isset($this->arUnionVarAssignments[$this->nUnionCount][$subject])) {
320                //already selected -> add equality check
321                $bWhereEqualitySubject = true;
322                $this->arUsedVarTypes[$subject]['s'] = true;
323            } else {
324                if (isset($this->arVarAssignments[$subject][0])) {
325                    $strTablePrefix = $this->arVarAssignments[$subject][0];
326                }
327                $this->arVarAssignments[$subject] = array($strTablePrefix, 's');
328                $this->arUnionVarAssignments[$this->nUnionCount][$subject] = array($strTablePrefix, 's');
329                $this->arUsedVarTypes[$subject]['s'] = true;
330                if (self::isResultVar($subject, $arResultVars)) {
331                    //new variable that needs to be selected
332                    $arSelect[$subject] = $this->createVariableSelectArray(
333                        's', $subject, $strTablePrefix
334                    );
335                    if (isset($this->arUsedVarAssignments[$subject])) {
336                        $arRefVars[$subject] = $strTablePrefix . '.subject';
337                    } else {
338                        $this->arUsedVarAssignments[$subject] = $strTablePrefix . '.subject';
339                    }
340                }
341            }
342        }
343
344        if (SparqlVariable::isVariable($predicate)) {
345            if (isset($this->arUnionVarAssignments[$this->nUnionCount][$predicate])) {
346                //already selected -> add equality check
347                $bWhereEqualityPredicate = true;
348                $this->arUsedVarTypes[$predicate]['p'] = true;
349            } else {
350                if (isset($this->arVarAssignments[$predicate][0])) {
351                    $strTablePrefix = $this->arVarAssignments[$predicate][0];
352                }
353                $this->arVarAssignments[$predicate] = array($strTablePrefix, 'p');
354                $this->arUnionVarAssignments[$this->nUnionCount][$predicate] = array($strTablePrefix, 'p');
355                $this->arUsedVarTypes[$predicate]['p'] = true;
356                if (self::isResultVar($predicate, $arResultVars)) {
357                    $arSelect[$predicate] = $this->createVariableSelectArray(
358                        'p', $predicate, $strTablePrefix
359                    );
360                    if (isset($this->arUsedVarAssignments[$predicate])) {
361                        $arRefVars[$predicate] = $strTablePrefix . '.predicate';
362                    } else {
363                        $this->arUsedVarAssignments[$predicate] = $strTablePrefix . '.predicate';
364                    }
365                }
366            }
367        }
368
369        if (SparqlVariable::isVariable($object)) {
370            if (isset($this->arUnionVarAssignments[$this->nUnionCount][$object])) {
371                //already selected -> add equality check
372                $bWhereEqualityObject = true;
373                $this->arUsedVarTypes[$object]['o'] = true;
374            } else {
375                if (isset($this->arVarAssignments[$object][0])) {
376                    $strTablePrefix = $this->arVarAssignments[$object][0];
377                }
378                $this->arVarAssignments[$object] = array($strTablePrefix, 'o');
379                $this->arUnionVarAssignments[$this->nUnionCount][$object] = array($strTablePrefix, 'o');
380                $this->arUsedVarTypes[$object]['o'] = true;
381                if (self::isResultVar($object, $arResultVars)) {
382                    $arSelect[$object] = $this->createVariableSelectArray(
383                        'o', $object, $strTablePrefix
384                    );
385                    if (isset($this->arUsedVarAssignments[$object])) {
386                        $arRefVars[$object] = $strTablePrefix . '.object';
387                    } else {
388                        $this->arUsedVarAssignments[$object] = $strTablePrefix . '.object';
389                    }
390                }
391                if (isset($this->query->varLanguages[$object])
392                 && $this->query->varLanguages[$object] !== null
393                ) {
394                    $strWhereEquality .=
395                        ' AND ' . $strTablePrefix . '.l_language = "'
396                        . addslashes($this->query->varLanguages[$object]) . '"';
397                }
398                if (isset($this->query->varDatatypes[$object])
399                 && $this->query->varDatatypes[$object] !== null
400                ) {
401                    $strWhereEquality .=
402                        ' AND ' . $strTablePrefix . '.l_datatype = "'
403                        . addslashes($this->query->varDatatypes[$object]) . '"';
404                }
405            }
406        }
407
408        /**
409        * WhereEquality - needs to be done now because strTablePrefix may change
410        */
411        if ($bWhereEqualitySubject) {
412            $strWhereEquality .= ' AND ' . self::getSqlEqualityCondition(
413                            array($strTablePrefix, 's'),
414                            $this->arVarAssignments[$subject]
415                        );
416        }
417        if ($bWhereEqualityPredicate) {
418            $strWhereEquality .= ' AND ' . self::getSqlEqualityCondition(
419                            array($strTablePrefix, 'p'),
420                            $this->arVarAssignments[$predicate]
421                        );
422        }
423        if ($bWhereEqualityObject) {
424            $strWhereEquality .= ' AND ' . self::getSqlEqualityCondition(
425                            array($strTablePrefix, 'o'),
426                            $this->arVarAssignments[$object]
427                        );
428        }
429
430
431        /**
432        *   FROM part
433        */
434        if ($this->nUnionTriplePatternCount == 0) {
435            //first FROM
436            $strFrom    = $this->tblStatements . ' as ' . $strTablePrefix;
437        } else {
438            //normal join
439            if (count($this->arModelIds) == 1) {
440                $strFrom    = 'LEFT JOIN ' . $this->tblStatements . ' as ' . $strTablePrefix
441                            . ' ON t0.modelID = ' . $strTablePrefix . '.modelID';
442            } else if (count($this->arModelIds) > 1) {
443                $arIDs     = array();
444                foreach ($this->arModelIds as $nId) {
445                    $arIDs[] = $strTablePrefix . '.modelID = ' . intval($nId);
446                }
447                $strFrom  = 'LEFT JOIN ' . $this->tblStatements . ' as ' . $strTablePrefix
448                          . ' ON (' . implode(' OR ', $arIDs) . ')';
449            } else {
450                $strFrom    = 'LEFT JOIN ' . $this->tblStatements . ' as ' . $strTablePrefix
451                            . ' ON t0.modelID = ' . $strTablePrefix . '.modelID';
452            }
453
454            foreach ($arRefVars as $strRefVar => $strSqlVar) {
455                $strFrom .= ' AND ' . $this->arUsedVarAssignments[$strRefVar] . ' = ' . $strSqlVar;
456            }
457
458            if ($graphPattern->getOptional() !== null) {
459                $strFrom .=  $this->getSqlCondition($subject  , $strTablePrefix, 'subject')
460                           . $this->getSqlCondition($predicate, $strTablePrefix, 'predicate')
461                           . $this->getSqlCondition($object   , $strTablePrefix, 'object')
462                           . $strWhereEquality;
463            }
464        }
465
466
467        /**
468        *   WHERE part
469        */
470        if ($this->nUnionTriplePatternCount == 0) {
471            if (count($this->arModelIds) == 1) {
472                $strWhere  = $strTablePrefix . '.modelID = ' . intval(reset($this->arModelIds));
473            } else if (count($this->arModelIds) > 1) {
474                $arIDs     = array();
475                foreach ($this->arModelIds as $nId) {
476                    $arIDs[] = $strTablePrefix . '.modelID = ' . intval($nId);
477                }
478                $strWhere  = '(' . implode(' OR ', $arIDs) . ')';
479            } else {
480                //so that we can append an AND
481                $strWhere = '1';
482            }
483        }
484        if ($graphPattern->getOptional() === null || $this->nGraphPatternCount == 0) {
485            $strWhere .=  $this->getSqlCondition($subject  , $strTablePrefix, 'subject')
486                        . $this->getSqlCondition($predicate, $strTablePrefix, 'predicate')
487                        . $this->getSqlCondition($object   , $strTablePrefix, 'object')
488                        . $strWhereEquality;
489        }
490
491        return array($arSelect, $strFrom, $strWhere);
492    }//function getTripleSql(QueryTriple $triple)
493
494
495
496
497    protected function createVariableSelectArray($chType, $varname, $strTablePrefix)
498    {
499        $var = $this->query->getResultVar($varname);
500        if ($var !== false) {
501            if ((string)$var != $varname) {
502                //copy over var assignments
503                $this->arVarAssignments[(string)$var] = $this->arVarAssignments[$varname];
504            }
505
506            //works on non-* only
507            $func = $var->getFunc();
508            if ($func != null) {
509                if ($func == 'datatype') {
510                    if ($chType != 'o') {
511                        throw new SparqlEngineDb_SqlGeneratorException(
512                            'datatype() works on objects only'
513                        );
514                    }
515                    return array(
516                        $strTablePrefix . '.l_datatype as "' . $strTablePrefix . '.' . $this->getSqlVariableNameValue($var) . '"',
517                        '"r"' .                      ' as "' . $strTablePrefix . '.' . $this->getSqlVariableNameIs($var) . '"',
518                        '""' .                       ' as "' . $strTablePrefix . '.' . $this->getSqlVariableNameLanguage($var) . '"',
519                        '""' .                       ' as "' . $strTablePrefix . '.' . $this->getSqlVariableNameDatatype($var) . '"',
520                    );
521                } else if ($func == 'lang') {
522                    if ($chType != 'o') {
523                        throw new SparqlEngineDb_SqlGeneratorException(
524                            'lang() works on objects only'
525                        );
526                    }
527                    return array(
528                        $strTablePrefix . '.l_language as "' . $strTablePrefix . '.' . $this->getSqlVariableNameValue($var) . '"',
529                        '"l"' .                      ' as "' . $strTablePrefix . '.' . $this->getSqlVariableNameIs($var) . '"',
530                        '""' .                       ' as "' . $strTablePrefix . '.' . $this->getSqlVariableNameLanguage($var) . '"',
531                        '""' .                       ' as "' . $strTablePrefix . '.' . $this->getSqlVariableNameDatatype($var) . '"',
532                    );
533                } else {
534                    throw new SparqlEngineDb_SqlGeneratorException(
535                        'Unsupported function for select "' . $func . '"'
536                    );
537                }
538            }
539        }
540
541        switch ($chType) {
542            case 's':
543                return array(
544                    $strTablePrefix . '.subject as "'    . $strTablePrefix . '.' . $this->getSqlVariableNameValue($varname) . '"',
545                    $strTablePrefix . '.subject_is as "' . $strTablePrefix . '.' . $this->getSqlVariableNameIs($varname) . '"'
546                );
547            case 'p':
548                return array(
549                    $strTablePrefix . '.predicate as "' . $strTablePrefix . '.' . $this->getSqlVariableNameValue($varname) . '"'
550                );
551            case 'o':
552                return array(
553                    $strTablePrefix . '.object as "'     . $strTablePrefix . '.' . $this->getSqlVariableNameValue($varname) . '"',
554                    $strTablePrefix . '.object_is as "'  . $strTablePrefix . '.' . $this->getSqlVariableNameIs($varname) . '"',
555                    $strTablePrefix . '.l_language as "' . $strTablePrefix . '.' . $this->getSqlVariableNameLanguage($varname) . '"',
556                    $strTablePrefix . '.l_datatype as "' . $strTablePrefix . '.' . $this->getSqlVariableNameDatatype($varname) . '"',
557                );
558            default:
559                throw new SparqlEngineDb_SqlGeneratorException(
560                    'Unknown sentence type "' . $chType . "', one of (s,p,o) expected"
561                );
562        }
563    }//protected function createVariableSelectArray($chType, $value, $strTablePrefix)
564
565
566
567    /**
568    *   Creates SELECT statements that have the same number of columns.
569    *   Needed for UNIONs.
570    *
571    *   @param array $arSelect  Array of arrays.
572    *       array(
573    *           //for each union part one
574    *           0 => array(
575    *               //foreach triple pattern
576    *               0 => array(
577    *                   '?person'   => array(
578    *                       't0.subject as "t0.subject"'
579    *                   )
580    *               )
581    *           )
582    *       )
583    *   @return array Array of SELECT strings
584    */
585    protected function createEqualSelects($arSelect)
586    {
587        $arNewSelect = array();
588        if (count($arSelect) == 1) {
589            if ($arSelect[0] == array(array())) {
590                //ASK and COUNT
591                return array(array(''));
592            }
593
594            foreach ($arSelect[0] as $arTripleVars) {
595                $ar = array();
596                foreach ($arTripleVars as $arVarParts) {
597                    $ar[] = implode(', ', $arVarParts);
598                }
599                if (count($ar) > 0) {
600                    $arNewSelect[0][] = implode(', ', $ar);
601                }
602            }
603            return $arNewSelect;
604        }
605
606        $arVars = array();
607         foreach ($arSelect as $arUnionVars) {
608            foreach ($arUnionVars as $arTripleVars) {
609                $arVars = array_merge($arVars, array_keys($arTripleVars));
610            }
611        }
612        $arVars = array_unique($arVars);
613
614        foreach ($arSelect as $nUnionCount => $arUnionVars) {
615            $arSelectVars = array();
616            foreach ($arUnionVars as $arTripleVars) {
617                foreach ($arTripleVars as $strVar => $arVarParts) {
618                    $arSelectVars[$strVar] = $arVarParts;
619                }
620            }
621
622            $ars = array();
623            foreach ($arVars as $strVar) {
624                if (isset($arSelectVars[$strVar])) {
625                    $ar     = $arSelectVars[$strVar];
626                    $nCount = count($arSelectVars[$strVar]);
627                } else {
628                    $ar     = array();
629                    $nCount = 0;
630                }
631
632                if ($nCount == 0) {
633                    //nothing of this variable in this union part
634                    $ar[] = 'NULL as '
635                        . '"' . $this->arVarAssignments[$strVar][0] . '.' . $this->arVarAssignments[$strVar]['sql_value'] . '"';
636                }
637                if ((
638                    isset($this->arUsedVarTypes[$strVar]['o'])
639                    || isset($this->arUsedVarTypes[$strVar]['s'])
640                    ) && $nCount < 2
641                ) {
642                    //it's a subject or object, but we don't want the type
643                    $ar[] = 'NULL as '
644                        . '"' . $this->arVarAssignments[$strVar][0] . '.' . $this->arVarAssignments[$strVar]['sql_is'] . '"';
645                }
646                if (isset($this->arUsedVarTypes[$strVar]['o']) && $nCount < 4) {
647                    //it's a subject or object, but we don't want the type
648                    if (isset($this->arVarAssignments[$strVar]['sql_lang'])) {
649                        $strColLanguage = $this->arVarAssignments[$strVar]['sql_lang'];
650                    } else {
651                        $strColLanguage = 'dummyLang';
652                    }
653                    if (isset($this->arVarAssignments[$strVar]['sql_type'])) {
654                        $strColDatatype = $this->arVarAssignments[$strVar]['sql_type'];
655                    } else {
656                        $strColDatatype = 'dummyType';
657                    }
658                    $ar[] = 'NULL as '
659                        . '"' . $this->arVarAssignments[$strVar][0] . '.' . $strColLanguage . '"';
660                    $ar[] = 'NULL as '
661                        . '"' . $this->arVarAssignments[$strVar][0] . '.' . $strColDatatype . '"';
662                }
663                $ars[] = implode(', ', $ar);
664            }
665            $arNewSelect[$nUnionCount] = $ars;
666        }
667
668        return $arNewSelect;
669    }//protected function createEqualSelects($arSelect)
670
671
672
673    /**
674    *   Creates an SQL statement that checks for the value
675    *   of some subject/predicate/object
676    *
677    *   @param mixed    $bject          subject|predicate|object
678    *   @param string   $strTablePrefix Table prefix (e.g. "t0")
679    *   @param string   $strType        Type of $bject ('subject'|'predicate'|'object')
680    *   @return string  Part of the SQL query (prefixed with AND)
681    */
682    function getSqlCondition($bject, $strTablePrefix, $strType)
683    {
684        if (is_string($bject)) {
685            if (SparqlVariable::isVariable($bject)) {
686                //variable?
687                if (self::isPreparedVariable($bject)) {
688                    //no, not really
689                    $value = $this->getPreparedVariablePlaceholder($bject);
690                } else {
691                    //yes
692                    return null;
693                }
694            } else {
695                $value = $this->dbConn->qstr($bject);
696            }
697            //literal
698            return ' AND ' . $strTablePrefix . '.' . $strType . ' = ' . $value;
699        }
700
701        if ($bject instanceof BlankNode) {
702            //Blank node
703            throw new SparqlEngineDb_SqlGeneratorException(
704                'FIXME: Querying for blank nodes not supported'
705            );
706
707        } else if ($bject instanceof Resource) {
708            //Resource
709            $r = ' AND ' . $strTablePrefix . '.' . $strType . ' = '
710                . $this->dbConn->qstr($bject->getURI());
711            if ($strType !== 'predicate') {
712                $r .= ' AND ' . $strTablePrefix . '.' . $strType . '_is ='
713                . ' "r"';
714            }
715            return $r;
716
717        } else if ($bject instanceof Literal) {
718            //Literal
719            //I'm doubling Filter code here, but what the hell
720            $strColDatatype = $strTablePrefix . '.l_datatype';
721            if ($bject->dtype == 'http://www.w3.org/2001/XMLSchema#integer'
722             || $bject->dtype == 'http://www.w3.org/2001/XMLSchema#double'
723            ) {
724                $strVariable = 'CAST(' . $strTablePrefix . '.' . $strType . ' AS DECIMAL(15,10))';
725                $strValue    = $bject->getLabel();
726            } else {
727                $strVariable = $strTablePrefix . '.' . $strType;
728                $strValue    = $this->dbConn->qstr($bject->getLabel());
729            }
730            $r = ' AND ' . $strVariable . ' = ' . $strValue;
731            if ($strType !== 'predicate') {
732                $r .= ' AND ' . $strTablePrefix . '.' . $strType . '_is ='
733                . ' "l"';
734            }
735
736            if ($strType == 'object') {
737                if ($bject->dtype == '' || $bject->dtype == 'http://www.w3.org/2001/XMLSchema#string') {
738                    //string
739                    $r .= ' AND ('
740                        . $strColDatatype . ' = ""'
741                        . ' OR ' . $strColDatatype . ' = "http://www.w3.org/2001/XMLSchema#string"'
742                        . ')';
743                } else {
744                    $r .= ' AND ' . $strColDatatype . ' = "'
745                        . $bject->dtype
746                        . '"';
747                }
748            }
749
750            if ($bject->lang != '') {
751                $strColLanguage = $strTablePrefix . '.l_language';
752                $r .= ' AND ' . $strColLanguage . ' = '
753                   . $this->dbConn->qstr($bject->lang);
754            }
755            return $r;
756
757        } else {
758            throw new SparqlEngineDb_SqlGeneratorException(
759                'Unsupported sentence part: ' . get_class($bject)
760            );
761        }
762    }//function getSqlCondition($bject, $strTablePrefix, $strType)
763
764
765
766    /**
767    *   Checks if the sentence part (subject, predicate or object) in
768    *   $arNew has the same content as $arOld.
769    *   Required for queries like ":x ?a ?a" where predicate and object
770    *   need to have the same value
771    *
772    *   @param array    $arNew  array($strTablePrefix, $strType = s|p|o)
773    *   @param array    $arOld  array($strTablePrefix, $strType = s|p|o)
774    *   @return string
775    */
776    protected static function getSqlEqualityCondition($arNew, $arOld)
777    {
778        $chTypeNew         = $arNew[1]; $chTypeOld         = $arOld[1];
779        $strTablePrefixNew = $arNew[0]; $strTablePrefixOld = $arOld[0];
780
781        if ($chTypeNew == 'p' || $chTypeOld == 'p') {
782            //just check value
783            //FIXME: it might be I need to check for resource type in object and subject
784            return
785                  $strTablePrefixNew . '.' . self::$arTableColumnNames[$chTypeNew]['value']
786                . ' = '
787                . $strTablePrefixOld . '.' . self::$arTableColumnNames[$chTypeOld]['value']
788                ;
789        } else if ($chTypeNew == 's' || $chTypeOld == 's') {
790            //check value and type
791            return
792                  $strTablePrefixNew . '.' . self::$arTableColumnNames[$chTypeNew]['value']
793                . ' = '
794                . $strTablePrefixOld . '.' . self::$arTableColumnNames[$chTypeOld]['value']
795                . ' AND '
796                . $strTablePrefixNew . '.' . self::$arTableColumnNames[$chTypeNew]['is']
797                . ' = '
798                . $strTablePrefixOld . '.' . self::$arTableColumnNames[$chTypeOld]['is']
799                ;
800        } else {
801            //two objects -> check everything
802            return
803                  $strTablePrefixNew . '.object = '     . $strTablePrefixOld . '.object'
804                . ' AND '
805                . $strTablePrefixNew . '.object_is = '  . $strTablePrefixOld . '.object_is'
806                . ' AND '
807                . $strTablePrefixNew . '.l_language = ' . $strTablePrefixOld . '.l_language'
808                . ' AND '
809                . $strTablePrefixNew . '.l_datatype = ' . $strTablePrefixOld . '.l_datatype'
810                ;
811        }
812    }//protected static function getSqlEqualityCondition($arNew, $arOld)
813
814
815
816    /**
817    *   Checks if the given variable name is part of the result
818    *   variables list.
819    *   Needed since $arResultVars may contain "*" that captures all variables.
820    *
821    *   @param string   $strVar         Variable name (e.g. "?p")
822    *   @param array    $arResultVars   Array with result variables
823    *   @return boolean     true if it is a result variable
824    */
825    protected static function isResultVar($strVar, &$arResultVars)
826    {
827        foreach ($arResultVars as $var) {
828            if ($var == '*') {
829                return true;
830            } else if ((is_string($var) && $var == $strVar)
831                || (is_object($var) && $var->getVariable() == $strVar)) {
832                return true;
833            }
834        }
835        return false;
836    }//protected static function isResultVar($strVar, &$arResultVars)
837
838
839
840    /**
841    *   Checks if the given variable is a replacement
842    *   for a prepared statement.
843    *
844    *   @return boolean
845    */
846    public static function isPreparedVariable($bject)
847    {
848        return is_string($bject) && strlen($bject) >= 3
849             && ($bject[0] == '?' || $bject[0] == '$')
850             && ($bject[1] == '?' || $bject[1] == '$')
851        ;
852    }//public static function isPreparedVariable($bject)
853
854
855
856    /**
857    *   Returns a placeholder to be included in the sql statement.
858    *   It will be replaced with a real prepared statement variable later on.
859    *   Also adds it to the internal placeholder array.
860    *
861    *   @param string $strVariable  The variable to get a placeholder for
862    *   @return string placeholder
863    */
864    protected function getPreparedVariablePlaceholder($strVariable)
865    {
866        $strName = substr($strVariable, 2);
867        if (!isset($this->arPlaceholders[$strName])) {
868            $this->arPlaceholders[$strName] = '@$%_PLACEHOLDER_'
869                . count($this->arPlaceholders) . '_%$@';
870        }
871        return $this->arPlaceholders[$strName];
872    }//protected function getPreparedVariablePlaceholder($strVariable)
873
874
875
876    public function getPlaceholders()
877    {
878        return $this->arPlaceholders;
879    }//public function getPlaceholders()
880
881
882
883    public function getVarAssignments()
884    {
885        return $this->arVarAssignments;
886    }//public function getVarAssignments()
887
888
889
890    public function getUsedVarAssignments()
891    {
892        return $this->arUsedVarAssignments;
893    }//public function getUsedVarAssignments()
894
895
896
897    public function getUsedVarTypes()
898    {
899        return $this->arUsedVarTypes;
900    }//public function getUsedVarTypes()
901
902
903
904    /**
905    *   Removes all NULL values from an array and returns it.
906    *
907    *   @param array $array     Some array
908    *   @return array $array without the NULL values.
909    */
910    protected static function removeNull($array)
911    {
912        foreach ($array as $key => &$value) {
913            if ($value === null) {
914                unset($array[$key]);
915            }
916        }
917        return $array;
918    }//protected static function removeNull($array)
919
920
921
922    /**
923    *   Removes a leading AND from the where clause which would render
924    *   the sql illegal.
925    */
926    protected function fixWhere($strWhere)
927    {
928        $strWhere = ltrim($strWhere);
929        if (substr($strWhere, 0, 4) == 'AND ') {
930            $strWhere = substr($strWhere, 4);
931        }
932        return $strWhere;
933    }//protected function fixWhere($strWhere)
934
935
936
937    protected function getSqlVariableName($var)
938    {
939        $strSparqlVar = (string)$var;
940        if (!isset($this->arVarAssignments[$strSparqlVar]['sqlname'])) {
941            if (preg_match('/[a-zA-Z0-9]+/', substr($strSparqlVar, 1))) {
942                $strName = 'v_' . substr($strSparqlVar, 1);
943            } else {
944                $strName = 'va_' . $this->nSqlVariableNameCount++;
945            }
946            $this->arVarAssignments[$strSparqlVar]['sqlname'] = $strName;
947        }
948        return $this->arVarAssignments[$strSparqlVar]['sqlname'];
949    }//protected function getSqlVariableName($var)
950
951
952
953    protected function getSqlVariableNameValue($var)
954    {
955        $strSparqlVar = (string)$var;
956        $this->arVarAssignments[$strSparqlVar]['sql_value'] =
957            'value_' . $this->getSqlVariableName($var);
958        return $this->arVarAssignments[$strSparqlVar]['sql_value'];
959    }//protected function getSqlVariableNameValue($var)
960
961
962
963    protected function getSqlVariableNameIs($var)
964    {
965        $strSparqlVar = (string)$var;
966        $this->arVarAssignments[$strSparqlVar]['sql_is'] =
967            'is_' . $this->getSqlVariableName($var);
968        return $this->arVarAssignments[$strSparqlVar]['sql_is'];
969    }//protected function getSqlVariableNameIs($var)
970
971
972
973    protected function getSqlVariableNameLanguage($var)
974    {
975        $strSparqlVar = (string)$var;
976        $this->arVarAssignments[$strSparqlVar]['sql_lang'] =
977            'lang_' . $this->getSqlVariableName($var);
978        return $this->arVarAssignments[$strSparqlVar]['sql_lang'];
979    }//protected function getSqlVariableNameLanguage($var)
980
981
982
983    protected function getSqlVariableNameDatatype($var)
984    {
985        $strSparqlVar = (string)$var;
986        $this->arVarAssignments[$strSparqlVar]['sql_type'] =
987            'type_' . $this->getSqlVariableName($var);
988        return $this->arVarAssignments[$strSparqlVar]['sql_type'];
989    }//protected function getSqlVariableNameDatatype($var)
990
991
992
993    public function setStatementsTable($tblStatements)
994    {
995        $this->tblStatements = $tblStatements;
996    }//public function setStatementsTable($tblStatements)
997
998}//class SparqlEngineDb_SqlGenerator
999?>
Note: See TracBrowser for help on using the repository browser.