1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Database
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * Query Building Class.
14 *
15 * @since 11.1
16 *
17 * @method string q() q($text, $escape = true) Alias for quote method
18 * @method string qn() qn($name, $as = null) Alias for quoteName method
19 * @method string e() e($text, $extra = false) Alias for escape method
20 * @property-read JDatabaseQueryElement $type
21 * @property-read JDatabaseQueryElement $select
22 * @property-read JDatabaseQueryElement $group
23 * @property-read JDatabaseQueryElement $having
24 */
25 abstract class JDatabaseQuery
26 {
27 /**
28 * @var JDatabaseDriver The database driver.
29 * @since 11.1
30 */
31 protected $db = null;
32
33 /**
34 * @var string The SQL query (if a direct query string was provided).
35 * @since 12.1
36 */
37 protected $sql = null;
38
39 /**
40 * @var string The query type.
41 * @since 11.1
42 */
43 protected $type = '';
44
45 /**
46 * @var JDatabaseQueryElement The query element for a generic query (type = null).
47 * @since 11.1
48 */
49 protected $element = null;
50
51 /**
52 * @var JDatabaseQueryElement The select element.
53 * @since 11.1
54 */
55 protected $select = null;
56
57 /**
58 * @var JDatabaseQueryElement The delete element.
59 * @since 11.1
60 */
61 protected $delete = null;
62
63 /**
64 * @var JDatabaseQueryElement The update element.
65 * @since 11.1
66 */
67 protected $update = null;
68
69 /**
70 * @var JDatabaseQueryElement The insert element.
71 * @since 11.1
72 */
73 protected $insert = null;
74
75 /**
76 * @var JDatabaseQueryElement The from element.
77 * @since 11.1
78 */
79 protected $from = null;
80
81 /**
82 * @var JDatabaseQueryElement The join element.
83 * @since 11.1
84 */
85 protected $join = null;
86
87 /**
88 * @var JDatabaseQueryElement The set element.
89 * @since 11.1
90 */
91 protected $set = null;
92
93 /**
94 * @var JDatabaseQueryElement The where element.
95 * @since 11.1
96 */
97 protected $where = null;
98
99 /**
100 * @var JDatabaseQueryElement The group by element.
101 * @since 11.1
102 */
103 protected $group = null;
104
105 /**
106 * @var JDatabaseQueryElement The having element.
107 * @since 11.1
108 */
109 protected $having = null;
110
111 /**
112 * @var JDatabaseQueryElement The column list for an INSERT statement.
113 * @since 11.1
114 */
115 protected $columns = null;
116
117 /**
118 * @var JDatabaseQueryElement The values list for an INSERT statement.
119 * @since 11.1
120 */
121 protected $values = null;
122
123 /**
124 * @var JDatabaseQueryElement The order element.
125 * @since 11.1
126 */
127 protected $order = null;
128
129 /**
130 * @var object The auto increment insert field element.
131 * @since 11.1
132 */
133 protected $autoIncrementField = null;
134
135 /**
136 * @var JDatabaseQueryElement The call element.
137 * @since 12.1
138 */
139 protected $call = null;
140
141 /**
142 * @var JDatabaseQueryElement The exec element.
143 * @since 12.1
144 */
145 protected $exec = null;
146
147 /**
148 * @var JDatabaseQueryElement The union element.
149 * @since 12.1
150 */
151 protected $union = null;
152
153 /**
154 * @var JDatabaseQueryElement The unionAll element.
155 * @since 13.1
156 */
157 protected $unionAll = null;
158
159 /**
160 * @var array Details of window function.
161 * @since 3.7.0
162 */
163 protected $selectRowNumber = null;
164
165 /**
166 * Magic method to provide method alias support for quote() and quoteName().
167 *
168 * @param string $method The called method.
169 * @param array $args The array of arguments passed to the method.
170 *
171 * @return string The aliased method's return value or null.
172 *
173 * @since 11.1
174 */
175 public function __call($method, $args)
176 {
177 if (empty($args))
178 {
179 return;
180 }
181
182 switch ($method)
183 {
184 case 'q':
185 return $this->quote($args[0], isset($args[1]) ? $args[1] : true);
186 break;
187
188 case 'qn':
189 return $this->quoteName($args[0], isset($args[1]) ? $args[1] : null);
190 break;
191
192 case 'e':
193 return $this->escape($args[0], isset($args[1]) ? $args[1] : false);
194 break;
195 }
196 }
197
198 /**
199 * Class constructor.
200 *
201 * @param JDatabaseDriver $db The database driver.
202 *
203 * @since 11.1
204 */
205 public function __construct(JDatabaseDriver $db = null)
206 {
207 $this->db = $db;
208 }
209
210 /**
211 * Magic function to convert the query to a string.
212 *
213 * @return string The completed query.
214 *
215 * @since 11.1
216 */
217 public function __toString()
218 {
219 $query = '';
220
221 if ($this->sql)
222 {
223 return $this->sql;
224 }
225
226 switch ($this->type)
227 {
228 case 'element':
229 $query .= (string) $this->element;
230 break;
231
232 case 'select':
233 $query .= (string) $this->select;
234 $query .= (string) $this->from;
235
236 if ($this->join)
237 {
238 // Special case for joins
239 foreach ($this->join as $join)
240 {
241 $query .= (string) $join;
242 }
243 }
244
245 if ($this->where)
246 {
247 $query .= (string) $this->where;
248 }
249
250 if ($this->selectRowNumber === null)
251 {
252 if ($this->group)
253 {
254 $query .= (string) $this->group;
255 }
256
257 if ($this->having)
258 {
259 $query .= (string) $this->having;
260 }
261
262 if ($this->union)
263 {
264 $query .= (string) $this->union;
265 }
266
267 if ($this->unionAll)
268 {
269 $query .= (string) $this->unionAll;
270 }
271 }
272
273 if ($this->order)
274 {
275 $query .= (string) $this->order;
276 }
277
278 break;
279
280 case 'delete':
281 $query .= (string) $this->delete;
282 $query .= (string) $this->from;
283
284 if ($this->join)
285 {
286 // Special case for joins
287 foreach ($this->join as $join)
288 {
289 $query .= (string) $join;
290 }
291 }
292
293 if ($this->where)
294 {
295 $query .= (string) $this->where;
296 }
297
298 if ($this->order)
299 {
300 $query .= (string) $this->order;
301 }
302
303 break;
304
305 case 'update':
306 $query .= (string) $this->update;
307
308 if ($this->join)
309 {
310 // Special case for joins
311 foreach ($this->join as $join)
312 {
313 $query .= (string) $join;
314 }
315 }
316
317 $query .= (string) $this->set;
318
319 if ($this->where)
320 {
321 $query .= (string) $this->where;
322 }
323
324 if ($this->order)
325 {
326 $query .= (string) $this->order;
327 }
328
329 break;
330
331 case 'insert':
332 $query .= (string) $this->insert;
333
334 // Set method
335 if ($this->set)
336 {
337 $query .= (string) $this->set;
338 }
339 // Columns-Values method
340 elseif ($this->values)
341 {
342 if ($this->columns)
343 {
344 $query .= (string) $this->columns;
345 }
346
347 $elements = $this->values->getElements();
348
349 if (!($elements[0] instanceof $this))
350 {
351 $query .= ' VALUES ';
352 }
353
354 $query .= (string) $this->values;
355 }
356
357 break;
358
359 case 'call':
360 $query .= (string) $this->call;
361 break;
362
363 case 'exec':
364 $query .= (string) $this->exec;
365 break;
366 }
367
368 if ($this instanceof JDatabaseQueryLimitable)
369 {
370 $query = $this->processLimit($query, $this->limit, $this->offset);
371 }
372
373 return $query;
374 }
375
376 /**
377 * Magic function to get protected variable value
378 *
379 * @param string $name The name of the variable.
380 *
381 * @return mixed
382 *
383 * @since 11.1
384 */
385 public function __get($name)
386 {
387 return isset($this->$name) ? $this->$name : null;
388 }
389
390 /**
391 * Add a single column, or array of columns to the CALL clause of the query.
392 *
393 * Note that you must not mix insert, update, delete and select method calls when building a query.
394 * The call method can, however, be called multiple times in the same query.
395 *
396 * Usage:
397 * $query->call('a.*')->call('b.id');
398 * $query->call(array('a.*', 'b.id'));
399 *
400 * @param mixed $columns A string or an array of field names.
401 *
402 * @return JDatabaseQuery Returns this object to allow chaining.
403 *
404 * @since 12.1
405 */
406 public function call($columns)
407 {
408 $this->type = 'call';
409
410 if (is_null($this->call))
411 {
412 $this->call = new JDatabaseQueryElement('CALL', $columns);
413 }
414 else
415 {
416 $this->call->append($columns);
417 }
418
419 return $this;
420 }
421
422 /**
423 * Casts a value to a char.
424 *
425 * Ensure that the value is properly quoted before passing to the method.
426 *
427 * Usage:
428 * $query->select($query->castAsChar('a'));
429 *
430 * @param string $value The value to cast as a char.
431 *
432 * @return string Returns the cast value.
433 *
434 * @since 11.1
435 */
436 public function castAsChar($value)
437 {
438 return $value;
439 }
440
441 /**
442 * Gets the number of characters in a string.
443 *
444 * Note, use 'length' to find the number of bytes in a string.
445 *
446 * Usage:
447 * $query->select($query->charLength('a'));
448 *
449 * @param string $field A value.
450 * @param string $operator Comparison operator between charLength integer value and $condition
451 * @param string $condition Integer value to compare charLength with.
452 *
453 * @return string The required char length call.
454 *
455 * @since 11.1
456 */
457 public function charLength($field, $operator = null, $condition = null)
458 {
459 return 'CHAR_LENGTH(' . $field . ')' . (isset($operator) && isset($condition) ? ' ' . $operator . ' ' . $condition : '');
460 }
461
462 /**
463 * Clear data from the query or a specific clause of the query.
464 *
465 * @param string $clause Optionally, the name of the clause to clear, or nothing to clear the whole query.
466 *
467 * @return JDatabaseQuery Returns this object to allow chaining.
468 *
469 * @since 11.1
470 */
471 public function clear($clause = null)
472 {
473 $this->sql = null;
474
475 switch ($clause)
476 {
477 case 'select':
478 $this->select = null;
479 $this->type = null;
480 $this->selectRowNumber = null;
481 break;
482
483 case 'delete':
484 $this->delete = null;
485 $this->type = null;
486 break;
487
488 case 'update':
489 $this->update = null;
490 $this->type = null;
491 break;
492
493 case 'insert':
494 $this->insert = null;
495 $this->type = null;
496 $this->autoIncrementField = null;
497 break;
498
499 case 'from':
500 $this->from = null;
501 break;
502
503 case 'join':
504 $this->join = null;
505 break;
506
507 case 'set':
508 $this->set = null;
509 break;
510
511 case 'where':
512 $this->where = null;
513 break;
514
515 case 'group':
516 $this->group = null;
517 break;
518
519 case 'having':
520 $this->having = null;
521 break;
522
523 case 'order':
524 $this->order = null;
525 break;
526
527 case 'columns':
528 $this->columns = null;
529 break;
530
531 case 'values':
532 $this->values = null;
533 break;
534
535 case 'exec':
536 $this->exec = null;
537 $this->type = null;
538 break;
539
540 case 'call':
541 $this->call = null;
542 $this->type = null;
543 break;
544
545 case 'limit':
546 $this->offset = 0;
547 $this->limit = 0;
548 break;
549
550 case 'offset':
551 $this->offset = 0;
552 break;
553
554 case 'union':
555 $this->union = null;
556 break;
557
558 case 'unionAll':
559 $this->unionAll = null;
560 break;
561
562 default:
563 $this->type = null;
564 $this->select = null;
565 $this->selectRowNumber = null;
566 $this->delete = null;
567 $this->update = null;
568 $this->insert = null;
569 $this->from = null;
570 $this->join = null;
571 $this->set = null;
572 $this->where = null;
573 $this->group = null;
574 $this->having = null;
575 $this->order = null;
576 $this->columns = null;
577 $this->values = null;
578 $this->autoIncrementField = null;
579 $this->exec = null;
580 $this->call = null;
581 $this->union = null;
582 $this->unionAll = null;
583 $this->offset = 0;
584 $this->limit = 0;
585 break;
586 }
587
588 return $this;
589 }
590
591 /**
592 * Adds a column, or array of column names that would be used for an INSERT INTO statement.
593 *
594 * @param mixed $columns A column name, or array of column names.
595 *
596 * @return JDatabaseQuery Returns this object to allow chaining.
597 *
598 * @since 11.1
599 */
600 public function columns($columns)
601 {
602 if (is_null($this->columns))
603 {
604 $this->columns = new JDatabaseQueryElement('()', $columns);
605 }
606 else
607 {
608 $this->columns->append($columns);
609 }
610
611 return $this;
612 }
613
614 /**
615 * Concatenates an array of column names or values.
616 *
617 * Usage:
618 * $query->select($query->concatenate(array('a', 'b')));
619 *
620 * @param array $values An array of values to concatenate.
621 * @param string $separator As separator to place between each value.
622 *
623 * @return string The concatenated values.
624 *
625 * @since 11.1
626 */
627 public function concatenate($values, $separator = null)
628 {
629 if ($separator)
630 {
631 return 'CONCATENATE(' . implode(' || ' . $this->quote($separator) . ' || ', $values) . ')';
632 }
633 else
634 {
635 return 'CONCATENATE(' . implode(' || ', $values) . ')';
636 }
637 }
638
639 /**
640 * Gets the current date and time.
641 *
642 * Usage:
643 * $query->where('published_up < '.$query->currentTimestamp());
644 *
645 * @return string
646 *
647 * @since 11.1
648 */
649 public function currentTimestamp()
650 {
651 return 'CURRENT_TIMESTAMP()';
652 }
653
654 /**
655 * Returns a PHP date() function compliant date format for the database driver.
656 *
657 * This method is provided for use where the query object is passed to a function for modification.
658 * If you have direct access to the database object, it is recommended you use the getDateFormat method directly.
659 *
660 * @return string The format string.
661 *
662 * @since 11.1
663 */
664 public function dateFormat()
665 {
666 if (!($this->db instanceof JDatabaseDriver))
667 {
668 throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
669 }
670
671 return $this->db->getDateFormat();
672 }
673
674 /**
675 * Creates a formatted dump of the query for debugging purposes.
676 *
677 * Usage:
678 * echo $query->dump();
679 *
680 * @return string
681 *
682 * @since 11.3
683 */
684 public function dump()
685 {
686 return '<pre class="jdatabasequery">' . str_replace('#__', $this->db->getPrefix(), $this) . '</pre>';
687 }
688
689 /**
690 * Add a table name to the DELETE clause of the query.
691 *
692 * Note that you must not mix insert, update, delete and select method calls when building a query.
693 *
694 * Usage:
695 * $query->delete('#__a')->where('id = 1');
696 *
697 * @param string $table The name of the table to delete from.
698 *
699 * @return JDatabaseQuery Returns this object to allow chaining.
700 *
701 * @since 11.1
702 */
703 public function delete($table = null)
704 {
705 $this->type = 'delete';
706 $this->delete = new JDatabaseQueryElement('DELETE', null);
707
708 if (!empty($table))
709 {
710 $this->from($table);
711 }
712
713 return $this;
714 }
715
716 /**
717 * Method to escape a string for usage in an SQL statement.
718 *
719 * This method is provided for use where the query object is passed to a function for modification.
720 * If you have direct access to the database object, it is recommended you use the escape method directly.
721 *
722 * Note that 'e' is an alias for this method as it is in JDatabaseDriver.
723 *
724 * @param string $text The string to be escaped.
725 * @param boolean $extra Optional parameter to provide extra escaping.
726 *
727 * @return string The escaped string.
728 *
729 * @since 11.1
730 * @throws RuntimeException if the internal db property is not a valid object.
731 */
732 public function escape($text, $extra = false)
733 {
734 if (!($this->db instanceof JDatabaseDriver))
735 {
736 throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
737 }
738
739 return $this->db->escape($text, $extra);
740 }
741
742 /**
743 * Add a single column, or array of columns to the EXEC clause of the query.
744 *
745 * Note that you must not mix insert, update, delete and select method calls when building a query.
746 * The exec method can, however, be called multiple times in the same query.
747 *
748 * Usage:
749 * $query->exec('a.*')->exec('b.id');
750 * $query->exec(array('a.*', 'b.id'));
751 *
752 * @param mixed $columns A string or an array of field names.
753 *
754 * @return JDatabaseQuery Returns this object to allow chaining.
755 *
756 * @since 12.1
757 */
758 public function exec($columns)
759 {
760 $this->type = 'exec';
761
762 if (is_null($this->exec))
763 {
764 $this->exec = new JDatabaseQueryElement('EXEC', $columns);
765 }
766 else
767 {
768 $this->exec->append($columns);
769 }
770
771 return $this;
772 }
773
774 /**
775 * Add a table to the FROM clause of the query.
776 *
777 * Note that while an array of tables can be provided, it is recommended you use explicit joins.
778 *
779 * Usage:
780 * $query->select('*')->from('#__a');
781 *
782 * @param mixed $tables A string or array of table names.
783 * This can be a JDatabaseQuery object (or a child of it) when used
784 * as a subquery in FROM clause along with a value for $subQueryAlias.
785 * @param string $subQueryAlias Alias used when $tables is a JDatabaseQuery.
786 *
787 * @return JDatabaseQuery Returns this object to allow chaining.
788 *
789 * @throws RuntimeException
790 *
791 * @since 11.1
792 */
793 public function from($tables, $subQueryAlias = null)
794 {
795 if (is_null($this->from))
796 {
797 if ($tables instanceof $this)
798 {
799 if (is_null($subQueryAlias))
800 {
801 throw new RuntimeException('JLIB_DATABASE_ERROR_NULL_SUBQUERY_ALIAS');
802 }
803
804 $tables = '( ' . (string) $tables . ' ) AS ' . $this->quoteName($subQueryAlias);
805 }
806
807 $this->from = new JDatabaseQueryElement('FROM', $tables);
808 }
809 else
810 {
811 $this->from->append($tables);
812 }
813
814 return $this;
815 }
816
817 /**
818 * Used to get a string to extract year from date column.
819 *
820 * Usage:
821 * $query->select($query->year($query->quoteName('dateColumn')));
822 *
823 * @param string $date Date column containing year to be extracted.
824 *
825 * @return string Returns string to extract year from a date.
826 *
827 * @since 12.1
828 */
829 public function year($date)
830 {
831 return 'YEAR(' . $date . ')';
832 }
833
834 /**
835 * Used to get a string to extract month from date column.
836 *
837 * Usage:
838 * $query->select($query->month($query->quoteName('dateColumn')));
839 *
840 * @param string $date Date column containing month to be extracted.
841 *
842 * @return string Returns string to extract month from a date.
843 *
844 * @since 12.1
845 */
846 public function month($date)
847 {
848 return 'MONTH(' . $date . ')';
849 }
850
851 /**
852 * Used to get a string to extract day from date column.
853 *
854 * Usage:
855 * $query->select($query->day($query->quoteName('dateColumn')));
856 *
857 * @param string $date Date column containing day to be extracted.
858 *
859 * @return string Returns string to extract day from a date.
860 *
861 * @since 12.1
862 */
863 public function day($date)
864 {
865 return 'DAY(' . $date . ')';
866 }
867
868 /**
869 * Used to get a string to extract hour from date column.
870 *
871 * Usage:
872 * $query->select($query->hour($query->quoteName('dateColumn')));
873 *
874 * @param string $date Date column containing hour to be extracted.
875 *
876 * @return string Returns string to extract hour from a date.
877 *
878 * @since 12.1
879 */
880 public function hour($date)
881 {
882 return 'HOUR(' . $date . ')';
883 }
884
885 /**
886 * Used to get a string to extract minute from date column.
887 *
888 * Usage:
889 * $query->select($query->minute($query->quoteName('dateColumn')));
890 *
891 * @param string $date Date column containing minute to be extracted.
892 *
893 * @return string Returns string to extract minute from a date.
894 *
895 * @since 12.1
896 */
897 public function minute($date)
898 {
899 return 'MINUTE(' . $date . ')';
900 }
901
902 /**
903 * Used to get a string to extract seconds from date column.
904 *
905 * Usage:
906 * $query->select($query->second($query->quoteName('dateColumn')));
907 *
908 * @param string $date Date column containing second to be extracted.
909 *
910 * @return string Returns string to extract second from a date.
911 *
912 * @since 12.1
913 */
914 public function second($date)
915 {
916 return 'SECOND(' . $date . ')';
917 }
918
919 /**
920 * Add a grouping column to the GROUP clause of the query.
921 *
922 * Usage:
923 * $query->group('id');
924 *
925 * @param mixed $columns A string or array of ordering columns.
926 *
927 * @return JDatabaseQuery Returns this object to allow chaining.
928 *
929 * @since 11.1
930 */
931 public function group($columns)
932 {
933 if (is_null($this->group))
934 {
935 $this->group = new JDatabaseQueryElement('GROUP BY', $columns);
936 }
937 else
938 {
939 $this->group->append($columns);
940 }
941
942 return $this;
943 }
944
945 /**
946 * A conditions to the HAVING clause of the query.
947 *
948 * Usage:
949 * $query->group('id')->having('COUNT(id) > 5');
950 *
951 * @param mixed $conditions A string or array of columns.
952 * @param string $glue The glue by which to join the conditions. Defaults to AND.
953 *
954 * @return JDatabaseQuery Returns this object to allow chaining.
955 *
956 * @since 11.1
957 */
958 public function having($conditions, $glue = 'AND')
959 {
960 if (is_null($this->having))
961 {
962 $glue = strtoupper($glue);
963 $this->having = new JDatabaseQueryElement('HAVING', $conditions, " $glue ");
964 }
965 else
966 {
967 $this->having->append($conditions);
968 }
969
970 return $this;
971 }
972
973 /**
974 * Add an INNER JOIN clause to the query.
975 *
976 * Usage:
977 * $query->innerJoin('b ON b.id = a.id')->innerJoin('c ON c.id = b.id');
978 *
979 * @param string $condition The join condition.
980 *
981 * @return JDatabaseQuery Returns this object to allow chaining.
982 *
983 * @since 11.1
984 */
985 public function innerJoin($condition)
986 {
987 $this->join('INNER', $condition);
988
989 return $this;
990 }
991
992 /**
993 * Add a table name to the INSERT clause of the query.
994 *
995 * Note that you must not mix insert, update, delete and select method calls when building a query.
996 *
997 * Usage:
998 * $query->insert('#__a')->set('id = 1');
999 * $query->insert('#__a')->columns('id, title')->values('1,2')->values('3,4');
1000 * $query->insert('#__a')->columns('id, title')->values(array('1,2', '3,4'));
1001 *
1002 * @param mixed $table The name of the table to insert data into.
1003 * @param boolean $incrementField The name of the field to auto increment.
1004 *
1005 * @return JDatabaseQuery Returns this object to allow chaining.
1006 *
1007 * @since 11.1
1008 */
1009 public function insert($table, $incrementField=false)
1010 {
1011 $this->type = 'insert';
1012 $this->insert = new JDatabaseQueryElement('INSERT INTO', $table);
1013 $this->autoIncrementField = $incrementField;
1014
1015 return $this;
1016 }
1017
1018 /**
1019 * Add a JOIN clause to the query.
1020 *
1021 * Usage:
1022 * $query->join('INNER', 'b ON b.id = a.id);
1023 *
1024 * @param string $type The type of join. This string is prepended to the JOIN keyword.
1025 * @param string $conditions A string or array of conditions.
1026 *
1027 * @return JDatabaseQuery Returns this object to allow chaining.
1028 *
1029 * @since 11.1
1030 */
1031 public function join($type, $conditions)
1032 {
1033 if (is_null($this->join))
1034 {
1035 $this->join = array();
1036 }
1037
1038 $this->join[] = new JDatabaseQueryElement(strtoupper($type) . ' JOIN', $conditions);
1039
1040 return $this;
1041 }
1042
1043 /**
1044 * Add a LEFT JOIN clause to the query.
1045 *
1046 * Usage:
1047 * $query->leftJoin('b ON b.id = a.id')->leftJoin('c ON c.id = b.id');
1048 *
1049 * @param string $condition The join condition.
1050 *
1051 * @return JDatabaseQuery Returns this object to allow chaining.
1052 *
1053 * @since 11.1
1054 */
1055 public function leftJoin($condition)
1056 {
1057 $this->join('LEFT', $condition);
1058
1059 return $this;
1060 }
1061
1062 /**
1063 * Get the length of a string in bytes.
1064 *
1065 * Note, use 'charLength' to find the number of characters in a string.
1066 *
1067 * Usage:
1068 * query->where($query->length('a').' > 3');
1069 *
1070 * @param string $value The string to measure.
1071 *
1072 * @return int
1073 *
1074 * @since 11.1
1075 */
1076 public function length($value)
1077 {
1078 return 'LENGTH(' . $value . ')';
1079 }
1080
1081 /**
1082 * Get the null or zero representation of a timestamp for the database driver.
1083 *
1084 * This method is provided for use where the query object is passed to a function for modification.
1085 * If you have direct access to the database object, it is recommended you use the nullDate method directly.
1086 *
1087 * Usage:
1088 * $query->where('modified_date <> '.$query->nullDate());
1089 *
1090 * @param boolean $quoted Optionally wraps the null date in database quotes (true by default).
1091 *
1092 * @return string Null or zero representation of a timestamp.
1093 *
1094 * @since 11.1
1095 */
1096 public function nullDate($quoted = true)
1097 {
1098 if (!($this->db instanceof JDatabaseDriver))
1099 {
1100 throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
1101 }
1102
1103 $result = $this->db->getNullDate($quoted);
1104
1105 if ($quoted)
1106 {
1107 return $this->db->quote($result);
1108 }
1109
1110 return $result;
1111 }
1112
1113 /**
1114 * Add an ordering column to the ORDER clause of the query.
1115 *
1116 * Usage:
1117 * $query->order('foo')->order('bar');
1118 * $query->order(array('foo','bar'));
1119 *
1120 * @param mixed $columns A string or array of ordering columns.
1121 *
1122 * @return JDatabaseQuery Returns this object to allow chaining.
1123 *
1124 * @since 11.1
1125 */
1126 public function order($columns)
1127 {
1128 if (is_null($this->order))
1129 {
1130 $this->order = new JDatabaseQueryElement('ORDER BY', $columns);
1131 }
1132 else
1133 {
1134 $this->order->append($columns);
1135 }
1136
1137 return $this;
1138 }
1139
1140 /**
1141 * Add an OUTER JOIN clause to the query.
1142 *
1143 * Usage:
1144 * $query->outerJoin('b ON b.id = a.id')->outerJoin('c ON c.id = b.id');
1145 *
1146 * @param string $condition The join condition.
1147 *
1148 * @return JDatabaseQuery Returns this object to allow chaining.
1149 *
1150 * @since 11.1
1151 */
1152 public function outerJoin($condition)
1153 {
1154 $this->join('OUTER', $condition);
1155
1156 return $this;
1157 }
1158
1159 /**
1160 * Method to quote and optionally escape a string to database requirements for insertion into the database.
1161 *
1162 * This method is provided for use where the query object is passed to a function for modification.
1163 * If you have direct access to the database object, it is recommended you use the quote method directly.
1164 *
1165 * Note that 'q' is an alias for this method as it is in JDatabaseDriver.
1166 *
1167 * Usage:
1168 * $query->quote('fulltext');
1169 * $query->q('fulltext');
1170 * $query->q(array('option', 'fulltext'));
1171 *
1172 * @param mixed $text A string or an array of strings to quote.
1173 * @param boolean $escape True to escape the string, false to leave it unchanged.
1174 *
1175 * @return string The quoted input string.
1176 *
1177 * @since 11.1
1178 * @throws RuntimeException if the internal db property is not a valid object.
1179 */
1180 public function quote($text, $escape = true)
1181 {
1182 if (!($this->db instanceof JDatabaseDriver))
1183 {
1184 throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
1185 }
1186
1187 return $this->db->quote($text, $escape);
1188 }
1189
1190 /**
1191 * Wrap an SQL statement identifier name such as column, table or database names in quotes to prevent injection
1192 * risks and reserved word conflicts.
1193 *
1194 * This method is provided for use where the query object is passed to a function for modification.
1195 * If you have direct access to the database object, it is recommended you use the quoteName method directly.
1196 *
1197 * Note that 'qn' is an alias for this method as it is in JDatabaseDriver.
1198 *
1199 * Usage:
1200 * $query->quoteName('#__a');
1201 * $query->qn('#__a');
1202 *
1203 * @param mixed $name The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
1204 * Each type supports dot-notation name.
1205 * @param mixed $as The AS query part associated to $name. It can be string or array, in latter case it has to be
1206 * same length of $name; if is null there will not be any AS part for string or array element.
1207 *
1208 * @return mixed The quote wrapped name, same type of $name.
1209 *
1210 * @since 11.1
1211 * @throws RuntimeException if the internal db property is not a valid object.
1212 */
1213 public function quoteName($name, $as = null)
1214 {
1215 if (!($this->db instanceof JDatabaseDriver))
1216 {
1217 throw new RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
1218 }
1219
1220 return $this->db->quoteName($name, $as);
1221 }
1222
1223 /**
1224 * Add a RIGHT JOIN clause to the query.
1225 *
1226 * Usage:
1227 * $query->rightJoin('b ON b.id = a.id')->rightJoin('c ON c.id = b.id');
1228 *
1229 * @param string $condition The join condition.
1230 *
1231 * @return JDatabaseQuery Returns this object to allow chaining.
1232 *
1233 * @since 11.1
1234 */
1235 public function rightJoin($condition)
1236 {
1237 $this->join('RIGHT', $condition);
1238
1239 return $this;
1240 }
1241
1242 /**
1243 * Add a single column, or array of columns to the SELECT clause of the query.
1244 *
1245 * Note that you must not mix insert, update, delete and select method calls when building a query.
1246 * The select method can, however, be called multiple times in the same query.
1247 *
1248 * Usage:
1249 * $query->select('a.*')->select('b.id');
1250 * $query->select(array('a.*', 'b.id'));
1251 *
1252 * @param mixed $columns A string or an array of field names.
1253 *
1254 * @return JDatabaseQuery Returns this object to allow chaining.
1255 *
1256 * @since 11.1
1257 */
1258 public function select($columns)
1259 {
1260 $this->type = 'select';
1261
1262 if (is_null($this->select))
1263 {
1264 $this->select = new JDatabaseQueryElement('SELECT', $columns);
1265 }
1266 else
1267 {
1268 $this->select->append($columns);
1269 }
1270
1271 return $this;
1272 }
1273
1274 /**
1275 * Add a single condition string, or an array of strings to the SET clause of the query.
1276 *
1277 * Usage:
1278 * $query->set('a = 1')->set('b = 2');
1279 * $query->set(array('a = 1', 'b = 2');
1280 *
1281 * @param mixed $conditions A string or array of string conditions.
1282 * @param string $glue The glue by which to join the condition strings. Defaults to ,.
1283 * Note that the glue is set on first use and cannot be changed.
1284 *
1285 * @return JDatabaseQuery Returns this object to allow chaining.
1286 *
1287 * @since 11.1
1288 */
1289 public function set($conditions, $glue = ',')
1290 {
1291 if (is_null($this->set))
1292 {
1293 $glue = strtoupper($glue);
1294 $this->set = new JDatabaseQueryElement('SET', $conditions, "\n\t$glue ");
1295 }
1296 else
1297 {
1298 $this->set->append($conditions);
1299 }
1300
1301 return $this;
1302 }
1303
1304 /**
1305 * Allows a direct query to be provided to the database
1306 * driver's setQuery() method, but still allow queries
1307 * to have bounded variables.
1308 *
1309 * Usage:
1310 * $query->setQuery('select * from #__users');
1311 *
1312 * @param mixed $sql An SQL Query
1313 *
1314 * @return JDatabaseQuery Returns this object to allow chaining.
1315 *
1316 * @since 12.1
1317 */
1318 public function setQuery($sql)
1319 {
1320 $this->sql = $sql;
1321
1322 return $this;
1323 }
1324
1325 /**
1326 * Add a table name to the UPDATE clause of the query.
1327 *
1328 * Note that you must not mix insert, update, delete and select method calls when building a query.
1329 *
1330 * Usage:
1331 * $query->update('#__foo')->set(...);
1332 *
1333 * @param string $table A table to update.
1334 *
1335 * @return JDatabaseQuery Returns this object to allow chaining.
1336 *
1337 * @since 11.1
1338 */
1339 public function update($table)
1340 {
1341 $this->type = 'update';
1342 $this->update = new JDatabaseQueryElement('UPDATE', $table);
1343
1344 return $this;
1345 }
1346
1347 /**
1348 * Adds a tuple, or array of tuples that would be used as values for an INSERT INTO statement.
1349 *
1350 * Usage:
1351 * $query->values('1,2,3')->values('4,5,6');
1352 * $query->values(array('1,2,3', '4,5,6'));
1353 *
1354 * @param string $values A single tuple, or array of tuples.
1355 *
1356 * @return JDatabaseQuery Returns this object to allow chaining.
1357 *
1358 * @since 11.1
1359 */
1360 public function values($values)
1361 {
1362 if (is_null($this->values))
1363 {
1364 $this->values = new JDatabaseQueryElement('()', $values, '),(');
1365 }
1366 else
1367 {
1368 $this->values->append($values);
1369 }
1370
1371 return $this;
1372 }
1373
1374 /**
1375 * Add a single condition, or an array of conditions to the WHERE clause of the query.
1376 *
1377 * Usage:
1378 * $query->where('a = 1')->where('b = 2');
1379 * $query->where(array('a = 1', 'b = 2'));
1380 *
1381 * @param mixed $conditions A string or array of where conditions.
1382 * @param string $glue The glue by which to join the conditions. Defaults to AND.
1383 * Note that the glue is set on first use and cannot be changed.
1384 *
1385 * @return JDatabaseQuery Returns this object to allow chaining.
1386 *
1387 * @since 11.1
1388 */
1389 public function where($conditions, $glue = 'AND')
1390 {
1391 if (is_null($this->where))
1392 {
1393 $glue = strtoupper($glue);
1394 $this->where = new JDatabaseQueryElement('WHERE', $conditions, " $glue ");
1395 }
1396 else
1397 {
1398 $this->where->append($conditions);
1399 }
1400
1401 return $this;
1402 }
1403
1404 /**
1405 * Extend the WHERE clause with a single condition or an array of conditions, with a potentially
1406 * different logical operator from the one in the current WHERE clause.
1407 *
1408 * Usage:
1409 * $query->where(array('a = 1', 'b = 2'))->extendWhere('XOR', array('c = 3', 'd = 4'));
1410 * will produce: WHERE ((a = 1 AND b = 2) XOR (c = 3 AND d = 4)
1411 *
1412 * @param string $outerGlue The glue by which to join the conditions to the current WHERE conditions.
1413 * @param mixed $conditions A string or array of WHERE conditions.
1414 * @param string $innerGlue The glue by which to join the conditions. Defaults to AND.
1415 *
1416 * @return JDatabaseQuery Returns this object to allow chaining.
1417 *
1418 * @since 3.6
1419 */
1420 public function extendWhere($outerGlue, $conditions, $innerGlue = 'AND')
1421 {
1422 // Replace the current WHERE with a new one which has the old one as an unnamed child.
1423 $this->where = new JDatabaseQueryElement('WHERE', $this->where->setName('()'), " $outerGlue ");
1424
1425 // Append the new conditions as a new unnamed child.
1426 $this->where->append(new JDatabaseQueryElement('()', $conditions, " $innerGlue "));
1427
1428 return $this;
1429 }
1430
1431 /**
1432 * Extend the WHERE clause with an OR and a single condition or an array of conditions.
1433 *
1434 * Usage:
1435 * $query->where(array('a = 1', 'b = 2'))->orWhere(array('c = 3', 'd = 4'));
1436 * will produce: WHERE ((a = 1 AND b = 2) OR (c = 3 AND d = 4)
1437 *
1438 * @param mixed $conditions A string or array of WHERE conditions.
1439 * @param string $glue The glue by which to join the conditions. Defaults to AND.
1440 *
1441 * @return JDatabaseQuery Returns this object to allow chaining.
1442 *
1443 * @since 3.6
1444 */
1445 public function orWhere($conditions, $glue = 'AND')
1446 {
1447 return $this->extendWhere('OR', $conditions, $glue);
1448 }
1449
1450 /**
1451 * Extend the WHERE clause with an AND and a single condition or an array of conditions.
1452 *
1453 * Usage:
1454 * $query->where(array('a = 1', 'b = 2'))->andWhere(array('c = 3', 'd = 4'));
1455 * will produce: WHERE ((a = 1 AND b = 2) AND (c = 3 OR d = 4)
1456 *
1457 * @param mixed $conditions A string or array of WHERE conditions.
1458 * @param string $glue The glue by which to join the conditions. Defaults to OR.
1459 *
1460 * @return JDatabaseQuery Returns this object to allow chaining.
1461 *
1462 * @since 3.6
1463 */
1464 public function andWhere($conditions, $glue = 'OR')
1465 {
1466 return $this->extendWhere('AND', $conditions, $glue);
1467 }
1468
1469 /**
1470 * Method to provide deep copy support to nested objects and
1471 * arrays when cloning.
1472 *
1473 * @return void
1474 *
1475 * @since 11.3
1476 */
1477 public function __clone()
1478 {
1479 foreach ($this as $k => $v)
1480 {
1481 if ($k === 'db')
1482 {
1483 continue;
1484 }
1485
1486 if (is_object($v) || is_array($v))
1487 {
1488 $this->{$k} = unserialize(serialize($v));
1489 }
1490 }
1491 }
1492
1493 /**
1494 * Add a query to UNION with the current query.
1495 * Multiple unions each require separate statements and create an array of unions.
1496 *
1497 * Usage (the $query base query MUST be a select query):
1498 * $query->union('SELECT name FROM #__foo')
1499 * $query->union('SELECT name FROM #__foo', true)
1500 * $query->union(array('SELECT name FROM #__foo','SELECT name FROM #__bar'))
1501 * $query->union($query2)->union($query3)
1502 * $query->union(array($query2, $query3))
1503 *
1504 * @param mixed $query The JDatabaseQuery object or string to union.
1505 * @param boolean $distinct True to only return distinct rows from the union.
1506 * @param string $glue The glue by which to join the conditions.
1507 *
1508 * @return JDatabaseQuery Returns this object to allow chaining.
1509 *
1510 * @link http://dev.mysql.com/doc/refman/5.0/en/union.html
1511 *
1512 * @since 12.1
1513 */
1514 public function union($query, $distinct = false, $glue = '')
1515 {
1516 // Set up the DISTINCT flag, the name with parentheses, and the glue.
1517 if ($distinct)
1518 {
1519 $name = 'UNION DISTINCT ()';
1520 $glue = ')' . PHP_EOL . 'UNION DISTINCT (';
1521 }
1522 else
1523 {
1524 $glue = ')' . PHP_EOL . 'UNION (';
1525 $name = 'UNION ()';
1526 }
1527
1528 // Get the JDatabaseQueryElement if it does not exist
1529 if (is_null($this->union))
1530 {
1531 $this->union = new JDatabaseQueryElement($name, $query, "$glue");
1532 }
1533 // Otherwise append the second UNION.
1534 else
1535 {
1536 $this->union->append($query);
1537 }
1538
1539 return $this;
1540 }
1541
1542 /**
1543 * Add a query to UNION DISTINCT with the current query. Simply a proxy to union with the DISTINCT keyword.
1544 *
1545 * Usage:
1546 * $query->unionDistinct('SELECT name FROM #__foo')
1547 *
1548 * @param mixed $query The JDatabaseQuery object or string to union.
1549 * @param string $glue The glue by which to join the conditions.
1550 *
1551 * @return JDatabaseQuery Returns this object to allow chaining.
1552 *
1553 * @see union
1554 *
1555 * @since 12.1
1556 */
1557 public function unionDistinct($query, $glue = '')
1558 {
1559 $distinct = true;
1560
1561 // Apply the distinct flag to the union.
1562 return $this->union($query, $distinct, $glue);
1563 }
1564
1565 /**
1566 * Find and replace sprintf-like tokens in a format string.
1567 * Each token takes one of the following forms:
1568 * %% - A literal percent character.
1569 * %[t] - Where [t] is a type specifier.
1570 * %[n]$[x] - Where [n] is an argument specifier and [t] is a type specifier.
1571 *
1572 * Types:
1573 * a - Numeric: Replacement text is coerced to a numeric type but not quoted or escaped.
1574 * e - Escape: Replacement text is passed to $this->escape().
1575 * E - Escape (extra): Replacement text is passed to $this->escape() with true as the second argument.
1576 * n - Name Quote: Replacement text is passed to $this->quoteName().
1577 * q - Quote: Replacement text is passed to $this->quote().
1578 * Q - Quote (no escape): Replacement text is passed to $this->quote() with false as the second argument.
1579 * r - Raw: Replacement text is used as-is. (Be careful)
1580 *
1581 * Date Types:
1582 * - Replacement text automatically quoted (use uppercase for Name Quote).
1583 * - Replacement text should be a string in date format or name of a date column.
1584 * y/Y - Year
1585 * m/M - Month
1586 * d/D - Day
1587 * h/H - Hour
1588 * i/I - Minute
1589 * s/S - Second
1590 *
1591 * Invariable Types:
1592 * - Takes no argument.
1593 * - Argument index not incremented.
1594 * t - Replacement text is the result of $this->currentTimestamp().
1595 * z - Replacement text is the result of $this->nullDate(false).
1596 * Z - Replacement text is the result of $this->nullDate(true).
1597 *
1598 * Usage:
1599 * $query->format('SELECT %1$n FROM %2$n WHERE %3$n = %4$a', 'foo', '#__foo', 'bar', 1);
1600 * Returns: SELECT `foo` FROM `#__foo` WHERE `bar` = 1
1601 *
1602 * Notes:
1603 * The argument specifier is optional but recommended for clarity.
1604 * The argument index used for unspecified tokens is incremented only when used.
1605 *
1606 * @param string $format The formatting string.
1607 *
1608 * @return string Returns a string produced according to the formatting string.
1609 *
1610 * @since 12.3
1611 */
1612 public function format($format)
1613 {
1614 $query = $this;
1615 $args = array_slice(func_get_args(), 1);
1616 array_unshift($args, null);
1617
1618 $i = 1;
1619 $func = function ($match) use ($query, $args, &$i)
1620 {
1621 if (isset($match[6]) && $match[6] == '%')
1622 {
1623 return '%';
1624 }
1625
1626 // No argument required, do not increment the argument index.
1627 switch ($match[5])
1628 {
1629 case 't':
1630 return $query->currentTimestamp();
1631 break;
1632
1633 case 'z':
1634 return $query->nullDate(false);
1635 break;
1636
1637 case 'Z':
1638 return $query->nullDate(true);
1639 break;
1640 }
1641
1642 // Increment the argument index only if argument specifier not provided.
1643 $index = is_numeric($match[4]) ? (int) $match[4] : $i++;
1644
1645 if (!$index || !isset($args[$index]))
1646 {
1647 // TODO - What to do? sprintf() throws a Warning in these cases.
1648 $replacement = '';
1649 }
1650 else
1651 {
1652 $replacement = $args[$index];
1653 }
1654
1655 switch ($match[5])
1656 {
1657 case 'a':
1658 return 0 + $replacement;
1659 break;
1660
1661 case 'e':
1662 return $query->escape($replacement);
1663 break;
1664
1665 case 'E':
1666 return $query->escape($replacement, true);
1667 break;
1668
1669 case 'n':
1670 return $query->quoteName($replacement);
1671 break;
1672
1673 case 'q':
1674 return $query->quote($replacement);
1675 break;
1676
1677 case 'Q':
1678 return $query->quote($replacement, false);
1679 break;
1680
1681 case 'r':
1682 return $replacement;
1683 break;
1684
1685 // Dates
1686 case 'y':
1687 return $query->year($query->quote($replacement));
1688 break;
1689
1690 case 'Y':
1691 return $query->year($query->quoteName($replacement));
1692 break;
1693
1694 case 'm':
1695 return $query->month($query->quote($replacement));
1696 break;
1697
1698 case 'M':
1699 return $query->month($query->quoteName($replacement));
1700 break;
1701
1702 case 'd':
1703 return $query->day($query->quote($replacement));
1704 break;
1705
1706 case 'D':
1707 return $query->day($query->quoteName($replacement));
1708 break;
1709
1710 case 'h':
1711 return $query->hour($query->quote($replacement));
1712 break;
1713
1714 case 'H':
1715 return $query->hour($query->quoteName($replacement));
1716 break;
1717
1718 case 'i':
1719 return $query->minute($query->quote($replacement));
1720 break;
1721
1722 case 'I':
1723 return $query->minute($query->quoteName($replacement));
1724 break;
1725
1726 case 's':
1727 return $query->second($query->quote($replacement));
1728 break;
1729
1730 case 'S':
1731 return $query->second($query->quoteName($replacement));
1732 break;
1733 }
1734
1735 return '';
1736 };
1737
1738 /**
1739 * Regexp to find an replace all tokens.
1740 * Matched fields:
1741 * 0: Full token
1742 * 1: Everything following '%'
1743 * 2: Everything following '%' unless '%'
1744 * 3: Argument specifier and '$'
1745 * 4: Argument specifier
1746 * 5: Type specifier
1747 * 6: '%' if full token is '%%'
1748 */
1749 return preg_replace_callback('#%(((([\d]+)\$)?([aeEnqQryYmMdDhHiIsStzZ]))|(%))#', $func, $format);
1750 }
1751
1752 /**
1753 * Add to the current date and time.
1754 * Usage:
1755 * $query->select($query->dateAdd());
1756 * Prefixing the interval with a - (negative sign) will cause subtraction to be used.
1757 * Note: Not all drivers support all units.
1758 *
1759 * @param mixed $date The date to add to. May be date or datetime
1760 * @param string $interval The string representation of the appropriate number of units
1761 * @param string $datePart The part of the date to perform the addition on
1762 *
1763 * @return string The string with the appropriate sql for addition of dates
1764 *
1765 * @link http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-add
1766 * @since 13.1
1767 */
1768 public function dateAdd($date, $interval, $datePart)
1769 {
1770 return trim("DATE_ADD('" . $date . "', INTERVAL " . $interval . ' ' . $datePart . ')');
1771 }
1772
1773 /**
1774 * Add a query to UNION ALL with the current query.
1775 * Multiple unions each require separate statements and create an array of unions.
1776 *
1777 * Usage:
1778 * $query->union('SELECT name FROM #__foo')
1779 * $query->union(array('SELECT name FROM #__foo','SELECT name FROM #__bar'))
1780 *
1781 * @param mixed $query The JDatabaseQuery object or string to union.
1782 * @param boolean $distinct Not used - ignored.
1783 * @param string $glue Not used - ignored.
1784 *
1785 * @return JDatabaseQuery Returns this object to allow chaining.
1786 *
1787 * @see union
1788 *
1789 * @since 13.1
1790 */
1791 public function unionAll($query, $distinct = false, $glue = '')
1792 {
1793 $glue = ')' . PHP_EOL . 'UNION ALL (';
1794 $name = 'UNION ALL ()';
1795
1796 // Get the JDatabaseQueryElement if it does not exist
1797 if (is_null($this->unionAll))
1798 {
1799 $this->unionAll = new JDatabaseQueryElement($name, $query, "$glue");
1800 }
1801
1802 // Otherwise append the second UNION.
1803 else
1804 {
1805 $this->unionAll->append($query);
1806 }
1807
1808 return $this;
1809 }
1810
1811 /**
1812 * Validate arguments which are passed to selectRowNumber method and set up common variables.
1813 *
1814 * @param string $orderBy An expression of ordering for window function.
1815 * @param string $orderColumnAlias An alias for new ordering column.
1816 *
1817 * @return void
1818 *
1819 * @since 3.7.0
1820 * @throws RuntimeException
1821 */
1822 protected function validateRowNumber($orderBy, $orderColumnAlias)
1823 {
1824 if ($this->selectRowNumber)
1825 {
1826 throw new RuntimeException("Method 'selectRowNumber' can be called only once per instance.");
1827 }
1828
1829 $this->type = 'select';
1830
1831 $this->selectRowNumber = array(
1832 'orderBy' => $orderBy,
1833 'orderColumnAlias' => $orderColumnAlias,
1834 );
1835 }
1836
1837 /**
1838 * Return the number of the current row.
1839 *
1840 * Usage:
1841 * $query->select('id');
1842 * $query->selectRowNumber('ordering,publish_up DESC', 'new_ordering');
1843 * $query->from('#__content');
1844 *
1845 * @param string $orderBy An expression of ordering for window function.
1846 * @param string $orderColumnAlias An alias for new ordering column.
1847 *
1848 * @return JDatabaseQuery Returns this object to allow chaining.
1849 *
1850 * @since 3.7.0
1851 * @throws RuntimeException
1852 */
1853 public function selectRowNumber($orderBy, $orderColumnAlias)
1854 {
1855 $this->validateRowNumber($orderBy, $orderColumnAlias);
1856 $this->select("ROW_NUMBER() OVER (ORDER BY $orderBy) AS $orderColumnAlias");
1857
1858 return $this;
1859 }
1860 }
1861