Completed
Push — master ( deab64...61f117 )
by Arjay
10:46 queued 08:50
created

OracleGrammar::compileSelect()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.1406

Importance

Changes 0
Metric Value
cc 3
eloc 7
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 17
ccs 6
cts 8
cp 0.75
crap 3.1406
rs 9.4285
1
<?php
2
3
namespace Yajra\Oci8\Query\Grammars;
4
5
use Illuminate\Database\Query\Builder;
6
use Illuminate\Database\Query\Grammars\Grammar;
7
use Yajra\Oci8\OracleReservedWords;
8
9
class OracleGrammar extends Grammar
10
{
11
    use OracleReservedWords;
12
13
    /**
14
     * The keyword identifier wrapper format.
15
     *
16
     * @var string
17
     */
18
    protected $wrapper = '%s';
19
20
    /**
21
     * @var string
22
     */
23
    protected $schema_prefix = '';
24
25
    /**
26
     * Compile an exists statement into SQL.
27
     *
28
     * @param \Illuminate\Database\Query\Builder $query
29
     * @return string
30
     */
31 3
    public function compileExists(Builder $query)
32
    {
33 3
        $q          = clone $query;
34 3
        $q->columns = [];
35 3
        $q->selectRaw('1 as "exists"')
36 3
          ->whereRaw("rownum = 1");
37
38 3
        return $this->compileSelect($q);
39
    }
40
41
    /**
42
     * Compile a select query into SQL.
43
     *
44
     * @param  \Illuminate\Database\Query\Builder
45
     * @return string
46
     */
47 153
    public function compileSelect(Builder $query)
48
    {
49 153
        if (is_null($query->columns)) {
50
            $query->columns = ['*'];
51
        }
52
53 153
        $components = $this->compileComponents($query);
54
55
        // If an offset is present on the query, we will need to wrap the query in
56
        // a big "ANSI" offset syntax block. This is very nasty compared to the
57
        // other database systems but is necessary for implementing features.
58 153
        if ($this->isPaginationable($query, $components)) {
59 12
            return $this->compileAnsiOffset($query, $components);
60
        }
61
62 144
        return trim($this->concatenate($components));
63
    }
64
65
    /**
66
     * @param Builder $query
67
     * @param array $components
68
     * @return bool
69
     */
70 153
    protected function isPaginationable(Builder $query, array $components)
71
    {
72 153
        return ($query->limit > 0 || $query->offset > 0) && ! array_key_exists('lock', $components);
73
    }
74
75
    /**
76
     * Create a full ANSI offset clause for the query.
77
     *
78
     * @param  \Illuminate\Database\Query\Builder $query
79
     * @param  array $components
80
     * @return string
81
     */
82 12
    protected function compileAnsiOffset(Builder $query, $components)
83
    {
84 12
        $constraint = $this->compileRowConstraint($query);
85
86 12
        $sql = $this->concatenate($components);
87
88
        // We are now ready to build the final SQL query so we'll create a common table
89
        // expression from the query and get the records with row numbers within our
90
        // given limit and offset value that we just put on as a query constraint.
91 12
        $temp = $this->compileTableExpression($sql, $constraint, $query);
92
93 12
        return $temp;
94
    }
95
96
    /**
97
     * Compile the limit / offset row constraint for a query.
98
     *
99
     * @param  \Illuminate\Database\Query\Builder $query
100
     * @return string
101
     */
102 12
    protected function compileRowConstraint($query)
103
    {
104 12
        $start = $query->offset + 1;
105
106 12
        if ($query->limit == 1) {
107 6
            return "= 1";
108
        }
109
110 6
        if ($query->limit > 1) {
111 6
            $finish = $query->offset + $query->limit;
112
113 6
            return "between {$start} and {$finish}";
114
        }
115
116 3
        return ">= {$start}";
117
    }
118
119
    /**
120
     * Compile a common table expression for a query.
121
     *
122
     * @param  string $sql
123
     * @param  string $constraint
124
     * @param Builder $query
125
     * @return string
126
     */
127 12
    protected function compileTableExpression($sql, $constraint, $query)
128
    {
129 12
        if ($query->limit > 1) {
130 6
            return "select t2.* from ( select rownum AS \"rn\", t1.* from ({$sql}) t1 ) t2 where t2.\"rn\" {$constraint}";
131
        } else {
132 9
            return "select * from ({$sql}) where rownum {$constraint}";
133
        }
134
    }
135
136
    /**
137
     * Compile a truncate table statement into SQL.
138
     *
139
     * @param  \Illuminate\Database\Query\Builder $query
140
     * @return array
141
     */
142 3
    public function compileTruncate(Builder $query)
143
    {
144 3
        return ['truncate table ' . $this->wrapTable($query->from) => []];
145
    }
146
147
    /**
148
     * Wrap a table in keyword identifiers.
149
     *
150
     * @param  \Illuminate\Database\Query\Expression|string $table
151
     * @return string
152
     */
153 195
    public function wrapTable($table)
154
    {
155 195
        if ($this->isExpression($table)) {
156
            return $this->getValue($table);
0 ignored issues
show
Bug introduced by
It seems like $table defined by parameter $table on line 153 can also be of type string; however, Illuminate\Database\Grammar::getValue() does only seem to accept object<Illuminate\Database\Query\Expression>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
157
        }
158
159 195
        if (strpos(strtolower($table), ' as ') !== false) {
160
            $table = str_replace(' as ', ' ', $table);
161
        }
162
163 195
        return $this->getSchemaPrefix() . $this->wrap($this->tablePrefix . $table, true);
164
    }
165
166
    /**
167
     * Return the schema prefix
168
     *
169
     * @return string
170
     */
171 195
    public function getSchemaPrefix()
172
    {
173 195
        return ! empty($this->schema_prefix) ? $this->schema_prefix . '.' : '';
174
    }
175
176
    /**
177
     * Set the shema prefix
178
     *
179
     * @param string $prefix
180
     */
181
    public function setSchemaPrefix($prefix)
182
    {
183
        $this->schema_prefix = $prefix;
184
    }
185
186
    /**
187
     * Compile an insert and get ID statement into SQL.
188
     *
189
     * @param  \Illuminate\Database\Query\Builder $query
190
     * @param  array $values
191
     * @param  string $sequence
192
     * @return string
193
     */
194 6
    public function compileInsertGetId(Builder $query, $values, $sequence = 'id')
195
    {
196 6
        if (empty($sequence)) {
197
            $sequence = 'id';
198
        }
199
200 6
        $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 4)[3]['object'];
201
202 6
        if (method_exists($backtrace, 'getModel')) {
203
            $model = $backtrace->getModel();
204
            if ($model->sequence && ! isset($values[$model->getKeyName()]) && $model->incrementing) {
205
                $values[$sequence] = null;
206
            }
207
        }
208
209 6
        return $this->compileInsert($query, $values) . ' returning ' . $this->wrap($sequence) . ' into ?';
210
    }
211
212
    /**
213
     * Compile an insert statement into SQL.
214
     *
215
     * @param  \Illuminate\Database\Query\Builder $query
216
     * @param  array $values
217
     * @return string
218
     */
219 15
    public function compileInsert(Builder $query, array $values)
220
    {
221
        // Essentially we will force every insert to be treated as a batch insert which
222
        // simply makes creating the SQL easier for us since we can utilize the same
223
        // basic routine regardless of an amount of records given to us to insert.
224 15
        $table = $this->wrapTable($query->from);
225
226 15
        if (! is_array(reset($values))) {
227 6
            $values = [$values];
228 6
        }
229
230 15
        $columns = $this->columnize(array_keys(reset($values)));
231
232
        // We need to build a list of parameter place-holders of values that are bound
233
        // to the query. Each insert should have the exact same amount of parameter
234
        // bindings so we can just go off the first list of values in this array.
235 15
        $parameters = $this->parameterize(reset($values));
236
237 15
        $value = array_fill(0, count($values), "($parameters)");
238
239 15
        if (count($value) > 1) {
240 3
            $insertQueries = [];
241 3
            foreach ($value as $parameter) {
242 3
                $parameter       = (str_replace(['(', ')'], '', $parameter));
243 3
                $insertQueries[] = "select " . $parameter . " from dual ";
244 3
            }
245 3
            $parameters = implode('union all ', $insertQueries);
246
247 3
            return "insert into $table ($columns) $parameters";
248
        }
249 12
        $parameters = implode(', ', $value);
250
251 12
        return "insert into $table ($columns) values $parameters";
252
    }
253
254
    /**
255
     * Compile an insert with blob field statement into SQL.
256
     *
257
     * @param  \Illuminate\Database\Query\Builder $query
258
     * @param  array $values
259
     * @param  array $binaries
260
     * @param  string $sequence
261
     * @return string
262
     */
263 6
    public function compileInsertLob(Builder $query, $values, $binaries, $sequence = 'id')
264
    {
265 6
        if (empty($sequence)) {
266
            $sequence = 'id';
267
        }
268
269 6
        $table = $this->wrapTable($query->from);
270
271 6
        if (! is_array(reset($values))) {
272 6
            $values = [$values];
273 6
        }
274
275 6
        if (! is_array(reset($binaries))) {
276 6
            $binaries = [$binaries];
277 6
        }
278
279 6
        $columns       = $this->columnize(array_keys(reset($values)));
280 6
        $binaryColumns = $this->columnize(array_keys(reset($binaries)));
281 6
        $columns .= (empty($columns) ? '' : ', ') . $binaryColumns;
282
283 6
        $parameters       = $this->parameterize(reset($values));
284 6
        $binaryParameters = $this->parameterize(reset($binaries));
285
286 6
        $value       = array_fill(0, count($values), "$parameters");
287 6
        $binaryValue = array_fill(0, count($binaries), str_replace('?', 'EMPTY_BLOB()', $binaryParameters));
288
289 6
        $value      = array_merge($value, $binaryValue);
290 6
        $parameters = implode(', ', array_filter($value));
291
292 6
        return "insert into $table ($columns) values ($parameters) returning " . $binaryColumns . ', ' . $this->wrap($sequence) . ' into ' . $binaryParameters . ', ?';
293
    }
294
295
    /**
296
     * Compile an update statement into SQL.
297
     *
298
     * @param  \Illuminate\Database\Query\Builder $query
299
     * @param  array $values
300
     * @param  array $binaries
301
     * @param  string $sequence
302
     * @return string
303
     */
304 6
    public function compileUpdateLob(Builder $query, array $values, array $binaries, $sequence = 'id')
305
    {
306 6
        $table = $this->wrapTable($query->from);
307
308
        // Each one of the columns in the update statements needs to be wrapped in the
309
        // keyword identifiers, also a place-holder needs to be created for each of
310
        // the values in the list of bindings so we can make the sets statements.
311 6
        $columns = [];
312
313 6
        foreach ($values as $key => $value) {
314 3
            $columns[] = $this->wrap($key) . ' = ' . $this->parameter($value);
315 6
        }
316
317 6
        $columns = implode(', ', $columns);
318
319
        // set blob variables
320 6
        if (! is_array(reset($binaries))) {
321 6
            $binaries = [$binaries];
322 6
        }
323 6
        $binaryColumns    = $this->columnize(array_keys(reset($binaries)));
324 6
        $binaryParameters = $this->parameterize(reset($binaries));
325
326
        // create EMPTY_BLOB sql for each binary
327 6
        $binarySql = [];
328 6
        foreach ((array) $binaryColumns as $binary) {
329 6
            $binarySql[] = "$binary = EMPTY_BLOB()";
330 6
        }
331
332
        // prepare binary SQLs
333 6
        if (count($binarySql)) {
334 6
            $binarySql = (empty($columns) ? '' : ', ') . implode(',', $binarySql);
335 6
        }
336
337
        // If the query has any "join" clauses, we will setup the joins on the builder
338
        // and compile them so we can attach them to this update, as update queries
339
        // can get join statements to attach to other tables when they're needed.
340 6
        if (isset($query->joins)) {
341
            $joins = ' ' . $this->compileJoins($query, $query->joins);
342
        } else {
343 6
            $joins = '';
344
        }
345
346
        // Of course, update queries may also be constrained by where clauses so we'll
347
        // need to compile the where clauses and attach it to the query so only the
348
        // intended records are updated by the SQL statements we generate to run.
349 6
        $where = $this->compileWheres($query);
350
351 6
        return "update {$table}{$joins} set $columns$binarySql $where returning " . $binaryColumns . ', ' . $this->wrap($sequence) . ' into ' . $binaryParameters . ', ?';
352
    }
353
354
    /**
355
     * Compile the lock into SQL.
356
     *
357
     * @param  \Illuminate\Database\Query\Builder $query
358
     * @param  bool|string $value
359
     * @return string
360
     */
361 3
    protected function compileLock(Builder $query, $value)
362
    {
363 3
        if (is_string($value)) {
364
            return $value;
365
        }
366
367 3
        if ($value) {
368 3
            return 'for update';
369
        }
370
371
        return '';
372
    }
373
374
    /**
375
     * Compile the "limit" portions of the query.
376
     *
377
     * @param  \Illuminate\Database\Query\Builder $query
378
     * @param  int $limit
379
     * @return string
380
     */
381 12
    protected function compileLimit(Builder $query, $limit)
382
    {
383 12
        return '';
384
    }
385
386
    /**
387
     * Compile the "offset" portions of the query.
388
     *
389
     * @param  \Illuminate\Database\Query\Builder $query
390
     * @param  int $offset
391
     * @return string
392
     */
393 3
    protected function compileOffset(Builder $query, $offset)
394
    {
395 3
        return '';
396
    }
397
398
    /**
399
     * Compile a "where date" clause.
400
     *
401
     * @param  \Illuminate\Database\Query\Builder $query
402
     * @param  array $where
403
     * @return string
404
     */
405 3
    protected function whereDate(Builder $query, $where)
406
    {
407 3
        $value = $this->parameter($where['value']);
408
409 3
        return "trunc({$this->wrap($where['column'])}) {$where['operator']} $value";
410
    }
411
412
    /**
413
     * Compile a date based where clause.
414
     *
415
     * @param  string $type
416
     * @param  \Illuminate\Database\Query\Builder $query
417
     * @param  array $where
418
     * @return string
419
     */
420 9
    protected function dateBasedWhere($type, Builder $query, $where)
421
    {
422 9
        $value = $this->parameter($where['value']);
423
424 9
        return "extract ($type from {$this->wrap($where['column'])}) {$where['operator']} $value";
425
    }
426
427
    /**
428
     * Wrap a single string in keyword identifiers.
429
     *
430
     * @param  string $value
431
     * @return string
432
     */
433 195 View Code Duplication
    protected function wrapValue($value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
434
    {
435 195
        if ($this->isReserved($value)) {
436 9
            return parent::wrapValue($value);
437
        }
438
439 195
        return $value !== '*' ? sprintf($this->wrapper, $value) : $value;
440
    }
441
}
442