Completed
Push — master ( c17eea...1101f2 )
by Arjay
11:40
created

OracleGrammar::whereDate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 1
rs 9.4285
1
<?php
2
3
namespace Yajra\Oci8\Query\Grammars;
4
5
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
6
use Illuminate\Database\Query\Builder;
7
use Illuminate\Database\Query\Grammars\Grammar;
8
use Illuminate\Support\Str;
9
use Yajra\Oci8\OracleReservedWords;
10
11
class OracleGrammar extends Grammar
12
{
13
    use OracleReservedWords;
14
15
    /**
16
     * The keyword identifier wrapper format.
17
     *
18
     * @var string
19
     */
20
    protected $wrapper = '%s';
21
22
    /**
23
     * @var string
24
     */
25
    protected $schema_prefix = '';
26
27
    /**
28
     * Compile an exists statement into SQL.
29
     *
30
     * @param \Illuminate\Database\Query\Builder $query
31
     * @return string
32
     */
33 3
    public function compileExists(Builder $query)
34
    {
35 3
        $q          = clone $query;
36 3
        $q->columns = [];
37 3
        $q->selectRaw('1 as "exists"')
38 3
          ->whereRaw("rownum = 1");
39
40 3
        return $this->compileSelect($q);
41
    }
42
43
    /**
44
     * Compile a select query into SQL.
45
     *
46
     * @param  \Illuminate\Database\Query\Builder
47
     * @return string
48
     */
49 159
    public function compileSelect(Builder $query)
50
    {
51 159
        if (is_null($query->columns)) {
52
            $query->columns = ['*'];
53
        }
54
55 159
        $components = $this->compileComponents($query);
56
57
        // If an offset is present on the query, we will need to wrap the query in
58
        // a big "ANSI" offset syntax block. This is very nasty compared to the
59
        // other database systems but is necessary for implementing features.
60 159
        if ($this->isPaginationable($query, $components)) {
61 12
            return $this->compileAnsiOffset($query, $components);
62
        }
63
64 150
        return trim($this->concatenate($components));
65
    }
66
67
    /**
68
     * @param Builder $query
69
     * @param array $components
70
     * @return bool
71
     */
72 159
    protected function isPaginationable(Builder $query, array $components)
73
    {
74 159
        return ($query->limit > 0 || $query->offset > 0) && ! array_key_exists('lock', $components);
75
    }
76
77
    /**
78
     * Create a full ANSI offset clause for the query.
79
     *
80
     * @param  \Illuminate\Database\Query\Builder $query
81
     * @param  array $components
82
     * @return string
83
     */
84 12
    protected function compileAnsiOffset(Builder $query, $components)
85
    {
86 12
        $constraint = $this->compileRowConstraint($query);
87
88 12
        $sql = $this->concatenate($components);
89
90
        // We are now ready to build the final SQL query so we'll create a common table
91
        // expression from the query and get the records with row numbers within our
92
        // given limit and offset value that we just put on as a query constraint.
93 12
        $temp = $this->compileTableExpression($sql, $constraint, $query);
94
95 12
        return $temp;
96
    }
97
98
    /**
99
     * Compile the limit / offset row constraint for a query.
100
     *
101
     * @param  \Illuminate\Database\Query\Builder $query
102
     * @return string
103
     */
104 12
    protected function compileRowConstraint($query)
105
    {
106 12
        $start = $query->offset + 1;
107
108 12
        if ($query->limit == 1) {
109 6
            return "= 1";
110
        }
111
112 6
        if ($query->limit > 1) {
113 6
            $finish = $query->offset + $query->limit;
114
115 6
            return "between {$start} and {$finish}";
116
        }
117
118 3
        return ">= {$start}";
119
    }
120
121
    /**
122
     * Compile a common table expression for a query.
123
     *
124
     * @param  string $sql
125
     * @param  string $constraint
126
     * @param Builder $query
127
     * @return string
128
     */
129 12
    protected function compileTableExpression($sql, $constraint, $query)
130
    {
131 12
        if ($query->limit > 1) {
132 6
            return "select t2.* from ( select rownum AS \"rn\", t1.* from ({$sql}) t1 ) t2 where t2.\"rn\" {$constraint}";
133
        } else {
134 9
            return "select * from ({$sql}) where rownum {$constraint}";
135
        }
136
    }
137
138
    /**
139
     * Compile a truncate table statement into SQL.
140
     *
141
     * @param  \Illuminate\Database\Query\Builder $query
142
     * @return array
143
     */
144 3
    public function compileTruncate(Builder $query)
145
    {
146 3
        return ['truncate table ' . $this->wrapTable($query->from) => []];
147
    }
148
149
    /**
150
     * Wrap a table in keyword identifiers.
151
     *
152
     * @param  \Illuminate\Database\Query\Expression|string $table
153
     * @return string
154
     */
155 201
    public function wrapTable($table)
156
    {
157 201
        if ($this->isExpression($table)) {
158
            return $this->getValue($table);
0 ignored issues
show
Bug introduced by
It seems like $table defined by parameter $table on line 155 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...
159
        }
160
161 201
        if (strpos(strtolower($table), ' as ') !== false) {
162
            $table = str_replace(' as ', ' ', $table);
163
        }
164
165 201
        $tableName = $this->wrap($this->tablePrefix . $table, true);
166 201
        $segments  = explode(' ', $table);
167 201
        if (count($segments) > 1) {
168
            $tableName = $this->wrap($this->tablePrefix . $segments[0]) . ' ' . $segments[1];
169
        }
170
171 201
        return $this->getSchemaPrefix() . $tableName;
172
    }
173
174
    /**
175
     * Return the schema prefix.
176
     *
177
     * @return string
178
     */
179 201
    public function getSchemaPrefix()
180
    {
181 201
        return ! empty($this->schema_prefix) ? $this->wrapValue($this->schema_prefix) . '.' : '';
182
    }
183
184
    /**
185
     * Set the schema prefix.
186
     *
187
     * @param string $prefix
188
     */
189
    public function setSchemaPrefix($prefix)
190
    {
191
        $this->schema_prefix = $prefix;
192
    }
193
194
    /**
195
     * Wrap a single string in keyword identifiers.
196
     *
197
     * @param  string $value
198
     * @return string
199
     */
200 201
    protected function wrapValue($value)
201
    {
202 201
        if ($value === '*') {
203 123
            return $value;
204
        }
205
206 201
        $value = $this->isReserved($value) ? Str::lower($value) : Str::upper($value);
207
208 201
        return '"' . str_replace('"', '""', $value) . '"';
209
    }
210
211
    /**
212
     * Compile an insert and get ID statement into SQL.
213
     *
214
     * @param  \Illuminate\Database\Query\Builder $query
215
     * @param  array $values
216
     * @param  string $sequence
217
     * @return string
218
     */
219 6
    public function compileInsertGetId(Builder $query, $values, $sequence = 'id')
220
    {
221 6
        if (empty($sequence)) {
222
            $sequence = 'id';
223
        }
224
225 6
        $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 4)[2]['object'];
226
227 6
        if ($backtrace instanceof EloquentBuilder) {
228
            $model = $backtrace->getModel();
229
            if ($model->sequence && ! isset($values[$model->getKeyName()]) && $model->incrementing) {
230
                $values[$sequence] = null;
231
            }
232
        }
233
234 6
        return $this->compileInsert($query, $values) . ' returning ' . $this->wrap($sequence) . ' into ?';
235
    }
236
237
    /**
238
     * Compile an insert statement into SQL.
239
     *
240
     * @param  \Illuminate\Database\Query\Builder $query
241
     * @param  array $values
242
     * @return string
243
     */
244 15
    public function compileInsert(Builder $query, array $values)
245
    {
246
        // Essentially we will force every insert to be treated as a batch insert which
247
        // simply makes creating the SQL easier for us since we can utilize the same
248
        // basic routine regardless of an amount of records given to us to insert.
249 15
        $table = $this->wrapTable($query->from);
250
251 15
        if (! is_array(reset($values))) {
252 6
            $values = [$values];
253 6
        }
254
255 15
        $columns = $this->columnize(array_keys(reset($values)));
256
257
        // We need to build a list of parameter place-holders of values that are bound
258
        // to the query. Each insert should have the exact same amount of parameter
259
        // bindings so we can just go off the first list of values in this array.
260 15
        $parameters = $this->parameterize(reset($values));
261
262 15
        $value = array_fill(0, count($values), "($parameters)");
263
264 15
        if (count($value) > 1) {
265 3
            $insertQueries = [];
266 3
            foreach ($value as $parameter) {
267 3
                $parameter       = (str_replace(['(', ')'], '', $parameter));
268 3
                $insertQueries[] = "select " . $parameter . " from dual ";
269 3
            }
270 3
            $parameters = implode('union all ', $insertQueries);
271
272 3
            return "insert into $table ($columns) $parameters";
273
        }
274 12
        $parameters = implode(', ', $value);
275
276 12
        return "insert into $table ($columns) values $parameters";
277
    }
278
279
    /**
280
     * Compile an insert with blob field statement into SQL.
281
     *
282
     * @param  \Illuminate\Database\Query\Builder $query
283
     * @param  array $values
284
     * @param  array $binaries
285
     * @param  string $sequence
286
     * @return string
287
     */
288 6
    public function compileInsertLob(Builder $query, $values, $binaries, $sequence = 'id')
289
    {
290 6
        if (empty($sequence)) {
291
            $sequence = 'id';
292
        }
293
294 6
        $table = $this->wrapTable($query->from);
295
296 6
        if (! is_array(reset($values))) {
297 6
            $values = [$values];
298 6
        }
299
300 6
        if (! is_array(reset($binaries))) {
301 6
            $binaries = [$binaries];
302 6
        }
303
304 6
        $columns       = $this->columnize(array_keys(reset($values)));
305 6
        $binaryColumns = $this->columnize(array_keys(reset($binaries)));
306 6
        $columns .= (empty($columns) ? '' : ', ') . $binaryColumns;
307
308 6
        $parameters       = $this->parameterize(reset($values));
309 6
        $binaryParameters = $this->parameterize(reset($binaries));
310
311 6
        $value       = array_fill(0, count($values), "$parameters");
312 6
        $binaryValue = array_fill(0, count($binaries), str_replace('?', 'EMPTY_BLOB()', $binaryParameters));
313
314 6
        $value      = array_merge($value, $binaryValue);
315 6
        $parameters = implode(', ', array_filter($value));
316
317 6
        return "insert into $table ($columns) values ($parameters) returning " . $binaryColumns . ', ' . $this->wrap($sequence) . ' into ' . $binaryParameters . ', ?';
318
    }
319
320
    /**
321
     * Compile an update statement into SQL.
322
     *
323
     * @param  \Illuminate\Database\Query\Builder $query
324
     * @param  array $values
325
     * @param  array $binaries
326
     * @param  string $sequence
327
     * @return string
328
     */
329 6
    public function compileUpdateLob(Builder $query, array $values, array $binaries, $sequence = 'id')
330
    {
331 6
        $table = $this->wrapTable($query->from);
332
333
        // Each one of the columns in the update statements needs to be wrapped in the
334
        // keyword identifiers, also a place-holder needs to be created for each of
335
        // the values in the list of bindings so we can make the sets statements.
336 6
        $columns = [];
337
338 6
        foreach ($values as $key => $value) {
339 3
            $columns[] = $this->wrap($key) . ' = ' . $this->parameter($value);
340 6
        }
341
342 6
        $columns = implode(', ', $columns);
343
344
        // set blob variables
345 6
        if (! is_array(reset($binaries))) {
346 6
            $binaries = [$binaries];
347 6
        }
348 6
        $binaryColumns    = $this->columnize(array_keys(reset($binaries)));
349 6
        $binaryParameters = $this->parameterize(reset($binaries));
350
351
        // create EMPTY_BLOB sql for each binary
352 6
        $binarySql = [];
353 6
        foreach ((array) $binaryColumns as $binary) {
354 6
            $binarySql[] = "$binary = EMPTY_BLOB()";
355 6
        }
356
357
        // prepare binary SQLs
358 6
        if (count($binarySql)) {
359 6
            $binarySql = (empty($columns) ? '' : ', ') . implode(',', $binarySql);
360 6
        }
361
362
        // If the query has any "join" clauses, we will setup the joins on the builder
363
        // and compile them so we can attach them to this update, as update queries
364
        // can get join statements to attach to other tables when they're needed.
365 6
        if (isset($query->joins)) {
366
            $joins = ' ' . $this->compileJoins($query, $query->joins);
367
        } else {
368 6
            $joins = '';
369
        }
370
371
        // Of course, update queries may also be constrained by where clauses so we'll
372
        // need to compile the where clauses and attach it to the query so only the
373
        // intended records are updated by the SQL statements we generate to run.
374 6
        $where = $this->compileWheres($query);
375
376 6
        return "update {$table}{$joins} set $columns$binarySql $where returning " . $binaryColumns . ', ' . $this->wrap($sequence) . ' into ' . $binaryParameters . ', ?';
377
    }
378
379
    /**
380
     * Compile the lock into SQL.
381
     *
382
     * @param  \Illuminate\Database\Query\Builder $query
383
     * @param  bool|string $value
384
     * @return string
385
     */
386 3
    protected function compileLock(Builder $query, $value)
387
    {
388 3
        if (is_string($value)) {
389
            return $value;
390
        }
391
392 3
        if ($value) {
393 3
            return 'for update';
394
        }
395
396
        return '';
397
    }
398
399
    /**
400
     * Compile the "limit" portions of the query.
401
     *
402
     * @param  \Illuminate\Database\Query\Builder $query
403
     * @param  int $limit
404
     * @return string
405
     */
406 12
    protected function compileLimit(Builder $query, $limit)
407
    {
408 12
        return '';
409
    }
410
411
    /**
412
     * Compile the "offset" portions of the query.
413
     *
414
     * @param  \Illuminate\Database\Query\Builder $query
415
     * @param  int $offset
416
     * @return string
417
     */
418 3
    protected function compileOffset(Builder $query, $offset)
419
    {
420 3
        return '';
421
    }
422
423
    /**
424
     * Compile a "where date" clause.
425
     *
426
     * @param  \Illuminate\Database\Query\Builder $query
427
     * @param  array $where
428
     * @return string
429
     */
430 3
    protected function whereDate(Builder $query, $where)
431
    {
432 3
        $value = $this->parameter($where['value']);
433
434 3
        return "trunc({$this->wrap($where['column'])}) {$where['operator']} $value";
435
    }
436
437
    /**
438
     * Compile a date based where clause.
439
     *
440
     * @param  string $type
441
     * @param  \Illuminate\Database\Query\Builder $query
442
     * @param  array $where
443
     * @return string
444
     */
445 9
    protected function dateBasedWhere($type, Builder $query, $where)
446
    {
447 9
        $value = $this->parameter($where['value']);
448
449 9
        return "extract ($type from {$this->wrap($where['column'])}) {$where['operator']} $value";
450
    }
451
}
452