SQLiteGrammar.php
TLDR
This file, SQLiteGrammar.php
, is a class that extends the Grammar
class and provides methods for compiling SQL queries with SQLite syntax. It contains methods for compiling different types of clauses, such as "where date", "where day", "where month", etc. There are also methods for compiling JSON-related statements. It overrides some methods inherited from the Grammar
class to provide SQLite-specific functionality.
Methods
compileLock
This method is responsible for compiling the lock into SQL. It takes a Builder
object and a $value
parameter and returns the compiled SQL string.
wrapUnion
This method wraps a union subquery in parentheses. It takes a $sql
parameter and returns the wrapped SQL string.
whereDate
This method compiles a "where date" clause. It takes a Builder
object and a $where
parameter and returns the compiled SQL string.
whereDay
This method compiles a "where day" clause. It takes a Builder
object and a $where
parameter and returns the compiled SQL string.
whereMonth
This method compiles a "where month" clause. It takes a Builder
object and a $where
parameter and returns the compiled SQL string.
whereYear
This method compiles a "where year" clause. It takes a Builder
object and a $where
parameter and returns the compiled SQL string.
whereTime
This method compiles a "where time" clause. It takes a Builder
object and a $where
parameter and returns the compiled SQL string.
dateBasedWhere
This method compiles a date-based where clause. It takes a $type
parameter, a Builder
object, and a $where
parameter and returns the compiled SQL string.
compileIndexHint
This method compiles the index hints for the query. It takes a Builder
object and an $indexHint
parameter and returns the compiled SQL string.
compileJsonLength
This method compiles a "JSON length" statement into SQL. It takes a $column
, $operator
, and $value
parameters and returns the compiled SQL string.
compileJsonContains
This method compiles a "JSON contains" statement into SQL. It takes a $column
and $value
parameters and returns the compiled SQL string.
prepareBindingForJsonContains
This method prepares the binding for a "JSON contains" statement. It takes a $binding
parameter and returns the prepared binding.
compileJsonContainsKey
This method compiles a "JSON contains key" statement into SQL. It takes a $column
parameter and returns the compiled SQL string.
compileUpdate
This method compiles an update statement into SQL. It takes a Builder
object and an array of $values
and returns the compiled SQL string.
compileInsertOrIgnore
This method compiles an insert ignore statement into SQL. It takes a Builder
object and an array of $values
and returns the compiled SQL string.
compileUpdateColumns
This method compiles the columns for an update statement. It takes a Builder
object and an array of $values
and returns the compiled SQL string.
compileUpsert
This method compiles an "upsert" statement into SQL. It takes a Builder
object and arrays of $values
, $uniqueBy
, and $update
and returns the compiled SQL string.
groupJsonColumnsForUpdate
This method groups the nested JSON columns. It takes an array of $values
and returns the grouped array.
compileJsonPatch
This method compiles a "JSON" patch statement into SQL. It takes a $column
and $value
and returns the compiled SQL string.
compileUpdateWithJoinsOrLimit
This method compiles an update statement with joins or limit into SQL. It takes a Builder
object and an array of $values
and returns the compiled SQL string.
prepareBindingsForUpdate
This method prepares the bindings for an update statement. It takes arrays of $bindings
and $values
and returns the prepared bindings.
compileDelete
This method compiles a delete statement into SQL. It takes a Builder
object and returns the compiled SQL string.
compileDeleteWithJoinsOrLimit
This method compiles a delete statement with joins or limit into SQL. It takes a Builder
object and returns the compiled SQL string.
compileTruncate
This method compiles a truncate table statement into SQL. It takes a Builder
object and returns the compiled SQL string.
wrapJsonSelector
This method wraps the given JSON selector. It takes a $value
parameter and returns the wrapped SQL string.
Classes
Class 1: SQLiteGrammar
This class extends the Grammar
class and provides methods for compiling SQL queries with SQLite syntax. It overrides some methods inherited from the Grammar
class and provides SQLite-specific functionality for compiling different types of clauses and statements.
<?php
namespace Illuminate\Database\Query\Grammars;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
class SQLiteGrammar extends Grammar
{
/**
* All of the available clause operators.
*
* @var string[]
*/
protected $operators = [
'=', '<', '>', '<=', '>=', '<>', '!=',
'like', 'not like', 'ilike',
'&', '|', '<<', '>>',
];
/**
* Compile the lock into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @param bool|string $value
* @return string
*/
protected function compileLock(Builder $query, $value)
{
return '';
}
/**
* Wrap a union subquery in parentheses.
*
* @param string $sql
* @return string
*/
protected function wrapUnion($sql)
{
return 'select * from ('.$sql.')';
}
/**
* 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('%Y-%m-%d', $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('%d', $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('%m', $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('%Y', $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('%H:%M:%S', $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 "strftime('{$type}', {$this->wrap($where['column'])}) {$where['operator']} cast({$value} as text)";
}
/**
* Compile the index hints for the query.
*
* @param \Illuminate\Database\Query\Builder $query
* @param \Illuminate\Database\Query\IndexHint $indexHint
* @return string
*/
protected function compileIndexHint(Builder $query, $indexHint)
{
return $indexHint->type === 'force'
? "indexed by {$indexHint->index}"
: '';
}
/**
* Compile a "JSON length" statement into SQL.
*
* @param string $column
* @param string $operator
* @param string $value
* @return string
*/
protected function compileJsonLength($column, $operator, $value)
{
[$field, $path] = $this->wrapJsonFieldAndPath($column);
return 'json_array_length('.$field.$path.') '.$operator.' '.$value;
}
/**
* Compile a "JSON contains" statement into SQL.
*
* @param string $column
* @param mixed $value
* @return string
*/
protected function compileJsonContains($column, $value)
{
[$field, $path] = $this->wrapJsonFieldAndPath($column);
return 'exists (select 1 from json_each('.$field.$path.') where '.$this->wrap('json_each.value').' is '.$value.')';
}
/**
* Prepare the binding for a "JSON contains" statement.
*
* @param mixed $binding
* @return mixed
*/
public function prepareBindingForJsonContains($binding)
{
return $binding;
}
/**
* Compile a "JSON contains key" statement into SQL.
*
* @param string $column
* @return string
*/
protected function compileJsonContainsKey($column)
{
[$field, $path] = $this->wrapJsonFieldAndPath($column);
return 'json_type('.$field.$path.') is not null';
}
/**
* Compile an update statement into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $values
* @return string
*/
public function compileUpdate(Builder $query, array $values)
{
if (isset($query->joins) || isset($query->limit)) {
return $this->compileUpdateWithJoinsOrLimit($query, $values);
}
return parent::compileUpdate($query, $values);
}
/**
* Compile an insert ignore statement into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $values
* @return string
*/
public function compileInsertOrIgnore(Builder $query, array $values)
{
return Str::replaceFirst('insert', 'insert or ignore', $this->compileInsert($query, $values));
}
/**
* 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)
{
$jsonGroups = $this->groupJsonColumnsForUpdate($values);
return collect($values)->reject(function ($value, $key) {
return $this->isJsonSelector($key);
})->merge($jsonGroups)->map(function ($value, $key) use ($jsonGroups) {
$column = last(explode('.', $key));
$value = isset($jsonGroups[$key]) ? $this->compileJsonPatch($column, $value) : $this->parameter($value);
return $this->wrap($column).' = '.$value;
})->implode(', ');
}
/**
* Compile an "upsert" statement into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $values
* @param array $uniqueBy
* @param array $update
* @return string
*/
public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
{
$sql = $this->compileInsert($query, $values);
$sql .= ' on conflict ('.$this->columnize($uniqueBy).') do update set ';
$columns = collect($update)->map(function ($value, $key) {
return is_numeric($key)
? $this->wrap($value).' = '.$this->wrapValue('excluded').'.'.$this->wrap($value)
: $this->wrap($key).' = '.$this->parameter($value);
})->implode(', ');
return $sql.$columns;
}
/**
* Group the nested JSON columns.
*
* @param array $values
* @return array
*/
protected function groupJsonColumnsForUpdate(array $values)
{
$groups = [];
foreach ($values as $key => $value) {
if ($this->isJsonSelector($key)) {
Arr::set($groups, str_replace('->', '.', Str::after($key, '.')), $value);
}
}
return $groups;
}
/**
* Compile a "JSON" patch statement into SQL.
*
* @param string $column
* @param mixed $value
* @return string
*/
protected function compileJsonPatch($column, $value)
{
return "json_patch(ifnull({$this->wrap($column)}, json('{}')), json({$this->parameter($value)}))";
}
/**
* Compile an update statement with joins or limit into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $values
* @return string
*/
protected function compileUpdateWithJoinsOrLimit(Builder $query, array $values)
{
$table = $this->wrapTable($query->from);
$columns = $this->compileUpdateColumns($query, $values);
$alias = last(preg_split('/\s+as\s+/i', $query->from));
$selectSql = $this->compileSelect($query->select($alias.'.rowid'));
return "update {$table} set {$columns} where {$this->wrap('rowid')} in ({$selectSql})";
}
/**
* Prepare the bindings for an update statement.
*
* @param array $bindings
* @param array $values
* @return array
*/
public function prepareBindingsForUpdate(array $bindings, array $values)
{
$groups = $this->groupJsonColumnsForUpdate($values);
$values = collect($values)->reject(function ($value, $key) {
return $this->isJsonSelector($key);
})->merge($groups)->map(function ($value) {
return is_array($value) ? json_encode($value) : $value;
})->all();
$cleanBindings = Arr::except($bindings, 'select');
return array_values(
array_merge($values, Arr::flatten($cleanBindings))
);
}
/**
* Compile a delete statement into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @return string
*/
public function compileDelete(Builder $query)
{
if (isset($query->joins) || isset($query->limit)) {
return $this->compileDeleteWithJoinsOrLimit($query);
}
return parent::compileDelete($query);
}
/**
* Compile a delete statement with joins or limit into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @return string
*/
protected function compileDeleteWithJoinsOrLimit(Builder $query)
{
$table = $this->wrapTable($query->from);
$alias = last(preg_split('/\s+as\s+/i', $query->from));
$selectSql = $this->compileSelect($query->select($alias.'.rowid'));
return "delete from {$table} where {$this->wrap('rowid')} in ({$selectSql})";
}
/**
* Compile a truncate table statement into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @return array
*/
public function compileTruncate(Builder $query)
{
return [
'delete from sqlite_sequence where name = ?' => [$query->from],
'delete from '.$this->wrapTable($query->from) => [],
];
}
/**
* Wrap the given JSON selector.
*
* @param string $value
* @return string
*/
protected function wrapJsonSelector($value)
{
[$field, $path] = $this->wrapJsonFieldAndPath($value);
return 'json_extract('.$field.$path.')';
}
}