CI_DB_query_builder::select()   B
last analyzed

Complexity

Conditions 6
Paths 16

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 16
nop 2
dl 0
loc 29
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.0.0
36
 * @filesource
37
 */
38
namespace Rioxygen\CiCoreDatabase;
39
40
/**
41
 * Query Builder Class
42
 *
43
 * This is the platform-independent base Query Builder implementation class.
44
 *
45
 * @package		CodeIgniter
46
 * @subpackage	Drivers
47
 * @category	Database
48
 * @author		EllisLab Dev Team
49
 * @link		https://codeigniter.com/user_guide/database/
50
 */
51
52
abstract class CI_DB_query_builder extends CI_DB_driver {
53
54
	/**
55
	 * Return DELETE SQL flag
56
	 *
57
	 * @var	bool
58
	 */
59
	protected $return_delete_sql		= FALSE;
60
61
	/**
62
	 * Reset DELETE data flag
63
	 *
64
	 * @var	bool
65
	 */
66
	protected $reset_delete_data		= FALSE;
67
68
	/**
69
	 * QB SELECT data
70
	 *
71
	 * @var	array
72
	 */
73
	protected $qb_select			= array();
74
75
	/**
76
	 * QB DISTINCT flag
77
	 *
78
	 * @var	bool
79
	 */
80
	protected $qb_distinct			= FALSE;
81
82
	/**
83
	 * QB FROM data
84
	 *
85
	 * @var	array
86
	 */
87
	protected $qb_from			= array();
88
89
	/**
90
	 * QB JOIN data
91
	 *
92
	 * @var	array
93
	 */
94
	protected $qb_join			= array();
95
96
	/**
97
	 * QB WHERE data
98
	 *
99
	 * @var	array
100
	 */
101
	protected $qb_where			= array();
102
103
	/**
104
	 * QB GROUP BY data
105
	 *
106
	 * @var	array
107
	 */
108
	protected $qb_groupby			= array();
109
110
	/**
111
	 * QB HAVING data
112
	 *
113
	 * @var	array
114
	 */
115
	protected $qb_having			= array();
116
117
	/**
118
	 * QB keys
119
	 *
120
	 * @var	array
121
	 */
122
	protected $qb_keys			= array();
123
124
	/**
125
	 * QB LIMIT data
126
	 *
127
	 * @var	int
128
	 */
129
	protected $qb_limit			= FALSE;
130
131
	/**
132
	 * QB OFFSET data
133
	 *
134
	 * @var	int
135
	 */
136
	protected $qb_offset			= FALSE;
137
138
	/**
139
	 * QB ORDER BY data
140
	 *
141
	 * @var	array
142
	 */
143
	protected $qb_orderby			= array();
144
145
	/**
146
	 * QB data sets
147
	 *
148
	 * @var	array
149
	 */
150
	protected $qb_set			= array();
151
152
	/**
153
	 * QB data set for update_batch()
154
	 *
155
	 * @var	array
156
	 */
157
	protected $qb_set_ub			= array();
158
159
	/**
160
	 * QB aliased tables list
161
	 *
162
	 * @var	array
163
	 */
164
	protected $qb_aliased_tables		= array();
165
166
	/**
167
	 * QB WHERE group started flag
168
	 *
169
	 * @var	bool
170
	 */
171
	protected $qb_where_group_started	= FALSE;
172
173
	/**
174
	 * QB WHERE group count
175
	 *
176
	 * @var	int
177
	 */
178
	protected $qb_where_group_count		= 0;
179
180
	// Query Builder Caching variables
181
182
	/**
183
	 * QB Caching flag
184
	 *
185
	 * @var	bool
186
	 */
187
	protected $qb_caching				= FALSE;
188
189
	/**
190
	 * QB Cache exists list
191
	 *
192
	 * @var	array
193
	 */
194
	protected $qb_cache_exists			= array();
195
196
	/**
197
	 * QB Cache SELECT data
198
	 *
199
	 * @var	array
200
	 */
201
	protected $qb_cache_select			= array();
202
203
	/**
204
	 * QB Cache FROM data
205
	 *
206
	 * @var	array
207
	 */
208
	protected $qb_cache_from			= array();
209
210
	/**
211
	 * QB Cache JOIN data
212
	 *
213
	 * @var	array
214
	 */
215
	protected $qb_cache_join			= array();
216
217
	/**
218
	 * QB Cache aliased tables list
219
	 *
220
	 * @var	array
221
	 */
222
	protected $qb_cache_aliased_tables			= array();
223
224
	/**
225
	 * QB Cache WHERE data
226
	 *
227
	 * @var	array
228
	 */
229
	protected $qb_cache_where			= array();
230
231
	/**
232
	 * QB Cache GROUP BY data
233
	 *
234
	 * @var	array
235
	 */
236
	protected $qb_cache_groupby			= array();
237
238
	/**
239
	 * QB Cache HAVING data
240
	 *
241
	 * @var	array
242
	 */
243
	protected $qb_cache_having			= array();
244
245
	/**
246
	 * QB Cache ORDER BY data
247
	 *
248
	 * @var	array
249
	 */
250
	protected $qb_cache_orderby			= array();
251
252
	/**
253
	 * QB Cache data sets
254
	 *
255
	 * @var	array
256
	 */
257
	protected $qb_cache_set				= array();
258
259
	/**
260
	 * QB No Escape data
261
	 *
262
	 * @var	array
263
	 */
264
	protected $qb_no_escape 			= array();
265
266
	/**
267
	 * QB Cache No Escape data
268
	 *
269
	 * @var	array
270
	 */
271
	protected $qb_cache_no_escape			= array();
272
273
	// --------------------------------------------------------------------
274
275
	/**
276
	 * Select
277
	 *
278
	 * Generates the SELECT portion of the query
279
	 *
280
	 * @param	string
281
	 * @param	mixed
282
	 * @return	CI_DB_query_builder
283
	 */
284
	public function select($select = '*', $escape = NULL)
285
	{
286
		if (is_string($select))
287
		{
288
			$select = explode(',', $select);
289
		}
290
291
		// If the escape value was not set, we will base it on the global setting
292
		is_bool($escape) OR $escape = $this->_protect_identifiers;
293
294
		foreach ($select as $val)
295
		{
296
			$val = trim($val);
297
298
			if ($val !== '')
299
			{
300
				$this->qb_select[] = $val;
301
				$this->qb_no_escape[] = $escape;
302
303
				if ($this->qb_caching === TRUE)
304
				{
305
					$this->qb_cache_select[] = $val;
306
					$this->qb_cache_exists[] = 'select';
307
					$this->qb_cache_no_escape[] = $escape;
308
				}
309
			}
310
		}
311
312
		return $this;
313
	}
314
315
	// --------------------------------------------------------------------
316
317
	/**
318
	 * Select Max
319
	 *
320
	 * Generates a SELECT MAX(field) portion of a query
321
	 *
322
	 * @param	string	the field
323
	 * @param	string	an alias
0 ignored issues
show
Bug introduced by
The type Rioxygen\CiCoreDatabase\an was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
324
	 * @return	CI_DB_query_builder
325
	 */
326
	public function select_max($select = '', $alias = '')
327
	{
328
		return $this->_max_min_avg_sum($select, $alias, 'MAX');
329
	}
330
331
	// --------------------------------------------------------------------
332
333
	/**
334
	 * Select Min
335
	 *
336
	 * Generates a SELECT MIN(field) portion of a query
337
	 *
338
	 * @param	string	the field
339
	 * @param	string	an alias
340
	 * @return	CI_DB_query_builder
341
	 */
342
	public function select_min($select = '', $alias = '')
343
	{
344
		return $this->_max_min_avg_sum($select, $alias, 'MIN');
345
	}
346
347
	// --------------------------------------------------------------------
348
349
	/**
350
	 * Select Average
351
	 *
352
	 * Generates a SELECT AVG(field) portion of a query
353
	 *
354
	 * @param	string	the field
355
	 * @param	string	an alias
356
	 * @return	CI_DB_query_builder
357
	 */
358
	public function select_avg($select = '', $alias = '')
359
	{
360
		return $this->_max_min_avg_sum($select, $alias, 'AVG');
361
	}
362
363
	// --------------------------------------------------------------------
364
365
	/**
366
	 * Select Sum
367
	 *
368
	 * Generates a SELECT SUM(field) portion of a query
369
	 *
370
	 * @param	string	the field
371
	 * @param	string	an alias
372
	 * @return	CI_DB_query_builder
373
	 */
374
	public function select_sum($select = '', $alias = '')
375
	{
376
		return $this->_max_min_avg_sum($select, $alias, 'SUM');
377
	}
378
379
	// --------------------------------------------------------------------
380
381
	/**
382
	 * SELECT [MAX|MIN|AVG|SUM]()
383
	 *
384
	 * @used-by	select_max()
385
	 * @used-by	select_min()
386
	 * @used-by	select_avg()
387
	 * @used-by	select_sum()
388
	 *
389
	 * @param	string	$select	Field name
390
	 * @param	string	$alias
391
	 * @param	string	$type
392
	 * @return	CI_DB_query_builder
393
	 */
394
	protected function _max_min_avg_sum($select = '', $alias = '', $type = 'MAX')
395
	{
396
		if ( ! is_string($select) OR $select === '')
397
		{
398
			$this->display_error('db_invalid_query');
399
		}
400
401
		$type = strtoupper($type);
402
403
		if ( ! in_array($type, array('MAX', 'MIN', 'AVG', 'SUM')))
404
		{
405
			show_error('Invalid function type: '.$type);
0 ignored issues
show
Bug introduced by
The function show_error was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

405
			/** @scrutinizer ignore-call */ 
406
   show_error('Invalid function type: '.$type);
Loading history...
406
		}
407
408
		if ($alias === '')
409
		{
410
			$alias = $this->_create_alias_from_table(trim($select));
411
		}
412
413
		$sql = $type.'('.$this->protect_identifiers(trim($select)).') AS '.$this->escape_identifiers(trim($alias));
414
415
		$this->qb_select[] = $sql;
416
		$this->qb_no_escape[] = NULL;
417
418
		if ($this->qb_caching === TRUE)
419
		{
420
			$this->qb_cache_select[] = $sql;
421
			$this->qb_cache_exists[] = 'select';
422
		}
423
424
		return $this;
425
	}
426
427
	// --------------------------------------------------------------------
428
429
	/**
430
	 * Determines the alias name based on the table
431
	 *
432
	 * @param	string	$item
433
	 * @return	string
434
	 */
435
	protected function _create_alias_from_table($item)
436
	{
437
		if (strpos($item, '.') !== FALSE)
438
		{
439
			$item = explode('.', $item);
440
			return end($item);
441
		}
442
443
		return $item;
444
	}
445
446
	// --------------------------------------------------------------------
447
448
	/**
449
	 * DISTINCT
450
	 *
451
	 * Sets a flag which tells the query string compiler to add DISTINCT
452
	 *
453
	 * @param	bool	$val
454
	 * @return	CI_DB_query_builder
455
	 */
456
	public function distinct($val = TRUE)
457
	{
458
		$this->qb_distinct = is_bool($val) ? $val : TRUE;
0 ignored issues
show
introduced by
The condition is_bool($val) can never be false.
Loading history...
459
		return $this;
460
	}
461
462
	// --------------------------------------------------------------------
463
464
	/**
465
	 * From
466
	 *
467
	 * Generates the FROM portion of the query
468
	 *
469
	 * @param	mixed	$from	can be a string or array
470
	 * @return	CI_DB_query_builder
471
	 */
472
	public function from($from)
473
	{
474
		foreach ((array) $from as $val)
475
		{
476
			if (strpos($val, ',') !== FALSE)
477
			{
478
				foreach (explode(',', $val) as $v)
479
				{
480
					$v = trim($v);
481
					$this->_track_aliases($v);
482
483
					$this->qb_from[] = $v = $this->protect_identifiers($v, TRUE, NULL, FALSE);
484
485
					if ($this->qb_caching === TRUE)
486
					{
487
						$this->qb_cache_from[] = $v;
488
						$this->qb_cache_exists[] = 'from';
489
					}
490
				}
491
			}
492
			else
493
			{
494
				$val = trim($val);
495
496
				// Extract any aliases that might exist. We use this information
497
				// in the protect_identifiers to know whether to add a table prefix
498
				$this->_track_aliases($val);
499
500
				$this->qb_from[] = $val = $this->protect_identifiers($val, TRUE, NULL, FALSE);
501
502
				if ($this->qb_caching === TRUE)
503
				{
504
					$this->qb_cache_from[] = $val;
505
					$this->qb_cache_exists[] = 'from';
506
				}
507
			}
508
		}
509
510
		return $this;
511
	}
512
513
	// --------------------------------------------------------------------
514
515
	/**
516
	 * JOIN
517
	 *
518
	 * Generates the JOIN portion of the query
519
	 *
520
	 * @param	string
521
	 * @param	string	the join condition
522
	 * @param	string	the type of join
523
	 * @param	string	whether not to try to escape identifiers
0 ignored issues
show
Bug introduced by
The type Rioxygen\CiCoreDatabase\whether was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
524
	 * @return	CI_DB_query_builder
525
	 */
526
	public function join($table, $cond, $type = '', $escape = NULL)
527
	{
528
		if ($type !== '')
529
		{
530
			$type = strtoupper(trim($type));
531
532
			if ( ! in_array($type, array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER'), TRUE))
533
			{
534
				$type = '';
535
			}
536
			else
537
			{
538
				$type .= ' ';
539
			}
540
		}
541
542
		// Extract any aliases that might exist. We use this information
543
		// in the protect_identifiers to know whether to add a table prefix
544
		$this->_track_aliases($table);
545
546
		is_bool($escape) OR $escape = $this->_protect_identifiers;
547
548
		if ( ! $this->_has_operator($cond))
549
		{
550
			$cond = ' USING ('.($escape ? $this->escape_identifiers($cond) : $cond).')';
551
		}
552
		elseif ($escape === FALSE)
553
		{
554
			$cond = ' ON '.$cond;
555
		}
556
		else
557
		{
558
			// Split multiple conditions
559
			if (preg_match_all('/\sAND\s|\sOR\s/i', $cond, $joints, PREG_OFFSET_CAPTURE))
560
			{
561
				$conditions = array();
562
				$joints = $joints[0];
563
				array_unshift($joints, array('', 0));
564
565
				for ($i = count($joints) - 1, $pos = strlen($cond); $i >= 0; $i--)
566
				{
567
					$joints[$i][1] += strlen($joints[$i][0]); // offset
568
					$conditions[$i] = substr($cond, $joints[$i][1], $pos - $joints[$i][1]);
569
					$pos = $joints[$i][1] - strlen($joints[$i][0]);
570
					$joints[$i] = $joints[$i][0];
571
				}
572
			}
573
			else
574
			{
575
				$conditions = array($cond);
576
				$joints = array('');
577
			}
578
579
			$cond = ' ON ';
580
			for ($i = 0, $c = count($conditions); $i < $c; $i++)
581
			{
582
				$operator = $this->_get_operator($conditions[$i]);
583
				$cond .= $joints[$i];
584
				$cond .= preg_match("/(\(*)?([\[\]\w\.'-]+)".preg_quote($operator)."(.*)/i", $conditions[$i], $match)
585
					? $match[1].$this->protect_identifiers($match[2]).$operator.$this->protect_identifiers($match[3])
586
					: $conditions[$i];
587
			}
588
		}
589
590
		// Do we want to escape the table name?
591
		if ($escape === TRUE)
592
		{
593
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
594
		}
595
596
		// Assemble the JOIN statement
597
		$this->qb_join[] = $join = $type.'JOIN '.$table.$cond;
598
599
		if ($this->qb_caching === TRUE)
600
		{
601
			$this->qb_cache_join[] = $join;
602
			$this->qb_cache_exists[] = 'join';
603
		}
604
605
		return $this;
606
	}
607
608
	// --------------------------------------------------------------------
609
610
	/**
611
	 * WHERE
612
	 *
613
	 * Generates the WHERE portion of the query.
614
	 * Separates multiple calls with 'AND'.
615
	 *
616
	 * @param	mixed
617
	 * @param	mixed
618
	 * @param	bool
619
	 * @return	CI_DB_query_builder
620
	 */
621
	public function where($key, $value = NULL, $escape = NULL)
622
	{
623
		return $this->_wh('qb_where', $key, $value, 'AND ', $escape);
624
	}
625
626
	// --------------------------------------------------------------------
627
628
	/**
629
	 * OR WHERE
630
	 *
631
	 * Generates the WHERE portion of the query.
632
	 * Separates multiple calls with 'OR'.
633
	 *
634
	 * @param	mixed
635
	 * @param	mixed
636
	 * @param	bool
637
	 * @return	CI_DB_query_builder
638
	 */
639
	public function or_where($key, $value = NULL, $escape = NULL)
640
	{
641
		return $this->_wh('qb_where', $key, $value, 'OR ', $escape);
642
	}
643
644
	// --------------------------------------------------------------------
645
646
	/**
647
	 * WHERE, HAVING
648
	 *
649
	 * @used-by	where()
650
	 * @used-by	or_where()
651
	 * @used-by	having()
652
	 * @used-by	or_having()
653
	 *
654
	 * @param	string	$qb_key	'qb_where' or 'qb_having'
655
	 * @param	mixed	$key
656
	 * @param	mixed	$value
657
	 * @param	string	$type
658
	 * @param	bool	$escape
659
	 * @return	CI_DB_query_builder
660
	 */
661
	protected function _wh($qb_key, $key, $value = NULL, $type = 'AND ', $escape = NULL)
662
	{
663
		$qb_cache_key = ($qb_key === 'qb_having') ? 'qb_cache_having' : 'qb_cache_where';
664
665
		if ( ! is_array($key))
666
		{
667
			$key = array($key => $value);
668
		}
669
670
		// If the escape value was not set will base it on the global setting
671
		is_bool($escape) OR $escape = $this->_protect_identifiers;
672
673
		foreach ($key as $k => $v)
674
		{
675
			$prefix = (count($this->$qb_key) === 0 && count($this->$qb_cache_key) === 0)
676
				? $this->_group_get_type('')
677
				: $this->_group_get_type($type);
678
679
			if ($v !== NULL)
680
			{
681
				if ($escape === TRUE)
682
				{
683
					$v = ' '.$this->escape($v);
684
				}
685
686
				if ( ! $this->_has_operator($k))
687
				{
688
					$k .= ' = ';
689
				}
690
			}
691
			elseif ( ! $this->_has_operator($k))
692
			{
693
				// value appears not to have been set, assign the test to IS NULL
694
				$k .= ' IS NULL';
695
			}
696
			elseif (preg_match('/\s*(!?=|<>|\sIS(?:\s+NOT)?\s)\s*$/i', $k, $match, PREG_OFFSET_CAPTURE))
697
			{
698
				$k = substr($k, 0, $match[0][1]).($match[1][0] === '=' ? ' IS NULL' : ' IS NOT NULL');
699
			}
700
701
			$this->{$qb_key}[] = array('condition' => $prefix.$k.$v, 'escape' => $escape);
702
			if ($this->qb_caching === TRUE)
703
			{
704
				$this->{$qb_cache_key}[] = array('condition' => $prefix.$k.$v, 'escape' => $escape);
705
				$this->qb_cache_exists[] = substr($qb_key, 3);
706
			}
707
708
		}
709
710
		return $this;
711
	}
712
713
	// --------------------------------------------------------------------
714
715
	/**
716
	 * WHERE IN
717
	 *
718
	 * Generates a WHERE field IN('item', 'item') SQL query,
719
	 * joined with 'AND' if appropriate.
720
	 *
721
	 * @param	string	$key	The field to search
722
	 * @param	array	$values	The values searched on
723
	 * @param	bool	$escape
724
	 * @return	CI_DB_query_builder
725
	 */
726
	public function where_in($key = NULL, $values = NULL, $escape = NULL)
727
	{
728
		return $this->_where_in($key, $values, FALSE, 'AND ', $escape);
729
	}
730
731
	// --------------------------------------------------------------------
732
733
	/**
734
	 * OR WHERE IN
735
	 *
736
	 * Generates a WHERE field IN('item', 'item') SQL query,
737
	 * joined with 'OR' if appropriate.
738
	 *
739
	 * @param	string	$key	The field to search
740
	 * @param	array	$values	The values searched on
741
	 * @param	bool	$escape
742
	 * @return	CI_DB_query_builder
743
	 */
744
	public function or_where_in($key = NULL, $values = NULL, $escape = NULL)
745
	{
746
		return $this->_where_in($key, $values, FALSE, 'OR ', $escape);
747
	}
748
749
	// --------------------------------------------------------------------
750
751
	/**
752
	 * WHERE NOT IN
753
	 *
754
	 * Generates a WHERE field NOT IN('item', 'item') SQL query,
755
	 * joined with 'AND' if appropriate.
756
	 *
757
	 * @param	string	$key	The field to search
758
	 * @param	array	$values	The values searched on
759
	 * @param	bool	$escape
760
	 * @return	CI_DB_query_builder
761
	 */
762
	public function where_not_in($key = NULL, $values = NULL, $escape = NULL)
763
	{
764
		return $this->_where_in($key, $values, TRUE, 'AND ', $escape);
765
	}
766
767
	// --------------------------------------------------------------------
768
769
	/**
770
	 * OR WHERE NOT IN
771
	 *
772
	 * Generates a WHERE field NOT IN('item', 'item') SQL query,
773
	 * joined with 'OR' if appropriate.
774
	 *
775
	 * @param	string	$key	The field to search
776
	 * @param	array	$values	The values searched on
777
	 * @param	bool	$escape
778
	 * @return	CI_DB_query_builder
779
	 */
780
	public function or_where_not_in($key = NULL, $values = NULL, $escape = NULL)
781
	{
782
		return $this->_where_in($key, $values, TRUE, 'OR ', $escape);
783
	}
784
785
	// --------------------------------------------------------------------
786
787
	/**
788
	 * Internal WHERE IN
789
	 *
790
	 * @used-by	where_in()
791
	 * @used-by	or_where_in()
792
	 * @used-by	where_not_in()
793
	 * @used-by	or_where_not_in()
794
	 *
795
	 * @param	string	$key	The field to search
796
	 * @param	array	$values	The values searched on
797
	 * @param	bool	$not	If the statement would be IN or NOT IN
798
	 * @param	string	$type
799
	 * @param	bool	$escape
800
	 * @return	CI_DB_query_builder
801
	 */
802
	protected function _where_in($key = NULL, $values = NULL, $not = FALSE, $type = 'AND ', $escape = NULL)
803
	{
804
		if ($key === NULL OR $values === NULL)
805
		{
806
			return $this;
807
		}
808
809
		if ( ! is_array($values))
0 ignored issues
show
introduced by
The condition ! is_array($values) can never be true.
Loading history...
810
		{
811
			$values = array($values);
812
		}
813
814
		is_bool($escape) OR $escape = $this->_protect_identifiers;
815
816
		$not = ($not) ? ' NOT' : '';
817
818
		if ($escape === TRUE)
819
		{
820
			$where_in = array();
821
			foreach ($values as $value)
822
			{
823
				$where_in[] = $this->escape($value);
824
			}
825
		}
826
		else
827
		{
828
			$where_in = array_values($values);
829
		}
830
831
		$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
832
			? $this->_group_get_type('')
833
			: $this->_group_get_type($type);
834
835
		$where_in = array(
836
			'condition' => $prefix.$key.$not.' IN('.implode(', ', $where_in).')',
837
			'escape' => $escape
838
		);
839
840
		$this->qb_where[] = $where_in;
841
		if ($this->qb_caching === TRUE)
842
		{
843
			$this->qb_cache_where[] = $where_in;
844
			$this->qb_cache_exists[] = 'where';
845
		}
846
847
		return $this;
848
	}
849
850
	// --------------------------------------------------------------------
851
852
	/**
853
	 * LIKE
854
	 *
855
	 * Generates a %LIKE% portion of the query.
856
	 * Separates multiple calls with 'AND'.
857
	 *
858
	 * @param	mixed	$field
859
	 * @param	string	$match
860
	 * @param	string	$side
861
	 * @param	bool	$escape
862
	 * @return	CI_DB_query_builder
863
	 */
864
	public function like($field, $match = '', $side = 'both', $escape = NULL)
865
	{
866
		return $this->_like($field, $match, 'AND ', $side, '', $escape);
867
	}
868
869
	// --------------------------------------------------------------------
870
871
	/**
872
	 * NOT LIKE
873
	 *
874
	 * Generates a NOT LIKE portion of the query.
875
	 * Separates multiple calls with 'AND'.
876
	 *
877
	 * @param	mixed	$field
878
	 * @param	string	$match
879
	 * @param	string	$side
880
	 * @param	bool	$escape
881
	 * @return	CI_DB_query_builder
882
	 */
883
	public function not_like($field, $match = '', $side = 'both', $escape = NULL)
884
	{
885
		return $this->_like($field, $match, 'AND ', $side, 'NOT', $escape);
886
	}
887
888
	// --------------------------------------------------------------------
889
890
	/**
891
	 * OR LIKE
892
	 *
893
	 * Generates a %LIKE% portion of the query.
894
	 * Separates multiple calls with 'OR'.
895
	 *
896
	 * @param	mixed	$field
897
	 * @param	string	$match
898
	 * @param	string	$side
899
	 * @param	bool	$escape
900
	 * @return	CI_DB_query_builder
901
	 */
902
	public function or_like($field, $match = '', $side = 'both', $escape = NULL)
903
	{
904
		return $this->_like($field, $match, 'OR ', $side, '', $escape);
905
	}
906
907
	// --------------------------------------------------------------------
908
909
	/**
910
	 * OR NOT LIKE
911
	 *
912
	 * Generates a NOT LIKE portion of the query.
913
	 * Separates multiple calls with 'OR'.
914
	 *
915
	 * @param	mixed	$field
916
	 * @param	string	$match
917
	 * @param	string	$side
918
	 * @param	bool	$escape
919
	 * @return	CI_DB_query_builder
920
	 */
921
	public function or_not_like($field, $match = '', $side = 'both', $escape = NULL)
922
	{
923
		return $this->_like($field, $match, 'OR ', $side, 'NOT', $escape);
924
	}
925
926
	// --------------------------------------------------------------------
927
928
	/**
929
	 * Internal LIKE
930
	 *
931
	 * @used-by	like()
932
	 * @used-by	or_like()
933
	 * @used-by	not_like()
934
	 * @used-by	or_not_like()
935
	 *
936
	 * @param	mixed	$field
937
	 * @param	string	$match
938
	 * @param	string	$type
939
	 * @param	string	$side
940
	 * @param	string	$not
941
	 * @param	bool	$escape
942
	 * @return	CI_DB_query_builder
943
	 */
944
	protected function _like($field, $match = '', $type = 'AND ', $side = 'both', $not = '', $escape = NULL)
945
	{
946
		if ( ! is_array($field))
947
		{
948
			$field = array($field => $match);
949
		}
950
951
		is_bool($escape) OR $escape = $this->_protect_identifiers;
952
		// lowercase $side in case somebody writes e.g. 'BEFORE' instead of 'before' (doh)
953
		$side = strtolower($side);
954
955
		foreach ($field as $k => $v)
956
		{
957
			$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0)
958
				? $this->_group_get_type('') : $this->_group_get_type($type);
959
960
			if ($escape === TRUE)
961
			{
962
				$v = $this->escape_like_str($v);
963
			}
964
965
			if ($side === 'none')
966
			{
967
				$like_statement = "{$prefix} {$k} {$not} LIKE '{$v}'";
968
			}
969
			elseif ($side === 'before')
970
			{
971
				$like_statement = "{$prefix} {$k} {$not} LIKE '%{$v}'";
972
			}
973
			elseif ($side === 'after')
974
			{
975
				$like_statement = "{$prefix} {$k} {$not} LIKE '{$v}%'";
976
			}
977
			else
978
			{
979
				$like_statement = "{$prefix} {$k} {$not} LIKE '%{$v}%'";
980
			}
981
982
			// some platforms require an escape sequence definition for LIKE wildcards
983
			if ($escape === TRUE && $this->_like_escape_str !== '')
984
			{
985
				$like_statement .= sprintf($this->_like_escape_str, $this->_like_escape_chr);
986
			}
987
988
			$this->qb_where[] = array('condition' => $like_statement, 'escape' => $escape);
989
			if ($this->qb_caching === TRUE)
990
			{
991
				$this->qb_cache_where[] = array('condition' => $like_statement, 'escape' => $escape);
992
				$this->qb_cache_exists[] = 'where';
993
			}
994
		}
995
996
		return $this;
997
	}
998
999
	// --------------------------------------------------------------------
1000
1001
	/**
1002
	 * Starts a query group.
1003
	 *
1004
	 * @param	string	$not	(Internal use only)
1005
	 * @param	string	$type	(Internal use only)
1006
	 * @return	CI_DB_query_builder
1007
	 */
1008
	public function group_start($not = '', $type = 'AND ')
1009
	{
1010
		$type = $this->_group_get_type($type);
1011
1012
		$this->qb_where_group_started = TRUE;
1013
		$prefix = (count($this->qb_where) === 0 && count($this->qb_cache_where) === 0) ? '' : $type;
1014
		$where = array(
1015
			'condition' => $prefix.$not.str_repeat(' ', ++$this->qb_where_group_count).' (',
1016
			'escape' => FALSE
1017
		);
1018
1019
		$this->qb_where[] = $where;
1020
		if ($this->qb_caching)
1021
		{
1022
			$this->qb_cache_where[] = $where;
1023
		}
1024
1025
		return $this;
1026
	}
1027
1028
	// --------------------------------------------------------------------
1029
1030
	/**
1031
	 * Starts a query group, but ORs the group
1032
	 *
1033
	 * @return	CI_DB_query_builder
1034
	 */
1035
	public function or_group_start()
1036
	{
1037
		return $this->group_start('', 'OR ');
1038
	}
1039
1040
	// --------------------------------------------------------------------
1041
1042
	/**
1043
	 * Starts a query group, but NOTs the group
1044
	 *
1045
	 * @return	CI_DB_query_builder
1046
	 */
1047
	public function not_group_start()
1048
	{
1049
		return $this->group_start('NOT ', 'AND ');
1050
	}
1051
1052
	// --------------------------------------------------------------------
1053
1054
	/**
1055
	 * Starts a query group, but OR NOTs the group
1056
	 *
1057
	 * @return	CI_DB_query_builder
1058
	 */
1059
	public function or_not_group_start()
1060
	{
1061
		return $this->group_start('NOT ', 'OR ');
1062
	}
1063
1064
	// --------------------------------------------------------------------
1065
1066
	/**
1067
	 * Ends a query group
1068
	 *
1069
	 * @return	CI_DB_query_builder
1070
	 */
1071
	public function group_end()
1072
	{
1073
		$this->qb_where_group_started = FALSE;
1074
		$where = array(
1075
			'condition' => str_repeat(' ', $this->qb_where_group_count--).')',
1076
			'escape' => FALSE
1077
		);
1078
1079
		$this->qb_where[] = $where;
1080
		if ($this->qb_caching)
1081
		{
1082
			$this->qb_cache_where[] = $where;
1083
		}
1084
1085
		return $this;
1086
	}
1087
1088
	// --------------------------------------------------------------------
1089
1090
	/**
1091
	 * Group_get_type
1092
	 *
1093
	 * @used-by	group_start()
1094
	 * @used-by	_like()
1095
	 * @used-by	_wh()
1096
	 * @used-by	_where_in()
1097
	 *
1098
	 * @param	string	$type
1099
	 * @return	string
1100
	 */
1101
	protected function _group_get_type($type)
1102
	{
1103
		if ($this->qb_where_group_started)
1104
		{
1105
			$type = '';
1106
			$this->qb_where_group_started = FALSE;
1107
		}
1108
1109
		return $type;
1110
	}
1111
1112
	// --------------------------------------------------------------------
1113
1114
	/**
1115
	 * GROUP BY
1116
	 *
1117
	 * @param	string	$by
1118
	 * @param	bool	$escape
1119
	 * @return	CI_DB_query_builder
1120
	 */
1121
	public function group_by($by, $escape = NULL)
1122
	{
1123
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1124
1125
		if (is_string($by))
0 ignored issues
show
introduced by
The condition is_string($by) can never be false.
Loading history...
1126
		{
1127
			$by = ($escape === TRUE)
1128
				? explode(',', $by)
1129
				: array($by);
1130
		}
1131
1132
		foreach ($by as $val)
1133
		{
1134
			$val = trim($val);
1135
1136
			if ($val !== '')
1137
			{
1138
				$val = array('field' => $val, 'escape' => $escape);
1139
1140
				$this->qb_groupby[] = $val;
1141
				if ($this->qb_caching === TRUE)
1142
				{
1143
					$this->qb_cache_groupby[] = $val;
1144
					$this->qb_cache_exists[] = 'groupby';
1145
				}
1146
			}
1147
		}
1148
1149
		return $this;
1150
	}
1151
1152
	// --------------------------------------------------------------------
1153
1154
	/**
1155
	 * HAVING
1156
	 *
1157
	 * Separates multiple calls with 'AND'.
1158
	 *
1159
	 * @param	string	$key
1160
	 * @param	string	$value
1161
	 * @param	bool	$escape
1162
	 * @return	CI_DB_query_builder
1163
	 */
1164
	public function having($key, $value = NULL, $escape = NULL)
1165
	{
1166
		return $this->_wh('qb_having', $key, $value, 'AND ', $escape);
1167
	}
1168
1169
	// --------------------------------------------------------------------
1170
1171
	/**
1172
	 * OR HAVING
1173
	 *
1174
	 * Separates multiple calls with 'OR'.
1175
	 *
1176
	 * @param	string	$key
1177
	 * @param	string	$value
1178
	 * @param	bool	$escape
1179
	 * @return	CI_DB_query_builder
1180
	 */
1181
	public function or_having($key, $value = NULL, $escape = NULL)
1182
	{
1183
		return $this->_wh('qb_having', $key, $value, 'OR ', $escape);
1184
	}
1185
1186
	// --------------------------------------------------------------------
1187
1188
	/**
1189
	 * ORDER BY
1190
	 *
1191
	 * @param	string	$orderby
1192
	 * @param	string	$direction	ASC, DESC or RANDOM
1193
	 * @param	bool	$escape
1194
	 * @return	CI_DB_query_builder
1195
	 */
1196
	public function order_by($orderby, $direction = '', $escape = NULL)
1197
	{
1198
		$direction = strtoupper(trim($direction));
1199
1200
		if ($direction === 'RANDOM')
1201
		{
1202
			$direction = '';
1203
1204
			// Do we have a seed value?
1205
			$orderby = ctype_digit((string) $orderby)
1206
				? sprintf($this->_random_keyword[1], $orderby)
1207
				: $this->_random_keyword[0];
1208
		}
1209
		elseif (empty($orderby))
1210
		{
1211
			return $this;
1212
		}
1213
		elseif ($direction !== '')
1214
		{
1215
			$direction = in_array($direction, array('ASC', 'DESC'), TRUE) ? ' '.$direction : '';
1216
		}
1217
1218
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1219
1220
		if ($escape === FALSE)
1221
		{
1222
			$qb_orderby[] = array('field' => $orderby, 'direction' => $direction, 'escape' => FALSE);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$qb_orderby was never initialized. Although not strictly required by PHP, it is generally a good practice to add $qb_orderby = array(); before regardless.
Loading history...
1223
		}
1224
		else
1225
		{
1226
			$qb_orderby = array();
1227
			foreach (explode(',', $orderby) as $field)
1228
			{
1229
				$qb_orderby[] = ($direction === '' && preg_match('/\s+(ASC|DESC)$/i', rtrim($field), $match, PREG_OFFSET_CAPTURE))
1230
					? array('field' => ltrim(substr($field, 0, $match[0][1])), 'direction' => ' '.$match[1][0], 'escape' => TRUE)
1231
					: array('field' => trim($field), 'direction' => $direction, 'escape' => TRUE);
1232
			}
1233
		}
1234
1235
		$this->qb_orderby = array_merge($this->qb_orderby, $qb_orderby);
1236
		if ($this->qb_caching === TRUE)
1237
		{
1238
			$this->qb_cache_orderby = array_merge($this->qb_cache_orderby, $qb_orderby);
1239
			$this->qb_cache_exists[] = 'orderby';
1240
		}
1241
1242
		return $this;
1243
	}
1244
1245
	// --------------------------------------------------------------------
1246
1247
	/**
1248
	 * LIMIT
1249
	 *
1250
	 * @param	int	$value	LIMIT value
1251
	 * @param	int	$offset	OFFSET value
1252
	 * @return	CI_DB_query_builder
1253
	 */
1254
	public function limit($value, $offset = 0)
1255
	{
1256
		is_null($value) OR $this->qb_limit = (int) $value;
1257
		empty($offset) OR $this->qb_offset = (int) $offset;
1258
1259
		return $this;
1260
	}
1261
1262
	// --------------------------------------------------------------------
1263
1264
	/**
1265
	 * Sets the OFFSET value
1266
	 *
1267
	 * @param	int	$offset	OFFSET value
1268
	 * @return	CI_DB_query_builder
1269
	 */
1270
	public function offset($offset)
1271
	{
1272
		empty($offset) OR $this->qb_offset = (int) $offset;
1273
		return $this;
1274
	}
1275
1276
	// --------------------------------------------------------------------
1277
1278
	/**
1279
	 * LIMIT string
1280
	 *
1281
	 * Generates a platform-specific LIMIT clause.
1282
	 *
1283
	 * @param	string	$sql	SQL Query
1284
	 * @return	string
1285
	 */
1286
	protected function _limit($sql)
1287
	{
1288
		return $sql.' LIMIT '.($this->qb_offset ? $this->qb_offset.', ' : '').(int) $this->qb_limit;
1289
	}
1290
1291
	// --------------------------------------------------------------------
1292
1293
	/**
1294
	 * The "set" function.
1295
	 *
1296
	 * Allows key/value pairs to be set for inserting or updating
1297
	 *
1298
	 * @param	mixed
1299
	 * @param	string
1300
	 * @param	bool
1301
	 * @return	CI_DB_query_builder
1302
	 */
1303
	public function set($key, $value = '', $escape = NULL)
1304
	{
1305
		$key = $this->_object_to_array($key);
1306
1307
		if ( ! is_array($key))
0 ignored issues
show
introduced by
The condition ! is_array($key) can never be true.
Loading history...
1308
		{
1309
			$key = array($key => $value);
1310
		}
1311
1312
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1313
1314
		foreach ($key as $k => $v)
1315
		{
1316
			$this->qb_set[$this->protect_identifiers($k, FALSE, $escape)] = ($escape)
1317
				? $this->escape($v) : $v;
1318
		}
1319
1320
		return $this;
1321
	}
1322
1323
	// --------------------------------------------------------------------
1324
1325
	/**
1326
	 * Get SELECT query string
1327
	 *
1328
	 * Compiles a SELECT query string and returns the sql.
0 ignored issues
show
Bug introduced by
The type Rioxygen\CiCoreDatabase\the was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1329
	 *
1330
	 * @param	string	the table name to select from (optional)
1331
	 * @param	bool	TRUE: resets QB values; FALSE: leave QB values alone
1332
	 * @return	string
1333
	 */
1334
	public function get_compiled_select($table = '', $reset = TRUE)
1335
	{
1336
		if ($table !== '')
1337
		{
1338
			$this->_track_aliases($table);
1339
			$this->from($table);
1340
		}
1341
1342
		$select = $this->_compile_select();
1343
1344
		if ($reset === TRUE)
1345
		{
1346
			$this->_reset_select();
1347
		}
1348
1349
		return $select;
1350
	}
1351
1352
	// --------------------------------------------------------------------
1353
1354
	/**
1355
	 * Get
1356
	 *
1357
	 * Compiles the select statement based on the other functions called
1358
	 * and runs the query
1359
	 *
1360
	 * @param	string	the table
1361
	 * @param	string	the limit clause
1362
	 * @param	string	the offset clause
1363
	 * @return	CI_DB_result
1364
	 */
1365
	public function get($table = '', $limit = NULL, $offset = NULL)
1366
	{
1367
		if ($table !== '')
1368
		{
1369
			$this->_track_aliases($table);
1370
			$this->from($table);
1371
		}
1372
1373
		if ( ! empty($limit))
1374
		{
1375
			$this->limit($limit, $offset);
1376
		}
1377
1378
		$result = $this->query($this->_compile_select());
1379
		$this->_reset_select();
1380
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type string|boolean which is incompatible with the documented return type Rioxygen\CiCoreDatabase\CI_DB_result.
Loading history...
1381
	}
1382
1383
	// --------------------------------------------------------------------
1384
1385
	/**
1386
	 * "Count All Results" query
1387
	 *
1388
	 * Generates a platform-specific query string that counts all records
1389
	 * returned by an Query Builder query.
1390
	 *
1391
	 * @param	string
1392
	 * @param	bool	the reset clause
1393
	 * @return	int
1394
	 */
1395
	public function count_all_results($table = '', $reset = TRUE)
1396
	{
1397
		if ($table !== '')
1398
		{
1399
			$this->_track_aliases($table);
1400
			$this->from($table);
1401
		}
1402
1403
		// ORDER BY usage is often problematic here (most notably
1404
		// on Microsoft SQL Server) and ultimately unnecessary
1405
		// for selecting COUNT(*) ...
1406
		$qb_orderby       = $this->qb_orderby;
1407
		$qb_cache_orderby = $this->qb_cache_orderby;
1408
		$this->qb_orderby = $this->qb_cache_orderby = NULL;
1409
1410
		$result = ($this->qb_distinct === TRUE OR ! empty($this->qb_groupby) OR ! empty($this->qb_cache_groupby) OR $this->qb_limit OR $this->qb_offset)
1411
			? $this->query($this->_count_string.$this->protect_identifiers('numrows')."\nFROM (\n".$this->_compile_select()."\n) CI_count_all_results")
1412
			: $this->query($this->_compile_select($this->_count_string.$this->protect_identifiers('numrows')));
0 ignored issues
show
Bug introduced by
$this->_count_string . $..._identifiers('numrows') of type string is incompatible with the type boolean expected by parameter $select_override of Rioxygen\CiCoreDatabase\...lder::_compile_select(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1412
			: $this->query($this->_compile_select(/** @scrutinizer ignore-type */ $this->_count_string.$this->protect_identifiers('numrows')));
Loading history...
1413
1414
		if ($reset === TRUE)
1415
		{
1416
			$this->_reset_select();
1417
		}
1418
		else
1419
		{
1420
			$this->qb_orderby       = $qb_orderby;
1421
			$this->qb_cache_orderby = $qb_cache_orderby;
1422
		}
1423
1424
		if ($result->num_rows() === 0)
1425
		{
1426
			return 0;
1427
		}
1428
1429
		$row = $result->row();
1430
		return (int) $row->numrows;
1431
	}
1432
1433
	// --------------------------------------------------------------------
1434
1435
	/**
1436
	 * Get_Where
1437
	 *
1438
	 * Allows the where clause, limit and offset to be added directly
1439
	 *
1440
	 * @param	string	$table
1441
	 * @param	string	$where
1442
	 * @param	int	$limit
1443
	 * @param	int	$offset
1444
	 * @return	CI_DB_result
1445
	 */
1446
	public function get_where($table = '', $where = NULL, $limit = NULL, $offset = NULL)
1447
	{
1448
		if ($table !== '')
1449
		{
1450
			$this->from($table);
1451
		}
1452
1453
		if ($where !== NULL)
1454
		{
1455
			$this->where($where);
1456
		}
1457
1458
		if ( ! empty($limit))
1459
		{
1460
			$this->limit($limit, $offset);
1461
		}
1462
1463
		$result = $this->query($this->_compile_select());
1464
		$this->_reset_select();
1465
		return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type string|boolean which is incompatible with the documented return type Rioxygen\CiCoreDatabase\CI_DB_result.
Loading history...
1466
	}
1467
1468
	// --------------------------------------------------------------------
1469
1470
	/**
1471
	 * Insert_Batch
1472
	 *
1473
	 * Compiles batch insert strings and runs the queries
1474
	 *
1475
	 * @param	string	$table	Table to insert into
1476
	 * @param	array	$set 	An associative array of insert values
1477
	 * @param	bool	$escape	Whether to escape values and identifiers
1478
	 * @return	int	Number of rows inserted or FALSE on failure
1479
	 */
1480
	public function insert_batch($table, $set = NULL, $escape = NULL, $batch_size = 100)
1481
	{
1482
		if ($set === NULL)
1483
		{
1484
			if (empty($this->qb_set))
1485
			{
1486
				return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?..._must_use_set') : FALSE returns the type false|string which is incompatible with the documented return type integer.
Loading history...
1487
			}
1488
		}
1489
		else
1490
		{
1491
			if (empty($set))
1492
			{
1493
				return ($this->db_debug) ? $this->display_error('insert_batch() called with no data') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?... with no data') : FALSE returns the type false|string which is incompatible with the documented return type integer.
Loading history...
1494
			}
1495
1496
			$this->set_insert_batch($set, '', $escape);
1497
		}
1498
1499
		if (strlen($table) === 0)
1500
		{
1501
			if ( ! isset($this->qb_from[0]))
1502
			{
1503
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?...ust_set_table') : FALSE returns the type false|string which is incompatible with the documented return type integer.
Loading history...
1504
			}
1505
1506
			$table = $this->qb_from[0];
1507
		}
1508
1509
		// Batch this baby
1510
		$affected_rows = 0;
1511
		for ($i = 0, $total = count($this->qb_set); $i < $total; $i += $batch_size)
1512
		{
1513
			if ($this->query($this->_insert_batch($this->protect_identifiers($table, TRUE, $escape, FALSE), $this->qb_keys, array_slice($this->qb_set, $i, $batch_size))))
1514
			{
1515
				$affected_rows += $this->affected_rows();
0 ignored issues
show
Bug introduced by
The method affected_rows() does not exist on Rioxygen\CiCoreDatabase\CI_DB_query_builder. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1515
				$affected_rows += $this->/** @scrutinizer ignore-call */ affected_rows();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1516
			}
1517
		}
1518
1519
		$this->_reset_write();
1520
		return $affected_rows;
1521
	}
1522
1523
	// --------------------------------------------------------------------
1524
1525
	/**
1526
	 * Insert batch statement
1527
	 *
1528
	 * Generates a platform-specific insert string from the supplied data.
1529
	 *
1530
	 * @param	string	$table	Table name
1531
	 * @param	array	$keys	INSERT keys
1532
	 * @param	array	$values	INSERT values
1533
	 * @return	string
1534
	 */
1535
	protected function _insert_batch($table, $keys, $values)
1536
	{
1537
		return 'INSERT INTO '.$table.' ('.implode(', ', $keys).') VALUES '.implode(', ', $values);
1538
	}
1539
1540
	// --------------------------------------------------------------------
1541
1542
	/**
1543
	 * The "set_insert_batch" function.  Allows key/value pairs to be set for batch inserts
1544
	 *
1545
	 * @param	mixed
1546
	 * @param	string
1547
	 * @param	bool
1548
	 * @return	CI_DB_query_builder
1549
	 */
1550
	public function set_insert_batch($key, $value = '', $escape = NULL)
1551
	{
1552
		$key = $this->_object_to_array_batch($key);
1553
1554
		if ( ! is_array($key))
0 ignored issues
show
introduced by
The condition ! is_array($key) can never be true.
Loading history...
1555
		{
1556
			$key = array($key => $value);
1557
		}
1558
1559
		is_bool($escape) OR $escape = $this->_protect_identifiers;
1560
1561
		$keys = array_keys($this->_object_to_array(reset($key)));
1562
		sort($keys);
1563
1564
		foreach ($key as $row)
1565
		{
1566
			$row = $this->_object_to_array($row);
1567
			if (count(array_diff($keys, array_keys($row))) > 0 OR count(array_diff(array_keys($row), $keys)) > 0)
1568
			{
1569
				// batch function above returns an error on an empty array
1570
				$this->qb_set[] = array();
1571
				return;
1572
			}
1573
1574
			ksort($row); // puts $row in the same order as our keys
1575
1576
			if ($escape !== FALSE)
1577
			{
1578
				$clean = array();
1579
				foreach ($row as $value)
1580
				{
1581
					$clean[] = $this->escape($value);
1582
				}
1583
1584
				$row = $clean;
1585
			}
1586
1587
			$this->qb_set[] = '('.implode(',', $row).')';
1588
		}
1589
1590
		foreach ($keys as $k)
1591
		{
1592
			$this->qb_keys[] = $this->protect_identifiers($k, FALSE, $escape);
1593
		}
1594
1595
		return $this;
1596
	}
1597
1598
	// --------------------------------------------------------------------
1599
1600
	/**
1601
	 * Get INSERT query string
1602
	 *
1603
	 * Compiles an insert query and returns the sql
1604
	 *
1605
	 * @param	string	the table to insert into
1606
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
1607
	 * @return	string
1608
	 */
1609
	public function get_compiled_insert($table = '', $reset = TRUE)
1610
	{
1611
		if ($this->_validate_insert($table) === FALSE)
0 ignored issues
show
introduced by
The condition $this->_validate_insert($table) === FALSE can never be true.
Loading history...
1612
		{
1613
			return FALSE;
1614
		}
1615
1616
		$sql = $this->_insert(
1617
			$this->protect_identifiers(
1618
				$this->qb_from[0], TRUE, NULL, FALSE
1619
			),
1620
			array_keys($this->qb_set),
1621
			array_values($this->qb_set)
1622
		);
1623
1624
		if ($reset === TRUE)
1625
		{
1626
			$this->_reset_write();
1627
		}
1628
1629
		return $sql;
1630
	}
1631
1632
	// --------------------------------------------------------------------
1633
1634
	/**
1635
	 * Insert
1636
	 *
1637
	 * Compiles an insert string and runs the query
1638
	 *
1639
	 * @param	string	the table to insert data into
1640
	 * @param	array	an associative array of insert values
1641
	 * @param	bool	$escape	Whether to escape values and identifiers
1642
	 * @return	bool	TRUE on success, FALSE on failure
1643
	 */
1644
	public function insert($table = '', $set = NULL, $escape = NULL)
1645
	{
1646
		if ($set !== NULL)
1647
		{
1648
			$this->set($set, '', $escape);
1649
		}
1650
1651
		if ($this->_validate_insert($table) === FALSE)
0 ignored issues
show
introduced by
The condition $this->_validate_insert($table) === FALSE can never be true.
Loading history...
1652
		{
1653
			return FALSE;
1654
		}
1655
1656
		$sql = $this->_insert(
1657
			$this->protect_identifiers(
1658
				$this->qb_from[0], TRUE, $escape, FALSE
1659
			),
1660
			array_keys($this->qb_set),
1661
			array_values($this->qb_set)
1662
		);
1663
1664
		$this->_reset_write();
1665
		return $this->query($sql);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query($sql) also could return the type object|string which is incompatible with the documented return type boolean.
Loading history...
1666
	}
1667
1668
	// --------------------------------------------------------------------
1669
1670
	/**
1671
	 * Validate Insert
1672
	 *
1673
	 * This method is used by both insert() and get_compiled_insert() to
1674
	 * validate that the there data is actually being set and that table
1675
	 * has been chosen to be inserted into.
1676
	 *
1677
	 * @param	string	the table to insert data into
1678
	 * @return	string
1679
	 */
1680
	protected function _validate_insert($table = '')
1681
	{
1682
		if (count($this->qb_set) === 0)
1683
		{
1684
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?..._must_use_set') : FALSE could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
1685
		}
1686
1687
		if ($table !== '')
1688
		{
1689
			$this->qb_from[0] = $table;
1690
		}
1691
		elseif ( ! isset($this->qb_from[0]))
1692
		{
1693
			return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?...ust_set_table') : FALSE could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
1694
		}
1695
1696
		return TRUE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return TRUE returns the type true which is incompatible with the documented return type string.
Loading history...
1697
	}
1698
1699
	// --------------------------------------------------------------------
1700
1701
	/**
1702
	 * Replace
1703
	 *
1704
	 * Compiles an replace into string and runs the query
1705
	 *
1706
	 * @param	string	the table to replace data into
1707
	 * @param	array	an associative array of insert values
1708
	 * @return	bool	TRUE on success, FALSE on failure
1709
	 */
1710
	public function replace($table = '', $set = NULL)
1711
	{
1712
		if ($set !== NULL)
1713
		{
1714
			$this->set($set);
1715
		}
1716
1717
		if (count($this->qb_set) === 0)
1718
		{
1719
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?..._must_use_set') : FALSE also could return the type string which is incompatible with the documented return type boolean.
Loading history...
1720
		}
1721
1722
		if ($table === '')
1723
		{
1724
			if ( ! isset($this->qb_from[0]))
1725
			{
1726
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?...ust_set_table') : FALSE also could return the type string which is incompatible with the documented return type boolean.
Loading history...
1727
			}
1728
1729
			$table = $this->qb_from[0];
1730
		}
1731
1732
		$sql = $this->_replace($this->protect_identifiers($table, TRUE, NULL, FALSE), array_keys($this->qb_set), array_values($this->qb_set));
1733
1734
		$this->_reset_write();
1735
		return $this->query($sql);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query($sql) also could return the type object|string which is incompatible with the documented return type boolean.
Loading history...
1736
	}
1737
1738
	// --------------------------------------------------------------------
1739
1740
	/**
1741
	 * Replace statement
1742
	 *
1743
	 * Generates a platform-specific replace string from the supplied data
1744
	 *
1745
	 * @param	string	the table name
1746
	 * @param	array	the insert keys
1747
	 * @param	array	the insert values
1748
	 * @return	string
1749
	 */
1750
	protected function _replace($table, $keys, $values)
1751
	{
1752
		return 'REPLACE INTO '.$table.' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
1753
	}
1754
1755
	// --------------------------------------------------------------------
1756
1757
	/**
1758
	 * FROM tables
1759
	 *
1760
	 * Groups tables in FROM clauses if needed, so there is no confusion
1761
	 * about operator precedence.
1762
	 *
1763
	 * Note: This is only used (and overridden) by MySQL and CUBRID.
1764
	 *
1765
	 * @return	string
1766
	 */
1767
	protected function _from_tables()
1768
	{
1769
		return implode(', ', $this->qb_from);
1770
	}
1771
1772
	// --------------------------------------------------------------------
1773
1774
	/**
1775
	 * Get UPDATE query string
1776
	 *
1777
	 * Compiles an update query and returns the sql
1778
	 *
1779
	 * @param	string	the table to update
1780
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
1781
	 * @return	string
1782
	 */
1783
	public function get_compiled_update($table = '', $reset = TRUE)
1784
	{
1785
		// Combine any cached components with the current statements
1786
		$this->_merge_cache();
1787
1788
		if ($this->_validate_update($table) === FALSE)
1789
		{
1790
			return FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return FALSE returns the type false which is incompatible with the documented return type string.
Loading history...
1791
		}
1792
1793
		$sql = $this->_update($this->qb_from[0], $this->qb_set);
1794
1795
		if ($reset === TRUE)
1796
		{
1797
			$this->_reset_write();
1798
		}
1799
1800
		return $sql;
1801
	}
1802
1803
	// --------------------------------------------------------------------
1804
1805
	/**
1806
	 * UPDATE
1807
	 *
1808
	 * Compiles an update string and runs the query.
1809
	 *
1810
	 * @param	string	$table
1811
	 * @param	array	$set	An associative array of update values
1812
	 * @param	mixed	$where
1813
	 * @param	int	$limit
1814
	 * @return	bool	TRUE on success, FALSE on failure
1815
	 */
1816
	public function update($table = '', $set = NULL, $where = NULL, $limit = NULL)
1817
	{
1818
		// Combine any cached components with the current statements
1819
		$this->_merge_cache();
1820
1821
		if ($set !== NULL)
1822
		{
1823
			$this->set($set);
1824
		}
1825
1826
		if ($this->_validate_update($table) === FALSE)
1827
		{
1828
			return FALSE;
1829
		}
1830
1831
		if ($where !== NULL)
1832
		{
1833
			$this->where($where);
1834
		}
1835
1836
		if ( ! empty($limit))
1837
		{
1838
			$this->limit($limit);
1839
		}
1840
1841
		$sql = $this->_update($this->qb_from[0], $this->qb_set);
1842
		$this->_reset_write();
1843
		return $this->query($sql);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query($sql) also could return the type object|string which is incompatible with the documented return type boolean.
Loading history...
1844
	}
1845
1846
	// --------------------------------------------------------------------
1847
1848
	/**
1849
	 * Validate Update
1850
	 *
1851
	 * This method is used by both update() and get_compiled_update() to
1852
	 * validate that data is actually being set and that a table has been
1853
	 * chosen to be update.
1854
	 *
1855
	 * @param	string	the table to update data on
1856
	 * @return	bool
1857
	 */
1858
	protected function _validate_update($table)
1859
	{
1860
		if (count($this->qb_set) === 0)
1861
		{
1862
			return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?..._must_use_set') : FALSE also could return the type string which is incompatible with the documented return type boolean.
Loading history...
1863
		}
1864
1865
		if ($table !== '')
1866
		{
1867
			$this->qb_from = array($this->protect_identifiers($table, TRUE, NULL, FALSE));
1868
		}
1869
		elseif ( ! isset($this->qb_from[0]))
1870
		{
1871
			return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?...ust_set_table') : FALSE also could return the type string which is incompatible with the documented return type boolean.
Loading history...
1872
		}
1873
1874
		return TRUE;
1875
	}
1876
1877
	// --------------------------------------------------------------------
1878
1879
	/**
1880
	 * Update_Batch
1881
	 *
1882
	 * Compiles an update string and runs the query
1883
	 *
1884
	 * @param	string	the table to retrieve the results from
1885
	 * @param	array	an associative array of update values
1886
	 * @param	string	the where key
1887
	 * @return	int	number of rows affected or FALSE on failure
1888
	 */
1889
	public function update_batch($table, $set = NULL, $index = NULL, $batch_size = 100)
1890
	{
1891
		// Combine any cached components with the current statements
1892
		$this->_merge_cache();
1893
1894
		if ($index === NULL)
1895
		{
1896
			return ($this->db_debug) ? $this->display_error('db_must_use_index') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?...ust_use_index') : FALSE returns the type false|string which is incompatible with the documented return type integer.
Loading history...
1897
		}
1898
1899
		if ($set === NULL)
1900
		{
1901
			if (empty($this->qb_set_ub))
1902
			{
1903
				return ($this->db_debug) ? $this->display_error('db_must_use_set') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?..._must_use_set') : FALSE returns the type false|string which is incompatible with the documented return type integer.
Loading history...
1904
			}
1905
		}
1906
		else
1907
		{
1908
			if (empty($set))
1909
			{
1910
				return ($this->db_debug) ? $this->display_error('update_batch() called with no data') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?... with no data') : FALSE returns the type false|string which is incompatible with the documented return type integer.
Loading history...
1911
			}
1912
1913
			$this->set_update_batch($set, $index);
1914
		}
1915
1916
		if (strlen($table) === 0)
1917
		{
1918
			if ( ! isset($this->qb_from[0]))
1919
			{
1920
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?...ust_set_table') : FALSE returns the type false|string which is incompatible with the documented return type integer.
Loading history...
1921
			}
1922
1923
			$table = $this->qb_from[0];
1924
		}
1925
1926
		// Batch this baby
1927
		$affected_rows = 0;
1928
		for ($i = 0, $total = count($this->qb_set_ub); $i < $total; $i += $batch_size)
1929
		{
1930
			if ($this->query($this->_update_batch($this->protect_identifiers($table, TRUE, NULL, FALSE), array_slice($this->qb_set_ub, $i, $batch_size), $index)))
1931
			{
1932
				$affected_rows += $this->affected_rows();
1933
			}
1934
1935
			$this->qb_where = array();
1936
		}
1937
1938
		$this->_reset_write();
1939
		return $affected_rows;
1940
	}
1941
1942
	// --------------------------------------------------------------------
1943
1944
	/**
1945
	 * Update_Batch statement
1946
	 *
1947
	 * Generates a platform-specific batch update string from the supplied data
1948
	 *
1949
	 * @param	string	$table	Table name
1950
	 * @param	array	$values	Update data
1951
	 * @param	string	$index	WHERE key
1952
	 * @return	string
1953
	 */
1954
	protected function _update_batch($table, $values, $index)
1955
	{
1956
		$ids = array();
1957
		foreach ($values as $key => $val)
1958
		{
1959
			$ids[] = $val[$index]['value'];
1960
1961
			foreach (array_keys($val) as $field)
1962
			{
1963
				if ($field !== $index)
1964
				{
1965
					$final[$val[$field]['field']][] = 'WHEN '.$val[$index]['field'].' = '.$val[$index]['value'].' THEN '.$val[$field]['value'];
1966
				}
1967
			}
1968
		}
1969
1970
		$cases = '';
1971
		foreach ($final as $k => $v)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $final does not seem to be defined for all execution paths leading up to this point.
Loading history...
1972
		{
1973
			$cases .= $k." = CASE \n"
1974
				.implode("\n", $v)."\n"
1975
				.'ELSE '.$k.' END, ';
1976
		}
1977
1978
		$this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $val seems to be defined by a foreach iteration on line 1957. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1979
1980
		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
1981
	}
1982
1983
	// --------------------------------------------------------------------
1984
1985
	/**
1986
	 * The "set_update_batch" function.  Allows key/value pairs to be set for batch updating
1987
	 *
1988
	 * @param	array
1989
	 * @param	string
1990
	 * @param	bool
1991
	 * @return	CI_DB_query_builder
1992
	 */
1993
	public function set_update_batch($key, $index = '', $escape = NULL)
1994
	{
1995
		$key = $this->_object_to_array_batch($key);
1996
1997
		if ( ! is_array($key))
0 ignored issues
show
introduced by
The condition ! is_array($key) can never be true.
Loading history...
1998
		{
1999
			// @todo error
2000
		}
2001
2002
		is_bool($escape) OR $escape = $this->_protect_identifiers;
2003
2004
		foreach ($key as $k => $v)
2005
		{
2006
			$index_set = FALSE;
2007
			$clean = array();
2008
			foreach ($v as $k2 => $v2)
2009
			{
2010
				if ($k2 === $index)
2011
				{
2012
					$index_set = TRUE;
2013
				}
2014
2015
				$clean[$k2] = array(
2016
					'field'  => $this->protect_identifiers($k2, FALSE, $escape),
2017
					'value'  => ($escape === FALSE ? $v2 : $this->escape($v2))
2018
				);
2019
			}
2020
2021
			if ($index_set === FALSE)
2022
			{
2023
				return $this->display_error('db_batch_missing_index');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->display_er...b_batch_missing_index') returns the type string which is incompatible with the documented return type Rioxygen\CiCoreDatabase\CI_DB_query_builder.
Loading history...
2024
			}
2025
2026
			$this->qb_set_ub[] = $clean;
2027
		}
2028
2029
		return $this;
2030
	}
2031
2032
	// --------------------------------------------------------------------
2033
2034
	/**
2035
	 * Empty Table
2036
	 *
2037
	 * Compiles a delete string and runs "DELETE FROM table"
2038
	 *
2039
	 * @param	string	the table to empty
2040
	 * @return	bool	TRUE on success, FALSE on failure
2041
	 */
2042
	public function empty_table($table = '')
2043
	{
2044
		if ($table === '')
2045
		{
2046
			if ( ! isset($this->qb_from[0]))
2047
			{
2048
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?...ust_set_table') : FALSE also could return the type string which is incompatible with the documented return type boolean.
Loading history...
2049
			}
2050
2051
			$table = $this->qb_from[0];
2052
		}
2053
		else
2054
		{
2055
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2056
		}
2057
2058
		$sql = $this->_delete($table);
2059
		$this->_reset_write();
2060
		return $this->query($sql);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query($sql) also could return the type object|string which is incompatible with the documented return type boolean.
Loading history...
2061
	}
2062
2063
	// --------------------------------------------------------------------
2064
2065
	/**
2066
	 * Truncate
2067
	 *
2068
	 * Compiles a truncate string and runs the query
2069
	 * If the database does not support the truncate() command
2070
	 * This function maps to "DELETE FROM table"
2071
	 *
2072
	 * @param	string	the table to truncate
2073
	 * @return	bool	TRUE on success, FALSE on failure
2074
	 */
2075
	public function truncate($table = '')
2076
	{
2077
		if ($table === '')
2078
		{
2079
			if ( ! isset($this->qb_from[0]))
2080
			{
2081
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db_debug ?...ust_set_table') : FALSE also could return the type string which is incompatible with the documented return type boolean.
Loading history...
2082
			}
2083
2084
			$table = $this->qb_from[0];
2085
		}
2086
		else
2087
		{
2088
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2089
		}
2090
2091
		$sql = $this->_truncate($table);
2092
		$this->_reset_write();
2093
		return $this->query($sql);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query($sql) also could return the type object|string which is incompatible with the documented return type boolean.
Loading history...
2094
	}
2095
2096
	// --------------------------------------------------------------------
2097
2098
	/**
2099
	 * Truncate statement
2100
	 *
2101
	 * Generates a platform-specific truncate string from the supplied data
2102
	 *
2103
	 * If the database does not support the truncate() command,
2104
	 * then this method maps to 'DELETE FROM table'
2105
	 *
2106
	 * @param	string	the table name
2107
	 * @return	string
2108
	 */
2109
	protected function _truncate($table)
2110
	{
2111
		return 'TRUNCATE '.$table;
2112
	}
2113
2114
	// --------------------------------------------------------------------
2115
2116
	/**
2117
	 * Get DELETE query string
2118
	 *
2119
	 * Compiles a delete query string and returns the sql
2120
	 *
2121
	 * @param	string	the table to delete from
2122
	 * @param	bool	TRUE: reset QB values; FALSE: leave QB values alone
2123
	 * @return	string
2124
	 */
2125
	public function get_compiled_delete($table = '', $reset = TRUE)
2126
	{
2127
		$this->return_delete_sql = TRUE;
2128
		$sql = $this->delete($table, '', NULL, $reset);
2129
		$this->return_delete_sql = FALSE;
2130
		return $sql;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $sql could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
2131
	}
2132
2133
	// --------------------------------------------------------------------
2134
2135
	/**
2136
	 * Delete
2137
	 *
2138
	 * Compiles a delete string and runs the query
2139
	 *
2140
	 * @param	mixed	the table(s) to delete from. String or array
2141
	 * @param	mixed	the where clause
2142
	 * @param	mixed	the limit clause
2143
	 * @param	bool
2144
	 * @return	mixed
2145
	 */
2146
	public function delete($table = '', $where = '', $limit = NULL, $reset_data = TRUE)
2147
	{
2148
		// Combine any cached components with the current statements
2149
		$this->_merge_cache();
2150
2151
		if ($table === '')
2152
		{
2153
			if ( ! isset($this->qb_from[0]))
2154
			{
2155
				return ($this->db_debug) ? $this->display_error('db_must_set_table') : FALSE;
2156
			}
2157
2158
			$table = $this->qb_from[0];
2159
		}
2160
		elseif (is_array($table))
2161
		{
2162
			empty($where) && $reset_data = FALSE;
2163
2164
			foreach ($table as $single_table)
2165
			{
2166
				$this->delete($single_table, $where, $limit, $reset_data);
2167
			}
2168
2169
			return;
2170
		}
2171
		else
2172
		{
2173
			$table = $this->protect_identifiers($table, TRUE, NULL, FALSE);
2174
		}
2175
2176
		if ($where !== '')
2177
		{
2178
			$this->where($where);
2179
		}
2180
2181
		if ( ! empty($limit))
2182
		{
2183
			$this->limit($limit);
2184
		}
2185
2186
		if (count($this->qb_where) === 0)
2187
		{
2188
			return ($this->db_debug) ? $this->display_error('db_del_must_use_where') : FALSE;
2189
		}
2190
2191
		$sql = $this->_delete($table);
2192
		if ($reset_data)
2193
		{
2194
			$this->_reset_write();
2195
		}
2196
2197
		return ($this->return_delete_sql === TRUE) ? $sql : $this->query($sql);
2198
	}
2199
2200
	// --------------------------------------------------------------------
2201
2202
	/**
2203
	 * Delete statement
2204
	 *
2205
	 * Generates a platform-specific delete string from the supplied data
2206
	 *
2207
	 * @param	string	the table name
2208
	 * @return	string
2209
	 */
2210
	protected function _delete($table)
2211
	{
2212
		return 'DELETE FROM '.$table.$this->_compile_wh('qb_where')
2213
			.($this->qb_limit ? ' LIMIT '.$this->qb_limit : '');
2214
	}
2215
2216
	// --------------------------------------------------------------------
2217
2218
	/**
2219
	 * DB Prefix
2220
	 *
2221
	 * Prepends a database prefix if one exists in configuration
2222
	 *
2223
	 * @param	string	the table
2224
	 * @return	string
2225
	 */
2226
	public function dbprefix($table = '')
2227
	{
2228
		if ($table === '')
2229
		{
2230
			$this->display_error('db_table_name_required');
2231
		}
2232
2233
		return $this->dbprefix.$table;
2234
	}
2235
2236
	// --------------------------------------------------------------------
2237
2238
	/**
2239
	 * Set DB Prefix
2240
	 *
2241
	 * Set's the DB Prefix to something new without needing to reconnect
2242
	 *
2243
	 * @param	string	the prefix
2244
	 * @return	string
2245
	 */
2246
	public function set_dbprefix($prefix = '')
2247
	{
2248
		return $this->dbprefix = $prefix;
2249
	}
2250
2251
	// --------------------------------------------------------------------
2252
2253
	/**
2254
	 * Track Aliases
2255
	 *
2256
	 * Used to track SQL statements written with aliased tables.
2257
	 *
2258
	 * @param	string	The table to inspect
0 ignored issues
show
Bug introduced by
The type Rioxygen\CiCoreDatabase\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2259
	 * @return	string
2260
	 */
2261
	protected function _track_aliases($table)
2262
	{
2263
		if (is_array($table))
2264
		{
2265
			foreach ($table as $t)
2266
			{
2267
				$this->_track_aliases($t);
2268
			}
2269
			return;
2270
		}
2271
2272
		// Does the string contain a comma?  If so, we need to separate
2273
		// the string into discreet statements
2274
		if (strpos($table, ',') !== FALSE)
2275
		{
2276
			return $this->_track_aliases(explode(',', $table));
2277
		}
2278
2279
		// if a table alias is used we can recognize it by a space
2280
		if (strpos($table, ' ') !== FALSE)
2281
		{
2282
			// if the alias is written with the AS keyword, remove it
2283
			$table = preg_replace('/\s+AS\s+/i', ' ', $table);
2284
2285
			// Grab the alias
2286
			$table = trim(strrchr($table, ' '));
2287
2288
			// Store the alias, if it doesn't already exist
2289
			if ( ! in_array($table, $this->qb_aliased_tables, TRUE))
2290
			{
2291
				$this->qb_aliased_tables[] = $table;
2292
				if ($this->qb_caching === TRUE && ! in_array($table, $this->qb_cache_aliased_tables, TRUE))
2293
				{
2294
					$this->qb_cache_aliased_tables[] = $table;
2295
					$this->qb_cache_exists[] = 'aliased_tables';
2296
				}
2297
			}
2298
		}
2299
	}
2300
2301
	// --------------------------------------------------------------------
2302
2303
	/**
2304
	 * Compile the SELECT statement
2305
	 *
2306
	 * Generates a query string based on which functions were used.
2307
	 * Should not be called directly.
2308
	 *
2309
	 * @param	bool	$select_override
2310
	 * @return	string
2311
	 */
2312
	protected function _compile_select($select_override = FALSE)
2313
	{
2314
		// Combine any cached components with the current statements
2315
		$this->_merge_cache();
2316
2317
		// Write the "select" portion of the query
2318
		if ($select_override !== FALSE)
2319
		{
2320
			$sql = $select_override;
2321
		}
2322
		else
2323
		{
2324
			$sql = ( ! $this->qb_distinct) ? 'SELECT ' : 'SELECT DISTINCT ';
2325
2326
			if (count($this->qb_select) === 0)
2327
			{
2328
				$sql .= '*';
2329
			}
2330
			else
2331
			{
2332
				// Cycle through the "select" portion of the query and prep each column name.
2333
				// The reason we protect identifiers here rather than in the select() function
2334
				// is because until the user calls the from() function we don't know if there are aliases
2335
				foreach ($this->qb_select as $key => $val)
2336
				{
2337
					$no_escape = isset($this->qb_no_escape[$key]) ? $this->qb_no_escape[$key] : NULL;
2338
					$this->qb_select[$key] = $this->protect_identifiers($val, FALSE, $no_escape);
2339
				}
2340
2341
				$sql .= implode(', ', $this->qb_select);
2342
			}
2343
		}
2344
2345
		// Write the "FROM" portion of the query
2346
		if (count($this->qb_from) > 0)
2347
		{
2348
			$sql .= "\nFROM ".$this->_from_tables();
2349
		}
2350
2351
		// Write the "JOIN" portion of the query
2352
		if (count($this->qb_join) > 0)
2353
		{
2354
			$sql .= "\n".implode("\n", $this->qb_join);
2355
		}
2356
2357
		$sql .= $this->_compile_wh('qb_where')
2358
			.$this->_compile_group_by()
2359
			.$this->_compile_wh('qb_having')
2360
			.$this->_compile_order_by(); // ORDER BY
2361
2362
		// LIMIT
2363
		if ($this->qb_limit OR $this->qb_offset)
2364
		{
2365
			return $this->_limit($sql."\n");
2366
		}
2367
2368
		return $sql;
2369
	}
2370
2371
	// --------------------------------------------------------------------
2372
2373
	/**
2374
	 * Compile WHERE, HAVING statements
2375
	 *
2376
	 * Escapes identifiers in WHERE and HAVING statements at execution time.
2377
	 *
2378
	 * Required so that aliases are tracked properly, regardless of whether
2379
	 * where(), or_where(), having(), or_having are called prior to from(),
2380
	 * join() and dbprefix is added only if needed.
2381
	 *
2382
	 * @param	string	$qb_key	'qb_where' or 'qb_having'
2383
	 * @return	string	SQL statement
2384
	 */
2385
	protected function _compile_wh($qb_key)
2386
	{
2387
		if (count($this->$qb_key) > 0)
2388
		{
2389
			for ($i = 0, $c = count($this->$qb_key); $i < $c; $i++)
2390
			{
2391
				// Is this condition already compiled?
2392
				if (is_string($this->{$qb_key}[$i]))
2393
				{
2394
					continue;
2395
				}
2396
				elseif ($this->{$qb_key}[$i]['escape'] === FALSE)
2397
				{
2398
					$this->{$qb_key}[$i] = $this->{$qb_key}[$i]['condition'];
2399
					continue;
2400
				}
2401
2402
				// Split multiple conditions
2403
				$conditions = preg_split(
2404
					'/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i',
2405
					$this->{$qb_key}[$i]['condition'],
2406
					-1,
2407
					PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
2408
				);
2409
2410
				for ($ci = 0, $cc = count($conditions); $ci < $cc; $ci++)
0 ignored issues
show
Bug introduced by
It seems like $conditions can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2410
				for ($ci = 0, $cc = count(/** @scrutinizer ignore-type */ $conditions); $ci < $cc; $ci++)
Loading history...
2411
				{
2412
					if (($op = $this->_get_operator($conditions[$ci])) === FALSE
2413
						OR ! preg_match('/^(\(?)(.*)('.preg_quote($op, '/').')\s*(.*(?<!\)))?(\)?)$/i', $conditions[$ci], $matches))
2414
					{
2415
						continue;
2416
					}
2417
2418
					// $matches = array(
2419
					//	0 => '(test <= foo)',	/* the whole thing */
2420
					//	1 => '(',		/* optional */
2421
					//	2 => 'test',		/* the field name */
2422
					//	3 => ' <= ',		/* $op */
2423
					//	4 => 'foo',		/* optional, if $op is e.g. 'IS NULL' */
2424
					//	5 => ')'		/* optional */
2425
					// );
2426
2427
					if ( ! empty($matches[4]))
2428
					{
2429
						$this->_is_literal($matches[4]) OR $matches[4] = $this->protect_identifiers(trim($matches[4]));
2430
						$matches[4] = ' '.$matches[4];
2431
					}
2432
2433
					$conditions[$ci] = $matches[1].$this->protect_identifiers(trim($matches[2]))
2434
						.' '.trim($matches[3]).$matches[4].$matches[5];
2435
				}
2436
2437
				$this->{$qb_key}[$i] = implode('', $conditions);
0 ignored issues
show
Bug introduced by
It seems like $conditions can also be of type false; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

2437
				$this->{$qb_key}[$i] = implode('', /** @scrutinizer ignore-type */ $conditions);
Loading history...
2438
			}
2439
2440
			return ($qb_key === 'qb_having' ? "\nHAVING " : "\nWHERE ")
2441
				.implode("\n", $this->$qb_key);
2442
		}
2443
2444
		return '';
2445
	}
2446
2447
	// --------------------------------------------------------------------
2448
2449
	/**
2450
	 * Compile GROUP BY
2451
	 *
2452
	 * Escapes identifiers in GROUP BY statements at execution time.
2453
	 *
2454
	 * Required so that aliases are tracked properly, regardless of whether
2455
	 * group_by() is called prior to from(), join() and dbprefix is added
2456
	 * only if needed.
2457
	 *
2458
	 * @return	string	SQL statement
2459
	 */
2460
	protected function _compile_group_by()
2461
	{
2462
		if (count($this->qb_groupby) > 0)
2463
		{
2464
			for ($i = 0, $c = count($this->qb_groupby); $i < $c; $i++)
2465
			{
2466
				// Is it already compiled?
2467
				if (is_string($this->qb_groupby[$i]))
2468
				{
2469
					continue;
2470
				}
2471
2472
				$this->qb_groupby[$i] = ($this->qb_groupby[$i]['escape'] === FALSE OR $this->_is_literal($this->qb_groupby[$i]['field']))
2473
					? $this->qb_groupby[$i]['field']
2474
					: $this->protect_identifiers($this->qb_groupby[$i]['field']);
2475
			}
2476
2477
			return "\nGROUP BY ".implode(', ', $this->qb_groupby);
2478
		}
2479
2480
		return '';
2481
	}
2482
2483
	// --------------------------------------------------------------------
2484
2485
	/**
2486
	 * Compile ORDER BY
2487
	 *
2488
	 * Escapes identifiers in ORDER BY statements at execution time.
2489
	 *
2490
	 * Required so that aliases are tracked properly, regardless of whether
2491
	 * order_by() is called prior to from(), join() and dbprefix is added
2492
	 * only if needed.
2493
	 *
2494
	 * @return	string	SQL statement
2495
	 */
2496
	protected function _compile_order_by()
2497
	{
2498
		if (empty($this->qb_orderby))
2499
		{
2500
			return '';
2501
		}
2502
2503
		for ($i = 0, $c = count($this->qb_orderby); $i < $c; $i++)
2504
		{
2505
			if (is_string($this->qb_orderby[$i]))
2506
			{
2507
				continue;
2508
			}
2509
2510
			if ($this->qb_orderby[$i]['escape'] !== FALSE && ! $this->_is_literal($this->qb_orderby[$i]['field']))
2511
			{
2512
				$this->qb_orderby[$i]['field'] = $this->protect_identifiers($this->qb_orderby[$i]['field']);
2513
			}
2514
2515
			$this->qb_orderby[$i] = $this->qb_orderby[$i]['field'].$this->qb_orderby[$i]['direction'];
2516
		}
2517
2518
		return "\nORDER BY ".implode(', ', $this->qb_orderby);
2519
	}
2520
2521
	// --------------------------------------------------------------------
2522
2523
	/**
2524
	 * Object to Array
2525
	 *
2526
	 * Takes an object as input and converts the class variables to array key/vals
2527
	 *
2528
	 * @param	object
2529
	 * @return	array
2530
	 */
2531
	protected function _object_to_array($object)
2532
	{
2533
		if ( ! is_object($object))
2534
		{
2535
			return $object;
2536
		}
2537
2538
		$array = array();
2539
		foreach (get_object_vars($object) as $key => $val)
2540
		{
2541
			// There are some built in keys we need to ignore for this conversion
2542
			if ( ! is_object($val) && ! is_array($val) && $key !== '_parent_name')
2543
			{
2544
				$array[$key] = $val;
2545
			}
2546
		}
2547
2548
		return $array;
2549
	}
2550
2551
	// --------------------------------------------------------------------
2552
2553
	/**
2554
	 * Object to Array
2555
	 *
2556
	 * Takes an object as input and converts the class variables to array key/vals
2557
	 *
2558
	 * @param	object
2559
	 * @return	array
2560
	 */
2561
	protected function _object_to_array_batch($object)
2562
	{
2563
		if ( ! is_object($object))
2564
		{
2565
			return $object;
2566
		}
2567
2568
		$array = array();
2569
		$out = get_object_vars($object);
2570
		$fields = array_keys($out);
2571
2572
		foreach ($fields as $val)
2573
		{
2574
			// There are some built in keys we need to ignore for this conversion
2575
			if ($val !== '_parent_name')
2576
			{
2577
				$i = 0;
2578
				foreach ($out[$val] as $data)
2579
				{
2580
					$array[$i++][$val] = $data;
2581
				}
2582
			}
2583
		}
2584
2585
		return $array;
2586
	}
2587
2588
	// --------------------------------------------------------------------
2589
2590
	/**
2591
	 * Start Cache
2592
	 *
2593
	 * Starts QB caching
2594
	 *
2595
	 * @return	CI_DB_query_builder
2596
	 */
2597
	public function start_cache()
2598
	{
2599
		$this->qb_caching = TRUE;
2600
		return $this;
2601
	}
2602
2603
	// --------------------------------------------------------------------
2604
2605
	/**
2606
	 * Stop Cache
2607
	 *
2608
	 * Stops QB caching
2609
	 *
2610
	 * @return	CI_DB_query_builder
2611
	 */
2612
	public function stop_cache()
2613
	{
2614
		$this->qb_caching = FALSE;
2615
		return $this;
2616
	}
2617
2618
	// --------------------------------------------------------------------
2619
2620
	/**
2621
	 * Flush Cache
2622
	 *
2623
	 * Empties the QB cache
2624
	 *
2625
	 * @return	CI_DB_query_builder
2626
	 */
2627
	public function flush_cache()
2628
	{
2629
		$this->_reset_run(array(
2630
			'qb_cache_select'		=> array(),
2631
			'qb_cache_from'			=> array(),
2632
			'qb_cache_join'			=> array(),
2633
			'qb_cache_where'		=> array(),
2634
			'qb_cache_groupby'		=> array(),
2635
			'qb_cache_having'		=> array(),
2636
			'qb_cache_orderby'		=> array(),
2637
			'qb_cache_set'			=> array(),
2638
			'qb_cache_exists'		=> array(),
2639
			'qb_cache_no_escape'	=> array(),
2640
			'qb_cache_aliased_tables'	=> array()
2641
		));
2642
2643
		return $this;
2644
	}
2645
2646
	// --------------------------------------------------------------------
2647
2648
	/**
2649
	 * Merge Cache
2650
	 *
2651
	 * When called, this function merges any cached QB arrays with
2652
	 * locally called ones.
2653
	 *
2654
	 * @return	void
2655
	 */
2656
	protected function _merge_cache()
2657
	{
2658
		if (count($this->qb_cache_exists) === 0)
2659
		{
2660
			return;
2661
		}
2662
		elseif (in_array('select', $this->qb_cache_exists, TRUE))
2663
		{
2664
			$qb_no_escape = $this->qb_cache_no_escape;
2665
		}
2666
2667
		foreach (array_unique($this->qb_cache_exists) as $val) // select, from, etc.
2668
		{
2669
			$qb_variable	= 'qb_'.$val;
2670
			$qb_cache_var	= 'qb_cache_'.$val;
2671
			$qb_new 	= $this->$qb_cache_var;
2672
2673
			for ($i = 0, $c = count($this->$qb_variable); $i < $c; $i++)
2674
			{
2675
				if ( ! in_array($this->{$qb_variable}[$i], $qb_new, TRUE))
2676
				{
2677
					$qb_new[] = $this->{$qb_variable}[$i];
2678
					if ($val === 'select')
2679
					{
2680
						$qb_no_escape[] = $this->qb_no_escape[$i];
2681
					}
2682
				}
2683
			}
2684
2685
			$this->$qb_variable = $qb_new;
2686
			if ($val === 'select')
2687
			{
2688
				$this->qb_no_escape = $qb_no_escape;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $qb_no_escape does not seem to be defined for all execution paths leading up to this point.
Loading history...
2689
			}
2690
		}
2691
	}
2692
2693
	// --------------------------------------------------------------------
2694
2695
	/**
2696
	 * Is literal
2697
	 *
2698
	 * Determines if a string represents a literal value or a field name
2699
	 *
2700
	 * @param	string	$str
2701
	 * @return	bool
2702
	 */
2703
	protected function _is_literal($str)
2704
	{
2705
		$str = trim($str);
2706
2707
		if (empty($str) OR ctype_digit($str) OR (string) (float) $str === $str OR in_array(strtoupper($str), array('TRUE', 'FALSE'), TRUE))
2708
		{
2709
			return TRUE;
2710
		}
2711
2712
		static $_str;
2713
2714
		if (empty($_str))
2715
		{
2716
			$_str = ($this->_escape_char !== '"')
2717
				? array('"', "'") : array("'");
2718
		}
2719
2720
		return in_array($str[0], $_str, TRUE);
2721
	}
2722
2723
	// --------------------------------------------------------------------
2724
2725
	/**
2726
	 * Reset Query Builder values.
2727
	 *
2728
	 * Publicly-visible method to reset the QB values.
2729
	 *
2730
	 * @return	CI_DB_query_builder
2731
	 */
2732
	public function reset_query()
2733
	{
2734
		$this->_reset_select();
2735
		$this->_reset_write();
2736
		return $this;
2737
	}
2738
2739
	// --------------------------------------------------------------------
2740
2741
	/**
2742
	 * Resets the query builder values.  Called by the get() function
2743
	 *
2744
	 * @param	array	An array of fields to reset
0 ignored issues
show
Bug introduced by
The type Rioxygen\CiCoreDatabase\An was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2745
	 * @return	void
2746
	 */
2747
	protected function _reset_run($qb_reset_items)
2748
	{
2749
		foreach ($qb_reset_items as $item => $default_value)
2750
		{
2751
			$this->$item = $default_value;
2752
		}
2753
	}
2754
2755
	// --------------------------------------------------------------------
2756
2757
	/**
2758
	 * Resets the query builder values.  Called by the get() function
2759
	 *
2760
	 * @return	void
2761
	 */
2762
	protected function _reset_select()
2763
	{
2764
		$this->_reset_run(array(
2765
			'qb_select'		=> array(),
2766
			'qb_from'		=> array(),
2767
			'qb_join'		=> array(),
2768
			'qb_where'		=> array(),
2769
			'qb_groupby'		=> array(),
2770
			'qb_having'		=> array(),
2771
			'qb_orderby'		=> array(),
2772
			'qb_aliased_tables'	=> array(),
2773
			'qb_no_escape'		=> array(),
2774
			'qb_distinct'		=> FALSE,
2775
			'qb_limit'		=> FALSE,
2776
			'qb_offset'		=> FALSE
2777
		));
2778
	}
2779
2780
	// --------------------------------------------------------------------
2781
2782
	/**
2783
	 * Resets the query builder "write" values.
2784
	 *
2785
	 * Called by the insert() update() insert_batch() update_batch() and delete() functions
2786
	 *
2787
	 * @return	void
2788
	 */
2789
	protected function _reset_write()
2790
	{
2791
		$this->_reset_run(array(
2792
			'qb_set'	=> array(),
2793
			'qb_set_ub'	=> array(),
2794
			'qb_from'	=> array(),
2795
			'qb_join'	=> array(),
2796
			'qb_where'	=> array(),
2797
			'qb_orderby'	=> array(),
2798
			'qb_keys'	=> array(),
2799
			'qb_limit'	=> FALSE
2800
		));
2801
	}
2802
2803
}
2804