Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 189
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
QueryJoiner
0.00% covered (danger)
0.00%
0 / 189
0.00% covered (danger)
0.00%
0 / 11
462
0.00% covered (danger)
0.00%
0 / 1
 setQuery
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 join
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 on
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 count
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 addLimit
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getLanguage
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 setLanguageCondition
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 sortedQuery
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 1
12
 unSortedQuery
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
6
 createMetaQuery
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2016 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 * @author Christophe GARCIA <christopheg@taotesting.com>
21 * @license GPLv2
22 * @package generis
23 *
24 */
25
26namespace oat\generis\model\kernel\persistence\smoothsql\search;
27
28use oat\search\base\LimitableInterface;
29use oat\search\base\OptionsInterface;
30use oat\search\base\ParentFluateInterface;
31use oat\search\base\Query\DriverSensitiveInterface;
32use oat\search\base\QueryBuilderInterface;
33use oat\search\base\SortableInterface;
34use oat\search\UsableTrait\DriverSensitiveTrait;
35use oat\search\UsableTrait\LimitableTrait;
36use oat\search\UsableTrait\OptionsTrait;
37use oat\search\UsableTrait\ParentFluateTrait;
38use oat\search\UsableTrait\SortableTrait;
39
40/**
41 *
42 * @author Christophe GARCIA <christopheg@taotesting.com>
43 */
44class QueryJoiner implements
45    DriverSensitiveInterface,
46    SortableInterface,
47    LimitableInterface,
48    ParentFluateInterface,
49    OptionsInterface
50{
51    use SortableTrait;
52
53    use LimitableTrait;
54
55    use ParentFluateTrait;
56
57    use DriverSensitiveTrait;
58
59    use OptionsTrait;
60
61    /**
62     *
63     * @var QueryBuilderInterface
64     */
65    protected $query;
66
67    /**
68     *
69     * @var QueryBuilderInterface
70     */
71    protected $join;
72
73    /**
74     *
75     * @var string
76     */
77    protected $on;
78    protected $count = false;
79
80    /**
81     *
82     * @param QueryBuilderInterface $query
83     * @return $this
84     */
85    public function setQuery(QueryBuilderInterface $query)
86    {
87        $this->query = $query->sort([])->setOffset(null)->setLimit(null);
88        return $this;
89    }
90
91    /**
92     *
93     * @param QueryBuilderInterface $query
94     * @return $this
95     */
96    public function join(QueryBuilderInterface $query)
97    {
98        $this->join = $query->sort([])->setOffset(null)->setLimit(null);
99        return $this;
100    }
101
102    /**
103     *
104     * @param string $predicate1
105     * @param string $predicate2
106     * @return $this
107     */
108    public function on($predicate)
109    {
110        $this->on = $predicate;
111        return $this;
112    }
113
114    /**
115     *
116     */
117    public function execute()
118    {
119        /* @var $gateWay GateWay */
120        $gateWay = $this->getParent();
121        $mainQuery = $gateWay->getSerialyser()->setCriteriaList($this->query)->serialyse();
122        $joinQuery = $gateWay->getSerialyser()->setCriteriaList($this->join)->serialyse();
123        return $this->createMetaQuery($mainQuery, $joinQuery, false);
124    }
125
126    public function count()
127    {
128        /* @var $gateWay GateWay */
129        $gateWay = $this->getParent();
130        $mainQuery = $gateWay->getSerialyser()->setCriteriaList($this->query)->serialyse();
131        $joinQuery = $gateWay->getSerialyser()->setCriteriaList($this->join)->serialyse();
132        return $this->createMetaQuery($mainQuery, $joinQuery, true);
133    }
134
135    /**
136     *
137     * @param type $query
138     * @return string
139     */
140    protected function addLimit($query)
141    {
142        $limit = $this->getLimit();
143        $offset = $this->getOffset();
144        if (intval($limit) > 0) {
145            $query .= ' ' . $this->getDriverEscaper()->dbCommand('LIMIT') . ' ' . $limit;
146            if (!is_null($offset)) {
147                $query .= ' ' . $this->getDriverEscaper()->dbCommand('OFFSET') . ' ' . $offset;
148            }
149        }
150        return $query;
151    }
152
153    /**
154     * create query begining
155     * @return $this
156     */
157    public function getLanguage()
158    {
159        $options = $this->getOptions();
160        $language = '';
161        if (array_key_exists('language', $options)) {
162            $language = $this->setLanguageCondition($options['language'], true);
163        }
164        if (array_key_exists('defaultLanguage', $options)) {
165            $language = $this->setLanguageCondition($options['defaultLanguage'], true);
166        }
167
168        return $language;
169    }
170
171    /**
172     * return an SQL string with language filter condition
173     *
174     * @param string $language
175     * @param boolean $emptyAvailable
176     * @return string
177     */
178    public function setLanguageCondition($language, $emptyAvailable = false)
179    {
180        $languageField = $this->getDriverEscaper()->reserved('l_language');
181        $languageValue = $this->getDriverEscaper()->escape($language);
182        $sql = ' AND ( ';
183        $sql .= $languageField . ' = ' . $this->getDriverEscaper()->quote($languageValue) . '';
184        if ($emptyAvailable) {
185            $sql .= ' ' . $this->getDriverEscaper()->dbCommand('OR') . ' ' . $languageField . ' = '
186                . $this->getDriverEscaper()->getEmpty();
187        }
188        $sql .= ' ) ';
189        return $sql;
190    }
191
192    /**
193     *
194     */
195    protected function sortedQuery($main, $join)
196    {
197
198        $sort = $this->getSort();
199        $index = 1;
200
201        $aggrObject = $this->getDriverEscaper()->reserved('J') . '.' .
202                $this->getDriverEscaper()->reserved('object');
203
204        $query = $this->getDriverEscaper()->dbCommand('SELECT') . ' '
205            . $this->getDriverEscaper()->reserved('subject') . ' '
206            . $this->getDriverEscaper()->dbCommand('FROM') . ' ( '
207            . $this->getDriverEscaper()->dbCommand('SELECT') . ' '
208            . $this->getDriverEscaper()->dbCommand('DISTINCT') . ' '
209            . $this->getDriverEscaper()->reserved('T') . '.'
210            . $this->getDriverEscaper()->reserved('subject') . ' , ';
211
212        $sortKeys = [];
213
214        foreach ($sort as $predicate => $sortOrder) {
215            $alias = 'J' . $index;
216            $sorterAlias = 'sorter' . $index;
217
218            $aggrObject = $this->getDriverEscaper()->reserved($alias) . '.' .
219                    $this->getDriverEscaper()->reserved('object');
220
221            $sortKeys[] = $this->getDriverEscaper()->groupAggregation($aggrObject, ' ') . ' ' .
222                    $this->getDriverEscaper()->dbCommand('AS') . ' ' .
223                    $this->getDriverEscaper()->reserved($sorterAlias);
224
225            $index++;
226        }
227
228        $query .= implode(' , ', $sortKeys) . ' '
229            . $this->getDriverEscaper()->dbCommand('FROM') . ' ( ' . $main . ' )'
230            . $this->getDriverEscaper()->dbCommand('AS') . ' '
231            . $this->getDriverEscaper()->reserved('T') . $this->getDriverEscaper()->dbCommand('JOIN')
232            . ' ( ' . $this->getDriverEscaper()->dbCommand('SELECT') . ' '
233            . $this->getDriverEscaper()->reserved('subject') . ' , '
234            . $this->getDriverEscaper()->reserved('object') . ' '
235            . $this->getDriverEscaper()->dbCommand('FROM') . ' '
236            . $this->getDriverEscaper()->reserved('statements') . ' '
237            . $this->getDriverEscaper()->dbCommand('WHERE') . ' '
238            . $this->getDriverEscaper()->reserved('predicate') . ' = '
239            . $this->getDriverEscaper()->quote($this->getDriverEscaper()->escape($this->on)) . ' ) '
240            . $this->getDriverEscaper()->dbCommand('AS') . ' '
241            . $this->getDriverEscaper()->reserved('R') . $this->getDriverEscaper()->dbCommand('ON')
242            . ' ( ' . $this->getDriverEscaper()->reserved('T') . '.'
243            . $this->getDriverEscaper()->reserved('subject') . ' = '
244            . $this->getDriverEscaper()->reserved('R') . '.'
245            . $this->getDriverEscaper()->reserved('subject') . ' ) ';
246
247
248        $index = 1;
249        $sortBy = [];
250        foreach ($sort as $predicate => $sortOrder) {
251            $alias = 'J' . $index;
252            $orderSub = 'SUBJ' . $index;
253            $orderAlias = 'ORDERJ' . $index;
254
255            $query .= $this->getDriverEscaper()->dbCommand('JOIN') . ' ( '
256                . $this->getDriverEscaper()->dbCommand('SELECT') . ' '
257                . $this->getDriverEscaper()->reserved($orderAlias) . '.'
258                . $this->getDriverEscaper()->reserved('subject') . ' , '
259                . $this->getDriverEscaper()->reserved($orderAlias) . '.'
260                . $this->getDriverEscaper()->reserved('object') . ' '
261                . $this->getDriverEscaper()->dbCommand('FROM') . ' ( ' . $join . ')'
262                . $this->getDriverEscaper()->dbCommand('AS') . ' '
263                . $this->getDriverEscaper()->reserved($orderSub) . ' '
264                . $this->getDriverEscaper()->dbCommand('JOIN') . ' ( '
265                . $this->getDriverEscaper()->dbCommand('SELECT') . ' '
266                . $this->getDriverEscaper()->reserved('subject') . ' , '
267                . $this->getDriverEscaper()->reserved('object') . ' '
268                . $this->getDriverEscaper()->dbCommand('FROM') . ' '
269                . $this->getDriverEscaper()->reserved('statements') . ' '
270                . $this->getDriverEscaper()->dbCommand('WHERE') . ' '
271                . $this->getDriverEscaper()->reserved('predicate') . ' = '
272                . $this->getDriverEscaper()->quote($this->getDriverEscaper()->escape($predicate)) . ' '
273                . $this->getLanguage() . ' ' . $this->getDriverEscaper()->dbCommand('GROUP') . ' '
274                . $this->getDriverEscaper()->dbCommand('BY') . ' '
275                . $this->getDriverEscaper()->reserved('subject') . ' , '
276                . $this->getDriverEscaper()->reserved('object') . ' ) '
277                . $this->getDriverEscaper()->dbCommand('AS') . ' '
278                . $this->getDriverEscaper()->reserved($orderAlias) . ' '
279                . $this->getDriverEscaper()->dbCommand('ON') . ' ( '
280                . $this->getDriverEscaper()->reserved($orderSub) . '.'
281                . $this->getDriverEscaper()->reserved('subject') . ' = '
282                . $this->getDriverEscaper()->reserved($orderAlias) . '.'
283                . $this->getDriverEscaper()->reserved('subject') . ' ) ) '
284                . $this->getDriverEscaper()->dbCommand('AS') . ' '
285                . $this->getDriverEscaper()->reserved($alias) . ' '
286                . $this->getDriverEscaper()->dbCommand('ON') . ' ( '
287                . $this->getDriverEscaper()->reserved($alias) . '.'
288                . $this->getDriverEscaper()->reserved('subject') . ' = '
289                . $this->getDriverEscaper()->reserved('R') . '.'
290                . $this->getDriverEscaper()->reserved('object') . ' ) ';
291
292            $sortBy[] = $this->getDriverEscaper()->reserved('sorter' . $index) . ' '
293                . $this->getDriverEscaper()->dbCommand($sortOrder);
294            $index++;
295        }
296
297        $query .= $this->getDriverEscaper()->dbCommand('GROUP') . ' '
298            . $this->getDriverEscaper()->dbCommand('BY') . ' '
299            . $this->getDriverEscaper()->reserved('T') . '.'
300            . $this->getDriverEscaper()->reserved('subject') . ' '
301            . $this->getDriverEscaper()->dbCommand('ORDER') . ' '
302            . $this->getDriverEscaper()->dbCommand('BY') . ' ' . implode(' , ', $sortBy) . ' ) '
303            . $this->getDriverEscaper()->dbCommand('AS') . ' '
304            . $this->getDriverEscaper()->reserved('rootq');
305
306        return ($query);
307    }
308
309    /**
310     *
311     * @param string $main
312     * @param string $join
313     * @return string
314     */
315    protected function unSortedQuery($main, $join)
316    {
317        $query = $this->getDriverEscaper()->dbCommand('SELECT') . ' '
318            . $this->getDriverEscaper()->dbCommand('DISTINCT') . ' '
319            . $this->getDriverEscaper()->reserved('A') . '.'
320            . $this->getDriverEscaper()->reserved('subject') . ' '
321            . $this->getDriverEscaper()->dbCommand('FROM') . ' (' . $main . ')'
322            . $this->getDriverEscaper()->dbCommand('AS') . ' '
323            . $this->getDriverEscaper()->reserved('A') . ' '
324            . $this->getDriverEscaper()->dbCommand('JOIN') . ' ('
325            . $this->getDriverEscaper()->dbCommand('SELECT') . ' '
326            . $this->getDriverEscaper()->reserved('subject') . ' , '
327            . $this->getDriverEscaper()->reserved('object') . ' '
328            . $this->getDriverEscaper()->dbCommand('FROM') . ' '
329            . $this->getDriverEscaper()->reserved('statements') . ' '
330            . $this->getDriverEscaper()->dbCommand('WHERE') . ' '
331            . $this->getDriverEscaper()->reserved('predicate') . ' = '
332            . $this->getDriverEscaper()->quote($this->getDriverEscaper()->escape($this->on)) . ') '
333            . $this->getDriverEscaper()->dbCommand('AS') . ' '
334            . $this->getDriverEscaper()->reserved('R') . ' '
335            . $this->getDriverEscaper()->dbCommand('ON') . ' ('
336            . $this->getDriverEscaper()->reserved('A') . '.'
337            . $this->getDriverEscaper()->reserved('subject') . ' = '
338            . $this->getDriverEscaper()->reserved('R') . '.'
339            . $this->getDriverEscaper()->reserved('subject') . ') '
340            . $this->getDriverEscaper()->dbCommand('JOIN') . ' ('
341            . $this->getDriverEscaper()->dbCommand('SELECT') . ' '
342            . $this->getDriverEscaper()->reserved('subject') . ' '
343            . $this->getDriverEscaper()->dbCommand('FROM') . ' ' . '(' . $join . ')'
344            . $this->getDriverEscaper()->dbCommand('AS') . ' '
345            . $this->getDriverEscaper()->reserved('sd') . ' ) '
346            . $this->getDriverEscaper()->dbCommand('AS') . ' '
347            . $this->getDriverEscaper()->reserved('D') . ' '
348            . $this->getDriverEscaper()->dbCommand('ON') . '('
349            . $this->getDriverEscaper()->reserved('D') . '.'
350            . $this->getDriverEscaper()->reserved('subject') . ' = '
351            . $this->getDriverEscaper()->reserved('R') . '.'
352            . $this->getDriverEscaper()->reserved('object') . ')';
353
354        if ($this->getRandom()) {
355            $query = $this->getDriverEscaper()->dbCommand('SELECT') . ' ' .
356                    $this->getDriverEscaper()->reserved('subject') . ' ' .
357                    $this->getDriverEscaper()->dbCommand('FROM') . ' ' .
358                    ' ( ' . $query . ' ) ' . $this->getDriverEscaper()->dbCommand('AS') . ' ' .
359                    $this->getDriverEscaper()->reserved('finalQ') . ' ' .
360                    $this->getDriverEscaper()->dbCommand('ORDER') . ' ' .
361                    $this->getDriverEscaper()->dbCommand('BY') . ' ' .
362                    $this->getDriverEscaper()->random() . ' ';
363        }
364        return $query;
365    }
366
367    /**
368     * @param string $main
369     * @param string $join
370     * @return string
371     */
372    protected function createMetaQuery($main, $join, $count)
373    {
374        $sort = $this->getSort();
375
376        if (empty($sort)) {
377            $query = $this->unSortedQuery($main, $join);
378        } else {
379            $query = $this->sortedQuery($main, $join);
380        }
381        if ($count) {
382            $query = $this->getDriverEscaper()->dbCommand('SELECT') . ' '
383                . $this->getDriverEscaper()->dbCommand('COUNT') . '( '
384                . $this->getDriverEscaper()->reserved('subject') . ' ) '
385                . $this->getDriverEscaper()->dbCommand('AS') . ' '
386                . $this->getDriverEscaper()->reserved('cpt') . ' '
387                . $this->getDriverEscaper()->dbCommand('FROM') . ' ( ' . $query . ' ) as cptQ';
388            return $query;
389        }
390        return $this->addLimit($query);
391    }
392}