SQLiteGrammar.php 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. namespace Illuminate\Database\Query\Grammars;
  3. use Illuminate\Support\Arr;
  4. use Illuminate\Support\Str;
  5. use Illuminate\Database\Query\Builder;
  6. class SQLiteGrammar extends Grammar
  7. {
  8. /**
  9. * The components that make up a select clause.
  10. *
  11. * @var array
  12. */
  13. protected $selectComponents = [
  14. 'aggregate',
  15. 'columns',
  16. 'from',
  17. 'joins',
  18. 'wheres',
  19. 'groups',
  20. 'havings',
  21. 'orders',
  22. 'limit',
  23. 'offset',
  24. 'lock',
  25. ];
  26. /**
  27. * All of the available clause operators.
  28. *
  29. * @var array
  30. */
  31. protected $operators = [
  32. '=', '<', '>', '<=', '>=', '<>', '!=',
  33. 'like', 'not like', 'ilike',
  34. '&', '|', '<<', '>>',
  35. ];
  36. /**
  37. * Compile a select query into SQL.
  38. *
  39. * @param \Illuminate\Database\Query\Builder $query
  40. * @return string
  41. */
  42. public function compileSelect(Builder $query)
  43. {
  44. $sql = parent::compileSelect($query);
  45. if ($query->unions) {
  46. $sql = 'select * from ('.$sql.') '.$this->compileUnions($query);
  47. }
  48. return $sql;
  49. }
  50. /**
  51. * Compile a single union statement.
  52. *
  53. * @param array $union
  54. * @return string
  55. */
  56. protected function compileUnion(array $union)
  57. {
  58. $conjunction = $union['all'] ? ' union all ' : ' union ';
  59. return $conjunction.'select * from ('.$union['query']->toSql().')';
  60. }
  61. /**
  62. * Compile a "where date" clause.
  63. *
  64. * @param \Illuminate\Database\Query\Builder $query
  65. * @param array $where
  66. * @return string
  67. */
  68. protected function whereDate(Builder $query, $where)
  69. {
  70. return $this->dateBasedWhere('%Y-%m-%d', $query, $where);
  71. }
  72. /**
  73. * Compile a "where day" clause.
  74. *
  75. * @param \Illuminate\Database\Query\Builder $query
  76. * @param array $where
  77. * @return string
  78. */
  79. protected function whereDay(Builder $query, $where)
  80. {
  81. return $this->dateBasedWhere('%d', $query, $where);
  82. }
  83. /**
  84. * Compile a "where month" clause.
  85. *
  86. * @param \Illuminate\Database\Query\Builder $query
  87. * @param array $where
  88. * @return string
  89. */
  90. protected function whereMonth(Builder $query, $where)
  91. {
  92. return $this->dateBasedWhere('%m', $query, $where);
  93. }
  94. /**
  95. * Compile a "where year" clause.
  96. *
  97. * @param \Illuminate\Database\Query\Builder $query
  98. * @param array $where
  99. * @return string
  100. */
  101. protected function whereYear(Builder $query, $where)
  102. {
  103. return $this->dateBasedWhere('%Y', $query, $where);
  104. }
  105. /**
  106. * Compile a "where time" clause.
  107. *
  108. * @param \Illuminate\Database\Query\Builder $query
  109. * @param array $where
  110. * @return string
  111. */
  112. protected function whereTime(Builder $query, $where)
  113. {
  114. return $this->dateBasedWhere('%H:%M:%S', $query, $where);
  115. }
  116. /**
  117. * Compile a date based where clause.
  118. *
  119. * @param string $type
  120. * @param \Illuminate\Database\Query\Builder $query
  121. * @param array $where
  122. * @return string
  123. */
  124. protected function dateBasedWhere($type, Builder $query, $where)
  125. {
  126. $value = $this->parameter($where['value']);
  127. return "strftime('{$type}', {$this->wrap($where['column'])}) {$where['operator']} cast({$value} as text)";
  128. }
  129. /**
  130. * Compile an insert statement into SQL.
  131. *
  132. * @param \Illuminate\Database\Query\Builder $query
  133. * @param array $values
  134. * @return string
  135. */
  136. public function compileInsert(Builder $query, array $values)
  137. {
  138. // Essentially we will force every insert to be treated as a batch insert which
  139. // simply makes creating the SQL easier for us since we can utilize the same
  140. // basic routine regardless of an amount of records given to us to insert.
  141. $table = $this->wrapTable($query->from);
  142. if (! is_array(reset($values))) {
  143. $values = [$values];
  144. }
  145. // If there is only one record being inserted, we will just use the usual query
  146. // grammar insert builder because no special syntax is needed for the single
  147. // row inserts in SQLite. However, if there are multiples, we'll continue.
  148. if (count($values) === 1) {
  149. return empty(reset($values))
  150. ? "insert into $table default values"
  151. : parent::compileInsert($query, reset($values));
  152. }
  153. $names = $this->columnize(array_keys(reset($values)));
  154. $columns = [];
  155. // SQLite requires us to build the multi-row insert as a listing of select with
  156. // unions joining them together. So we'll build out this list of columns and
  157. // then join them all together with select unions to complete the queries.
  158. foreach (array_keys(reset($values)) as $column) {
  159. $columns[] = '? as '.$this->wrap($column);
  160. }
  161. $columns = array_fill(0, count($values), implode(', ', $columns));
  162. return "insert into $table ($names) select ".implode(' union all select ', $columns);
  163. }
  164. /**
  165. * Compile an update statement into SQL.
  166. *
  167. * @param \Illuminate\Database\Query\Builder $query
  168. * @param array $values
  169. * @return string
  170. */
  171. public function compileUpdate(Builder $query, $values)
  172. {
  173. $table = $this->wrapTable($query->from);
  174. $columns = collect($values)->map(function ($value, $key) use ($query) {
  175. return $this->wrap(Str::after($key, $query->from.'.')).' = '.$this->parameter($value);
  176. })->implode(', ');
  177. if (isset($query->joins) || isset($query->limit)) {
  178. $selectSql = parent::compileSelect($query->select("{$query->from}.rowid"));
  179. return "update {$table} set $columns where {$this->wrap('rowid')} in ({$selectSql})";
  180. }
  181. return trim("update {$table} set {$columns} {$this->compileWheres($query)}");
  182. }
  183. /**
  184. * Prepare the bindings for an update statement.
  185. *
  186. * @param array $bindings
  187. * @param array $values
  188. * @return array
  189. */
  190. public function prepareBindingsForUpdate(array $bindings, array $values)
  191. {
  192. $cleanBindings = Arr::except($bindings, ['select', 'join']);
  193. return array_values(
  194. array_merge($values, $bindings['join'], Arr::flatten($cleanBindings))
  195. );
  196. }
  197. /**
  198. * Compile a delete statement into SQL.
  199. *
  200. * @param \Illuminate\Database\Query\Builder $query
  201. * @return string
  202. */
  203. public function compileDelete(Builder $query)
  204. {
  205. if (isset($query->joins) || isset($query->limit)) {
  206. $selectSql = parent::compileSelect($query->select("{$query->from}.rowid"));
  207. return "delete from {$this->wrapTable($query->from)} where {$this->wrap('rowid')} in ({$selectSql})";
  208. }
  209. $wheres = is_array($query->wheres) ? $this->compileWheres($query) : '';
  210. return trim("delete from {$this->wrapTable($query->from)} $wheres");
  211. }
  212. /**
  213. * Prepare the bindings for a delete statement.
  214. *
  215. * @param array $bindings
  216. * @return array
  217. */
  218. public function prepareBindingsForDelete(array $bindings)
  219. {
  220. $cleanBindings = Arr::except($bindings, ['select', 'join']);
  221. return array_values(
  222. array_merge($bindings['join'], Arr::flatten($cleanBindings))
  223. );
  224. }
  225. /**
  226. * Compile a truncate table statement into SQL.
  227. *
  228. * @param \Illuminate\Database\Query\Builder $query
  229. * @return array
  230. */
  231. public function compileTruncate(Builder $query)
  232. {
  233. return [
  234. 'delete from sqlite_sequence where name = ?' => [$query->from],
  235. 'delete from '.$this->wrapTable($query->from) => [],
  236. ];
  237. }
  238. }