| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549 |
- <?php
- namespace Illuminate\Database\Query\Grammars;
- use Illuminate\Contracts\Database\Query\Expression;
- use Illuminate\Database\Concerns\CompilesJsonPaths;
- use Illuminate\Database\Grammar as BaseGrammar;
- use Illuminate\Database\Query\Builder;
- use Illuminate\Database\Query\JoinClause;
- use Illuminate\Database\Query\JoinLateralClause;
- use Illuminate\Support\Arr;
- use RuntimeException;
- class Grammar extends BaseGrammar
- {
- use CompilesJsonPaths;
- /**
- * The grammar specific operators.
- *
- * @var array
- */
- protected $operators = [];
- /**
- * The grammar specific bitwise operators.
- *
- * @var array
- */
- protected $bitwiseOperators = [];
- /**
- * The components that make up a select clause.
- *
- * @var string[]
- */
- protected $selectComponents = [
- 'aggregate',
- 'columns',
- 'from',
- 'indexHint',
- 'joins',
- 'wheres',
- 'groups',
- 'havings',
- 'orders',
- 'limit',
- 'offset',
- 'lock',
- ];
- /**
- * Compile a select query into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return string
- */
- public function compileSelect(Builder $query)
- {
- if (($query->unions || $query->havings) && $query->aggregate) {
- return $this->compileUnionAggregate($query);
- }
- // If a "group limit" is in place, we will need to compile the SQL to use a
- // different syntax. This primarily supports limits on eager loads using
- // Eloquent. We'll also set the columns if they have not been defined.
- if (isset($query->groupLimit)) {
- if (is_null($query->columns)) {
- $query->columns = ['*'];
- }
- return $this->compileGroupLimit($query);
- }
- // If the query does not have any columns set, we'll set the columns to the
- // * character to just get all of the columns from the database. Then we
- // can build the query and concatenate all the pieces together as one.
- $original = $query->columns;
- if (is_null($query->columns)) {
- $query->columns = ['*'];
- }
- // To compile the query, we'll spin through each component of the query and
- // see if that component exists. If it does we'll just call the compiler
- // function for the component which is responsible for making the SQL.
- $sql = trim($this->concatenate(
- $this->compileComponents($query))
- );
- if ($query->unions) {
- $sql = $this->wrapUnion($sql).' '.$this->compileUnions($query);
- }
- $query->columns = $original;
- return $sql;
- }
- /**
- * Compile the components necessary for a select clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return array
- */
- protected function compileComponents(Builder $query)
- {
- $sql = [];
- foreach ($this->selectComponents as $component) {
- if (isset($query->$component)) {
- $method = 'compile'.ucfirst($component);
- $sql[$component] = $this->$method($query, $query->$component);
- }
- }
- return $sql;
- }
- /**
- * Compile an aggregated select clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $aggregate
- * @return string
- */
- protected function compileAggregate(Builder $query, $aggregate)
- {
- $column = $this->columnize($aggregate['columns']);
- // If the query has a "distinct" constraint and we're not asking for all columns
- // we need to prepend "distinct" onto the column name so that the query takes
- // it into account when it performs the aggregating operations on the data.
- if (is_array($query->distinct)) {
- $column = 'distinct '.$this->columnize($query->distinct);
- } elseif ($query->distinct && $column !== '*') {
- $column = 'distinct '.$column;
- }
- return 'select '.$aggregate['function'].'('.$column.') as aggregate';
- }
- /**
- * Compile the "select *" portion of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $columns
- * @return string|null
- */
- protected function compileColumns(Builder $query, $columns)
- {
- // If the query is actually performing an aggregating select, we will let that
- // compiler handle the building of the select clauses, as it will need some
- // more syntax that is best handled by that function to keep things neat.
- if (! is_null($query->aggregate)) {
- return;
- }
- if ($query->distinct) {
- $select = 'select distinct ';
- } else {
- $select = 'select ';
- }
- return $select.$this->columnize($columns);
- }
- /**
- * Compile the "from" portion of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param string $table
- * @return string
- */
- protected function compileFrom(Builder $query, $table)
- {
- return 'from '.$this->wrapTable($table);
- }
- /**
- * Compile the "join" portions of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $joins
- * @return string
- */
- protected function compileJoins(Builder $query, $joins)
- {
- return collect($joins)->map(function ($join) use ($query) {
- $table = $this->wrapTable($join->table);
- $nestedJoins = is_null($join->joins) ? '' : ' '.$this->compileJoins($query, $join->joins);
- $tableAndNestedJoins = is_null($join->joins) ? $table : '('.$table.$nestedJoins.')';
- if ($join instanceof JoinLateralClause) {
- return $this->compileJoinLateral($join, $tableAndNestedJoins);
- }
- return trim("{$join->type} join {$tableAndNestedJoins} {$this->compileWheres($join)}");
- })->implode(' ');
- }
- /**
- * Compile a "lateral join" clause.
- *
- * @param \Illuminate\Database\Query\JoinLateralClause $join
- * @param string $expression
- * @return string
- *
- * @throws \RuntimeException
- */
- public function compileJoinLateral(JoinLateralClause $join, string $expression): string
- {
- throw new RuntimeException('This database engine does not support lateral joins.');
- }
- /**
- * Compile the "where" portions of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return string
- */
- public function compileWheres(Builder $query)
- {
- // Each type of where clause has its own compiler function, which is responsible
- // for actually creating the where clauses SQL. This helps keep the code nice
- // and maintainable since each clause has a very small method that it uses.
- if (is_null($query->wheres)) {
- return '';
- }
- // If we actually have some where clauses, we will strip off the first boolean
- // operator, which is added by the query builders for convenience so we can
- // avoid checking for the first clauses in each of the compilers methods.
- if (count($sql = $this->compileWheresToArray($query)) > 0) {
- return $this->concatenateWhereClauses($query, $sql);
- }
- return '';
- }
- /**
- * Get an array of all the where clauses for the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return array
- */
- protected function compileWheresToArray($query)
- {
- return collect($query->wheres)->map(function ($where) use ($query) {
- return $where['boolean'].' '.$this->{"where{$where['type']}"}($query, $where);
- })->all();
- }
- /**
- * Format the where clause statements into one string.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $sql
- * @return string
- */
- protected function concatenateWhereClauses($query, $sql)
- {
- $conjunction = $query instanceof JoinClause ? 'on' : 'where';
- return $conjunction.' '.$this->removeLeadingBoolean(implode(' ', $sql));
- }
- /**
- * Compile a raw where clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereRaw(Builder $query, $where)
- {
- return $where['sql'] instanceof Expression ? $where['sql']->getValue($this) : $where['sql'];
- }
- /**
- * Compile a basic where clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereBasic(Builder $query, $where)
- {
- $value = $this->parameter($where['value']);
- $operator = str_replace('?', '??', $where['operator']);
- return $this->wrap($where['column']).' '.$operator.' '.$value;
- }
- /**
- * Compile a bitwise operator where clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereBitwise(Builder $query, $where)
- {
- return $this->whereBasic($query, $where);
- }
- /**
- * Compile a "where in" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereIn(Builder $query, $where)
- {
- if (! empty($where['values'])) {
- return $this->wrap($where['column']).' in ('.$this->parameterize($where['values']).')';
- }
- return '0 = 1';
- }
- /**
- * Compile a "where not in" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereNotIn(Builder $query, $where)
- {
- if (! empty($where['values'])) {
- return $this->wrap($where['column']).' not in ('.$this->parameterize($where['values']).')';
- }
- return '1 = 1';
- }
- /**
- * Compile a "where not in raw" clause.
- *
- * For safety, whereIntegerInRaw ensures this method is only used with integer values.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereNotInRaw(Builder $query, $where)
- {
- if (! empty($where['values'])) {
- return $this->wrap($where['column']).' not in ('.implode(', ', $where['values']).')';
- }
- return '1 = 1';
- }
- /**
- * Compile a "where in raw" clause.
- *
- * For safety, whereIntegerInRaw ensures this method is only used with integer values.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereInRaw(Builder $query, $where)
- {
- if (! empty($where['values'])) {
- return $this->wrap($where['column']).' in ('.implode(', ', $where['values']).')';
- }
- return '0 = 1';
- }
- /**
- * Compile a "where null" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereNull(Builder $query, $where)
- {
- return $this->wrap($where['column']).' is null';
- }
- /**
- * Compile a "where not null" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereNotNull(Builder $query, $where)
- {
- return $this->wrap($where['column']).' is not null';
- }
- /**
- * Compile a "between" where clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereBetween(Builder $query, $where)
- {
- $between = $where['not'] ? 'not between' : 'between';
- $min = $this->parameter(is_array($where['values']) ? reset($where['values']) : $where['values'][0]);
- $max = $this->parameter(is_array($where['values']) ? end($where['values']) : $where['values'][1]);
- return $this->wrap($where['column']).' '.$between.' '.$min.' and '.$max;
- }
- /**
- * Compile a "between" where clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereBetweenColumns(Builder $query, $where)
- {
- $between = $where['not'] ? 'not between' : 'between';
- $min = $this->wrap(is_array($where['values']) ? reset($where['values']) : $where['values'][0]);
- $max = $this->wrap(is_array($where['values']) ? end($where['values']) : $where['values'][1]);
- return $this->wrap($where['column']).' '.$between.' '.$min.' and '.$max;
- }
- /**
- * Compile a "where date" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereDate(Builder $query, $where)
- {
- return $this->dateBasedWhere('date', $query, $where);
- }
- /**
- * Compile a "where time" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereTime(Builder $query, $where)
- {
- return $this->dateBasedWhere('time', $query, $where);
- }
- /**
- * Compile a "where day" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereDay(Builder $query, $where)
- {
- return $this->dateBasedWhere('day', $query, $where);
- }
- /**
- * Compile a "where month" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereMonth(Builder $query, $where)
- {
- return $this->dateBasedWhere('month', $query, $where);
- }
- /**
- * Compile a "where year" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereYear(Builder $query, $where)
- {
- return $this->dateBasedWhere('year', $query, $where);
- }
- /**
- * Compile a date based where clause.
- *
- * @param string $type
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function dateBasedWhere($type, Builder $query, $where)
- {
- $value = $this->parameter($where['value']);
- return $type.'('.$this->wrap($where['column']).') '.$where['operator'].' '.$value;
- }
- /**
- * Compile a where clause comparing two columns.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereColumn(Builder $query, $where)
- {
- return $this->wrap($where['first']).' '.$where['operator'].' '.$this->wrap($where['second']);
- }
- /**
- * Compile a nested where clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereNested(Builder $query, $where)
- {
- // Here we will calculate what portion of the string we need to remove. If this
- // is a join clause query, we need to remove the "on" portion of the SQL and
- // if it is a normal query we need to take the leading "where" of queries.
- $offset = $where['query'] instanceof JoinClause ? 3 : 6;
- return '('.substr($this->compileWheres($where['query']), $offset).')';
- }
- /**
- * Compile a where condition with a sub-select.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereSub(Builder $query, $where)
- {
- $select = $this->compileSelect($where['query']);
- return $this->wrap($where['column']).' '.$where['operator']." ($select)";
- }
- /**
- * Compile a where exists clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereExists(Builder $query, $where)
- {
- return 'exists ('.$this->compileSelect($where['query']).')';
- }
- /**
- * Compile a where exists clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereNotExists(Builder $query, $where)
- {
- return 'not exists ('.$this->compileSelect($where['query']).')';
- }
- /**
- * Compile a where row values condition.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereRowValues(Builder $query, $where)
- {
- $columns = $this->columnize($where['columns']);
- $values = $this->parameterize($where['values']);
- return '('.$columns.') '.$where['operator'].' ('.$values.')';
- }
- /**
- * Compile a "where JSON boolean" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereJsonBoolean(Builder $query, $where)
- {
- $column = $this->wrapJsonBooleanSelector($where['column']);
- $value = $this->wrapJsonBooleanValue(
- $this->parameter($where['value'])
- );
- return $column.' '.$where['operator'].' '.$value;
- }
- /**
- * Compile a "where JSON contains" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereJsonContains(Builder $query, $where)
- {
- $not = $where['not'] ? 'not ' : '';
- return $not.$this->compileJsonContains(
- $where['column'],
- $this->parameter($where['value'])
- );
- }
- /**
- * Compile a "JSON contains" statement into SQL.
- *
- * @param string $column
- * @param string $value
- * @return string
- *
- * @throws \RuntimeException
- */
- protected function compileJsonContains($column, $value)
- {
- throw new RuntimeException('This database engine does not support JSON contains operations.');
- }
- /**
- * Compile a "where JSON overlaps" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereJsonOverlaps(Builder $query, $where)
- {
- $not = $where['not'] ? 'not ' : '';
- return $not.$this->compileJsonOverlaps(
- $where['column'],
- $this->parameter($where['value'])
- );
- }
- /**
- * Compile a "JSON overlaps" statement into SQL.
- *
- * @param string $column
- * @param string $value
- * @return string
- *
- * @throws \RuntimeException
- */
- protected function compileJsonOverlaps($column, $value)
- {
- throw new RuntimeException('This database engine does not support JSON overlaps operations.');
- }
- /**
- * Prepare the binding for a "JSON contains" statement.
- *
- * @param mixed $binding
- * @return string
- */
- public function prepareBindingForJsonContains($binding)
- {
- return json_encode($binding, JSON_UNESCAPED_UNICODE);
- }
- /**
- * Compile a "where JSON contains key" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereJsonContainsKey(Builder $query, $where)
- {
- $not = $where['not'] ? 'not ' : '';
- return $not.$this->compileJsonContainsKey(
- $where['column']
- );
- }
- /**
- * Compile a "JSON contains key" statement into SQL.
- *
- * @param string $column
- * @return string
- *
- * @throws \RuntimeException
- */
- protected function compileJsonContainsKey($column)
- {
- throw new RuntimeException('This database engine does not support JSON contains key operations.');
- }
- /**
- * Compile a "where JSON length" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- protected function whereJsonLength(Builder $query, $where)
- {
- return $this->compileJsonLength(
- $where['column'],
- $where['operator'],
- $this->parameter($where['value'])
- );
- }
- /**
- * Compile a "JSON length" statement into SQL.
- *
- * @param string $column
- * @param string $operator
- * @param string $value
- * @return string
- *
- * @throws \RuntimeException
- */
- protected function compileJsonLength($column, $operator, $value)
- {
- throw new RuntimeException('This database engine does not support JSON length operations.');
- }
- /**
- * Compile a "JSON value cast" statement into SQL.
- *
- * @param string $value
- * @return string
- */
- public function compileJsonValueCast($value)
- {
- return $value;
- }
- /**
- * Compile a "where fulltext" clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- public function whereFullText(Builder $query, $where)
- {
- throw new RuntimeException('This database engine does not support fulltext search operations.');
- }
- /**
- * Compile a clause based on an expression.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $where
- * @return string
- */
- public function whereExpression(Builder $query, $where)
- {
- return $where['column']->getValue($this);
- }
- /**
- * Compile the "group by" portions of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $groups
- * @return string
- */
- protected function compileGroups(Builder $query, $groups)
- {
- return 'group by '.$this->columnize($groups);
- }
- /**
- * Compile the "having" portions of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return string
- */
- protected function compileHavings(Builder $query)
- {
- return 'having '.$this->removeLeadingBoolean(collect($query->havings)->map(function ($having) {
- return $having['boolean'].' '.$this->compileHaving($having);
- })->implode(' '));
- }
- /**
- * Compile a single having clause.
- *
- * @param array $having
- * @return string
- */
- protected function compileHaving(array $having)
- {
- // If the having clause is "raw", we can just return the clause straight away
- // without doing any more processing on it. Otherwise, we will compile the
- // clause into SQL based on the components that make it up from builder.
- return match ($having['type']) {
- 'Raw' => $having['sql'],
- 'between' => $this->compileHavingBetween($having),
- 'Null' => $this->compileHavingNull($having),
- 'NotNull' => $this->compileHavingNotNull($having),
- 'bit' => $this->compileHavingBit($having),
- 'Expression' => $this->compileHavingExpression($having),
- 'Nested' => $this->compileNestedHavings($having),
- default => $this->compileBasicHaving($having),
- };
- }
- /**
- * Compile a basic having clause.
- *
- * @param array $having
- * @return string
- */
- protected function compileBasicHaving($having)
- {
- $column = $this->wrap($having['column']);
- $parameter = $this->parameter($having['value']);
- return $column.' '.$having['operator'].' '.$parameter;
- }
- /**
- * Compile a "between" having clause.
- *
- * @param array $having
- * @return string
- */
- protected function compileHavingBetween($having)
- {
- $between = $having['not'] ? 'not between' : 'between';
- $column = $this->wrap($having['column']);
- $min = $this->parameter(head($having['values']));
- $max = $this->parameter(last($having['values']));
- return $column.' '.$between.' '.$min.' and '.$max;
- }
- /**
- * Compile a having null clause.
- *
- * @param array $having
- * @return string
- */
- protected function compileHavingNull($having)
- {
- $column = $this->wrap($having['column']);
- return $column.' is null';
- }
- /**
- * Compile a having not null clause.
- *
- * @param array $having
- * @return string
- */
- protected function compileHavingNotNull($having)
- {
- $column = $this->wrap($having['column']);
- return $column.' is not null';
- }
- /**
- * Compile a having clause involving a bit operator.
- *
- * @param array $having
- * @return string
- */
- protected function compileHavingBit($having)
- {
- $column = $this->wrap($having['column']);
- $parameter = $this->parameter($having['value']);
- return '('.$column.' '.$having['operator'].' '.$parameter.') != 0';
- }
- /**
- * Compile a having clause involving an expression.
- *
- * @param array $having
- * @return string
- */
- protected function compileHavingExpression($having)
- {
- return $having['column']->getValue($this);
- }
- /**
- * Compile a nested having clause.
- *
- * @param array $having
- * @return string
- */
- protected function compileNestedHavings($having)
- {
- return '('.substr($this->compileHavings($having['query']), 7).')';
- }
- /**
- * Compile the "order by" portions of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $orders
- * @return string
- */
- protected function compileOrders(Builder $query, $orders)
- {
- if (! empty($orders)) {
- return 'order by '.implode(', ', $this->compileOrdersToArray($query, $orders));
- }
- return '';
- }
- /**
- * Compile the query orders to an array.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $orders
- * @return array
- */
- protected function compileOrdersToArray(Builder $query, $orders)
- {
- return array_map(function ($order) {
- return $order['sql'] ?? $this->wrap($order['column']).' '.$order['direction'];
- }, $orders);
- }
- /**
- * Compile the random statement into SQL.
- *
- * @param string|int $seed
- * @return string
- */
- public function compileRandom($seed)
- {
- return 'RANDOM()';
- }
- /**
- * Compile the "limit" portions of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param int $limit
- * @return string
- */
- protected function compileLimit(Builder $query, $limit)
- {
- return 'limit '.(int) $limit;
- }
- /**
- * Compile a group limit clause.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return string
- */
- protected function compileGroupLimit(Builder $query)
- {
- $selectBindings = array_merge($query->getRawBindings()['select'], $query->getRawBindings()['order']);
- $query->setBindings($selectBindings, 'select');
- $query->setBindings([], 'order');
- $limit = (int) $query->groupLimit['value'];
- $offset = $query->offset;
- if (isset($offset)) {
- $offset = (int) $offset;
- $limit += $offset;
- $query->offset = null;
- }
- $components = $this->compileComponents($query);
- $components['columns'] .= $this->compileRowNumber(
- $query->groupLimit['column'],
- $components['orders'] ?? ''
- );
- unset($components['orders']);
- $table = $this->wrap('laravel_table');
- $row = $this->wrap('laravel_row');
- $sql = $this->concatenate($components);
- $sql = 'select * from ('.$sql.') as '.$table.' where '.$row.' <= '.$limit;
- if (isset($offset)) {
- $sql .= ' and '.$row.' > '.$offset;
- }
- return $sql.' order by '.$row;
- }
- /**
- * Compile a row number clause.
- *
- * @param string $partition
- * @param string $orders
- * @return string
- */
- protected function compileRowNumber($partition, $orders)
- {
- $over = trim('partition by '.$this->wrap($partition).' '.$orders);
- return ', row_number() over ('.$over.') as '.$this->wrap('laravel_row');
- }
- /**
- * Compile the "offset" portions of the query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param int $offset
- * @return string
- */
- protected function compileOffset(Builder $query, $offset)
- {
- return 'offset '.(int) $offset;
- }
- /**
- * Compile the "union" queries attached to the main query.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return string
- */
- protected function compileUnions(Builder $query)
- {
- $sql = '';
- foreach ($query->unions as $union) {
- $sql .= $this->compileUnion($union);
- }
- if (! empty($query->unionOrders)) {
- $sql .= ' '.$this->compileOrders($query, $query->unionOrders);
- }
- if (isset($query->unionLimit)) {
- $sql .= ' '.$this->compileLimit($query, $query->unionLimit);
- }
- if (isset($query->unionOffset)) {
- $sql .= ' '.$this->compileOffset($query, $query->unionOffset);
- }
- return ltrim($sql);
- }
- /**
- * Compile a single union statement.
- *
- * @param array $union
- * @return string
- */
- protected function compileUnion(array $union)
- {
- $conjunction = $union['all'] ? ' union all ' : ' union ';
- return $conjunction.$this->wrapUnion($union['query']->toSql());
- }
- /**
- * Wrap a union subquery in parentheses.
- *
- * @param string $sql
- * @return string
- */
- protected function wrapUnion($sql)
- {
- return '('.$sql.')';
- }
- /**
- * Compile a union aggregate query into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return string
- */
- protected function compileUnionAggregate(Builder $query)
- {
- $sql = $this->compileAggregate($query, $query->aggregate);
- $query->aggregate = null;
- return $sql.' from ('.$this->compileSelect($query).') as '.$this->wrapTable('temp_table');
- }
- /**
- * Compile an exists statement into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return string
- */
- public function compileExists(Builder $query)
- {
- $select = $this->compileSelect($query);
- return "select exists({$select}) as {$this->wrap('exists')}";
- }
- /**
- * Compile an insert statement into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $values
- * @return string
- */
- public function compileInsert(Builder $query, array $values)
- {
- // Essentially we will force every insert to be treated as a batch insert which
- // simply makes creating the SQL easier for us since we can utilize the same
- // basic routine regardless of an amount of records given to us to insert.
- $table = $this->wrapTable($query->from);
- if (empty($values)) {
- return "insert into {$table} default values";
- }
- if (! is_array(reset($values))) {
- $values = [$values];
- }
- $columns = $this->columnize(array_keys(reset($values)));
- // We need to build a list of parameter place-holders of values that are bound
- // to the query. Each insert should have the exact same number of parameter
- // bindings so we will loop through the record and parameterize them all.
- $parameters = collect($values)->map(function ($record) {
- return '('.$this->parameterize($record).')';
- })->implode(', ');
- return "insert into $table ($columns) values $parameters";
- }
- /**
- * Compile an insert ignore statement into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $values
- * @return string
- *
- * @throws \RuntimeException
- */
- public function compileInsertOrIgnore(Builder $query, array $values)
- {
- throw new RuntimeException('This database engine does not support inserting while ignoring errors.');
- }
- /**
- * Compile an insert and get ID statement into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $values
- * @param string $sequence
- * @return string
- */
- public function compileInsertGetId(Builder $query, $values, $sequence)
- {
- return $this->compileInsert($query, $values);
- }
- /**
- * Compile an insert statement using a subquery into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $columns
- * @param string $sql
- * @return string
- */
- public function compileInsertUsing(Builder $query, array $columns, string $sql)
- {
- $table = $this->wrapTable($query->from);
- if (empty($columns) || $columns === ['*']) {
- return "insert into {$table} $sql";
- }
- return "insert into {$table} ({$this->columnize($columns)}) $sql";
- }
- /**
- * Compile an insert ignore statement using a subquery into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $columns
- * @param string $sql
- * @return string
- *
- * @throws \RuntimeException
- */
- public function compileInsertOrIgnoreUsing(Builder $query, array $columns, string $sql)
- {
- throw new RuntimeException('This database engine does not support inserting while ignoring errors.');
- }
- /**
- * Compile an update statement into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $values
- * @return string
- */
- public function compileUpdate(Builder $query, array $values)
- {
- $table = $this->wrapTable($query->from);
- $columns = $this->compileUpdateColumns($query, $values);
- $where = $this->compileWheres($query);
- return trim(
- isset($query->joins)
- ? $this->compileUpdateWithJoins($query, $table, $columns, $where)
- : $this->compileUpdateWithoutJoins($query, $table, $columns, $where)
- );
- }
- /**
- * Compile the columns for an update statement.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $values
- * @return string
- */
- protected function compileUpdateColumns(Builder $query, array $values)
- {
- return collect($values)->map(function ($value, $key) {
- return $this->wrap($key).' = '.$this->parameter($value);
- })->implode(', ');
- }
- /**
- * Compile an update statement without joins into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param string $table
- * @param string $columns
- * @param string $where
- * @return string
- */
- protected function compileUpdateWithoutJoins(Builder $query, $table, $columns, $where)
- {
- return "update {$table} set {$columns} {$where}";
- }
- /**
- * Compile an update statement with joins into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param string $table
- * @param string $columns
- * @param string $where
- * @return string
- */
- protected function compileUpdateWithJoins(Builder $query, $table, $columns, $where)
- {
- $joins = $this->compileJoins($query, $query->joins);
- return "update {$table} {$joins} set {$columns} {$where}";
- }
- /**
- * Compile an "upsert" statement into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param array $values
- * @param array $uniqueBy
- * @param array $update
- * @return string
- *
- * @throws \RuntimeException
- */
- public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
- {
- throw new RuntimeException('This database engine does not support upserts.');
- }
- /**
- * Prepare the bindings for an update statement.
- *
- * @param array $bindings
- * @param array $values
- * @return array
- */
- public function prepareBindingsForUpdate(array $bindings, array $values)
- {
- $cleanBindings = Arr::except($bindings, ['select', 'join']);
- $values = Arr::flatten(array_map(fn ($value) => value($value), $values));
- return array_values(
- array_merge($bindings['join'], $values, Arr::flatten($cleanBindings))
- );
- }
- /**
- * Compile a delete statement into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return string
- */
- public function compileDelete(Builder $query)
- {
- $table = $this->wrapTable($query->from);
- $where = $this->compileWheres($query);
- return trim(
- isset($query->joins)
- ? $this->compileDeleteWithJoins($query, $table, $where)
- : $this->compileDeleteWithoutJoins($query, $table, $where)
- );
- }
- /**
- * Compile a delete statement without joins into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param string $table
- * @param string $where
- * @return string
- */
- protected function compileDeleteWithoutJoins(Builder $query, $table, $where)
- {
- return "delete from {$table} {$where}";
- }
- /**
- * Compile a delete statement with joins into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param string $table
- * @param string $where
- * @return string
- */
- protected function compileDeleteWithJoins(Builder $query, $table, $where)
- {
- $alias = last(explode(' as ', $table));
- $joins = $this->compileJoins($query, $query->joins);
- return "delete {$alias} from {$table} {$joins} {$where}";
- }
- /**
- * Prepare the bindings for a delete statement.
- *
- * @param array $bindings
- * @return array
- */
- public function prepareBindingsForDelete(array $bindings)
- {
- return Arr::flatten(
- Arr::except($bindings, 'select')
- );
- }
- /**
- * Compile a truncate table statement into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @return array
- */
- public function compileTruncate(Builder $query)
- {
- return ['truncate table '.$this->wrapTable($query->from) => []];
- }
- /**
- * Compile the lock into SQL.
- *
- * @param \Illuminate\Database\Query\Builder $query
- * @param bool|string $value
- * @return string
- */
- protected function compileLock(Builder $query, $value)
- {
- return is_string($value) ? $value : '';
- }
- /**
- * Determine if the grammar supports savepoints.
- *
- * @return bool
- */
- public function supportsSavepoints()
- {
- return true;
- }
- /**
- * Compile the SQL statement to define a savepoint.
- *
- * @param string $name
- * @return string
- */
- public function compileSavepoint($name)
- {
- return 'SAVEPOINT '.$name;
- }
- /**
- * Compile the SQL statement to execute a savepoint rollback.
- *
- * @param string $name
- * @return string
- */
- public function compileSavepointRollBack($name)
- {
- return 'ROLLBACK TO SAVEPOINT '.$name;
- }
- /**
- * Wrap the given JSON selector for boolean values.
- *
- * @param string $value
- * @return string
- */
- protected function wrapJsonBooleanSelector($value)
- {
- return $this->wrapJsonSelector($value);
- }
- /**
- * Wrap the given JSON boolean value.
- *
- * @param string $value
- * @return string
- */
- protected function wrapJsonBooleanValue($value)
- {
- return $value;
- }
- /**
- * Concatenate an array of segments, removing empties.
- *
- * @param array $segments
- * @return string
- */
- protected function concatenate($segments)
- {
- return implode(' ', array_filter($segments, function ($value) {
- return (string) $value !== '';
- }));
- }
- /**
- * Remove the leading boolean from a statement.
- *
- * @param string $value
- * @return string
- */
- protected function removeLeadingBoolean($value)
- {
- return preg_replace('/and |or /i', '', $value, 1);
- }
- /**
- * Substitute the given bindings into the given raw SQL query.
- *
- * @param string $sql
- * @param array $bindings
- * @return string
- */
- public function substituteBindingsIntoRawSql($sql, $bindings)
- {
- $bindings = array_map(fn ($value) => $this->escape($value), $bindings);
- $query = '';
- $isStringLiteral = false;
- for ($i = 0; $i < strlen($sql); $i++) {
- $char = $sql[$i];
- $nextChar = $sql[$i + 1] ?? null;
- // Single quotes can be escaped as '' according to the SQL standard while
- // MySQL uses \'. Postgres has operators like ?| that must get encoded
- // in PHP like ??|. We should skip over the escaped characters here.
- if (in_array($char.$nextChar, ["\'", "''", '??'])) {
- $query .= $char.$nextChar;
- $i += 1;
- } elseif ($char === "'") { // Starting / leaving string literal...
- $query .= $char;
- $isStringLiteral = ! $isStringLiteral;
- } elseif ($char === '?' && ! $isStringLiteral) { // Substitutable binding...
- $query .= array_shift($bindings) ?? '?';
- } else { // Normal character...
- $query .= $char;
- }
- }
- return $query;
- }
- /**
- * Get the grammar specific operators.
- *
- * @return array
- */
- public function getOperators()
- {
- return $this->operators;
- }
- /**
- * Get the grammar specific bitwise operators.
- *
- * @return array
- */
- public function getBitwiseOperators()
- {
- return $this->bitwiseOperators;
- }
- }
|