Completed
Pull Request — 4.0 (#153)
by Maxime
03:32
created

OracleGrammar::dateBasedWhere()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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