PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/laravel_tintuc/vendor/laravel/framework/src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php

https://gitlab.com/nmhieucoder/laravel_tintuc
PHP | 532 lines | 225 code | 75 blank | 232 comment | 17 complexity | 415c44912f5832aa71e9075c1708c784 MD5 | raw file
  1. <?php
  2. namespace Illuminate\Database\Query\Grammars;
  3. use Illuminate\Database\Query\Builder;
  4. use Illuminate\Support\Arr;
  5. use Illuminate\Support\Str;
  6. class SqlServerGrammar extends Grammar
  7. {
  8. /**
  9. * All of the available clause operators.
  10. *
  11. * @var string[]
  12. */
  13. protected $operators = [
  14. '=', '<', '>', '<=', '>=', '!<', '!>', '<>', '!=',
  15. 'like', 'not like', 'ilike',
  16. '&', '&=', '|', '|=', '^', '^=',
  17. ];
  18. /**
  19. * Compile a select query into SQL.
  20. *
  21. * @param \Illuminate\Database\Query\Builder $query
  22. * @return string
  23. */
  24. public function compileSelect(Builder $query)
  25. {
  26. if (! $query->offset) {
  27. return parent::compileSelect($query);
  28. }
  29. // If an offset is present on the query, we will need to wrap the query in
  30. // a big "ANSI" offset syntax block. This is very nasty compared to the
  31. // other database systems but is necessary for implementing features.
  32. if (is_null($query->columns)) {
  33. $query->columns = ['*'];
  34. }
  35. return $this->compileAnsiOffset(
  36. $query, $this->compileComponents($query)
  37. );
  38. }
  39. /**
  40. * Compile the "select *" portion of the query.
  41. *
  42. * @param \Illuminate\Database\Query\Builder $query
  43. * @param array $columns
  44. * @return string|null
  45. */
  46. protected function compileColumns(Builder $query, $columns)
  47. {
  48. if (! is_null($query->aggregate)) {
  49. return;
  50. }
  51. $select = $query->distinct ? 'select distinct ' : 'select ';
  52. // If there is a limit on the query, but not an offset, we will add the top
  53. // clause to the query, which serves as a "limit" type clause within the
  54. // SQL Server system similar to the limit keywords available in MySQL.
  55. if (is_numeric($query->limit) && $query->limit > 0 && $query->offset <= 0) {
  56. $select .= 'top '.((int) $query->limit).' ';
  57. }
  58. return $select.$this->columnize($columns);
  59. }
  60. /**
  61. * Compile the "from" portion of the query.
  62. *
  63. * @param \Illuminate\Database\Query\Builder $query
  64. * @param string $table
  65. * @return string
  66. */
  67. protected function compileFrom(Builder $query, $table)
  68. {
  69. $from = parent::compileFrom($query, $table);
  70. if (is_string($query->lock)) {
  71. return $from.' '.$query->lock;
  72. }
  73. if (! is_null($query->lock)) {
  74. return $from.' with(rowlock,'.($query->lock ? 'updlock,' : '').'holdlock)';
  75. }
  76. return $from;
  77. }
  78. /**
  79. * Compile a "where date" clause.
  80. *
  81. * @param \Illuminate\Database\Query\Builder $query
  82. * @param array $where
  83. * @return string
  84. */
  85. protected function whereDate(Builder $query, $where)
  86. {
  87. $value = $this->parameter($where['value']);
  88. return 'cast('.$this->wrap($where['column']).' as date) '.$where['operator'].' '.$value;
  89. }
  90. /**
  91. * Compile a "where time" clause.
  92. *
  93. * @param \Illuminate\Database\Query\Builder $query
  94. * @param array $where
  95. * @return string
  96. */
  97. protected function whereTime(Builder $query, $where)
  98. {
  99. $value = $this->parameter($where['value']);
  100. return 'cast('.$this->wrap($where['column']).' as time) '.$where['operator'].' '.$value;
  101. }
  102. /**
  103. * Compile a "JSON contains" statement into SQL.
  104. *
  105. * @param string $column
  106. * @param string $value
  107. * @return string
  108. */
  109. protected function compileJsonContains($column, $value)
  110. {
  111. [$field, $path] = $this->wrapJsonFieldAndPath($column);
  112. return $value.' in (select [value] from openjson('.$field.$path.'))';
  113. }
  114. /**
  115. * Prepare the binding for a "JSON contains" statement.
  116. *
  117. * @param mixed $binding
  118. * @return string
  119. */
  120. public function prepareBindingForJsonContains($binding)
  121. {
  122. return is_bool($binding) ? json_encode($binding) : $binding;
  123. }
  124. /**
  125. * Compile a "JSON length" statement into SQL.
  126. *
  127. * @param string $column
  128. * @param string $operator
  129. * @param string $value
  130. * @return string
  131. */
  132. protected function compileJsonLength($column, $operator, $value)
  133. {
  134. [$field, $path] = $this->wrapJsonFieldAndPath($column);
  135. return '(select count(*) from openjson('.$field.$path.')) '.$operator.' '.$value;
  136. }
  137. /**
  138. * Create a full ANSI offset clause for the query.
  139. *
  140. * @param \Illuminate\Database\Query\Builder $query
  141. * @param array $components
  142. * @return string
  143. */
  144. protected function compileAnsiOffset(Builder $query, $components)
  145. {
  146. // An ORDER BY clause is required to make this offset query work, so if one does
  147. // not exist we'll just create a dummy clause to trick the database and so it
  148. // does not complain about the queries for not having an "order by" clause.
  149. if (empty($components['orders'])) {
  150. $components['orders'] = 'order by (select 0)';
  151. }
  152. // We need to add the row number to the query so we can compare it to the offset
  153. // and limit values given for the statements. So we will add an expression to
  154. // the "select" that will give back the row numbers on each of the records.
  155. $components['columns'] .= $this->compileOver($components['orders']);
  156. unset($components['orders']);
  157. if ($this->queryOrderContainsSubquery($query)) {
  158. $query->bindings = $this->sortBindingsForSubqueryOrderBy($query);
  159. }
  160. // Next we need to calculate the constraints that should be placed on the query
  161. // to get the right offset and limit from our query but if there is no limit
  162. // set we will just handle the offset only since that is all that matters.
  163. $sql = $this->concatenate($components);
  164. return $this->compileTableExpression($sql, $query);
  165. }
  166. /**
  167. * Compile the over statement for a table expression.
  168. *
  169. * @param string $orderings
  170. * @return string
  171. */
  172. protected function compileOver($orderings)
  173. {
  174. return ", row_number() over ({$orderings}) as row_num";
  175. }
  176. /**
  177. * Determine if the query's order by clauses contain a subquery.
  178. *
  179. * @param \Illuminate\Database\Query\Builder $query
  180. * @return bool
  181. */
  182. protected function queryOrderContainsSubquery($query)
  183. {
  184. if (! is_array($query->orders)) {
  185. return false;
  186. }
  187. return Arr::first($query->orders, function ($value) {
  188. return $this->isExpression($value['column']);
  189. }, false) !== false;
  190. }
  191. /**
  192. * Move the order bindings to be after the "select" statement to account for a order by subquery.
  193. *
  194. * @param \Illuminate\Database\Query\Builder $query
  195. * @return array
  196. */
  197. protected function sortBindingsForSubqueryOrderBy($query)
  198. {
  199. return Arr::sort($query->bindings, function ($bindings, $key) {
  200. return array_search($key, ['select', 'order', 'from', 'join', 'where', 'groupBy', 'having', 'union', 'unionOrder']);
  201. });
  202. }
  203. /**
  204. * Compile a common table expression for a query.
  205. *
  206. * @param string $sql
  207. * @param \Illuminate\Database\Query\Builder $query
  208. * @return string
  209. */
  210. protected function compileTableExpression($sql, $query)
  211. {
  212. $constraint = $this->compileRowConstraint($query);
  213. return "select * from ({$sql}) as temp_table where row_num {$constraint} order by row_num";
  214. }
  215. /**
  216. * Compile the limit / offset row constraint for a query.
  217. *
  218. * @param \Illuminate\Database\Query\Builder $query
  219. * @return string
  220. */
  221. protected function compileRowConstraint($query)
  222. {
  223. $start = (int) $query->offset + 1;
  224. if ($query->limit > 0) {
  225. $finish = (int) $query->offset + (int) $query->limit;
  226. return "between {$start} and {$finish}";
  227. }
  228. return ">= {$start}";
  229. }
  230. /**
  231. * Compile a delete statement without joins into SQL.
  232. *
  233. * @param \Illuminate\Database\Query\Builder $query
  234. * @param string $table
  235. * @param string $where
  236. * @return string
  237. */
  238. protected function compileDeleteWithoutJoins(Builder $query, $table, $where)
  239. {
  240. $sql = parent::compileDeleteWithoutJoins($query, $table, $where);
  241. return ! is_null($query->limit) && $query->limit > 0 && $query->offset <= 0
  242. ? Str::replaceFirst('delete', 'delete top ('.$query->limit.')', $sql)
  243. : $sql;
  244. }
  245. /**
  246. * Compile the random statement into SQL.
  247. *
  248. * @param string $seed
  249. * @return string
  250. */
  251. public function compileRandom($seed)
  252. {
  253. return 'NEWID()';
  254. }
  255. /**
  256. * Compile the "limit" portions of the query.
  257. *
  258. * @param \Illuminate\Database\Query\Builder $query
  259. * @param int $limit
  260. * @return string
  261. */
  262. protected function compileLimit(Builder $query, $limit)
  263. {
  264. return '';
  265. }
  266. /**
  267. * Compile the "offset" portions of the query.
  268. *
  269. * @param \Illuminate\Database\Query\Builder $query
  270. * @param int $offset
  271. * @return string
  272. */
  273. protected function compileOffset(Builder $query, $offset)
  274. {
  275. return '';
  276. }
  277. /**
  278. * Compile the lock into SQL.
  279. *
  280. * @param \Illuminate\Database\Query\Builder $query
  281. * @param bool|string $value
  282. * @return string
  283. */
  284. protected function compileLock(Builder $query, $value)
  285. {
  286. return '';
  287. }
  288. /**
  289. * Wrap a union subquery in parentheses.
  290. *
  291. * @param string $sql
  292. * @return string
  293. */
  294. protected function wrapUnion($sql)
  295. {
  296. return 'select * from ('.$sql.') as '.$this->wrapTable('temp_table');
  297. }
  298. /**
  299. * Compile an exists statement into SQL.
  300. *
  301. * @param \Illuminate\Database\Query\Builder $query
  302. * @return string
  303. */
  304. public function compileExists(Builder $query)
  305. {
  306. $existsQuery = clone $query;
  307. $existsQuery->columns = [];
  308. return $this->compileSelect($existsQuery->selectRaw('1 [exists]')->limit(1));
  309. }
  310. /**
  311. * Compile an update statement with joins into SQL.
  312. *
  313. * @param \Illuminate\Database\Query\Builder $query
  314. * @param string $table
  315. * @param string $columns
  316. * @param string $where
  317. * @return string
  318. */
  319. protected function compileUpdateWithJoins(Builder $query, $table, $columns, $where)
  320. {
  321. $alias = last(explode(' as ', $table));
  322. $joins = $this->compileJoins($query, $query->joins);
  323. return "update {$alias} set {$columns} from {$table} {$joins} {$where}";
  324. }
  325. /**
  326. * Compile an "upsert" statement into SQL.
  327. *
  328. * @param \Illuminate\Database\Query\Builder $query
  329. * @param array $values
  330. * @param array $uniqueBy
  331. * @param array $update
  332. * @return string
  333. */
  334. public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
  335. {
  336. $columns = $this->columnize(array_keys(reset($values)));
  337. $sql = 'merge '.$this->wrapTable($query->from).' ';
  338. $parameters = collect($values)->map(function ($record) {
  339. return '('.$this->parameterize($record).')';
  340. })->implode(', ');
  341. $sql .= 'using (values '.$parameters.') '.$this->wrapTable('laravel_source').' ('.$columns.') ';
  342. $on = collect($uniqueBy)->map(function ($column) use ($query) {
  343. return $this->wrap('laravel_source.'.$column).' = '.$this->wrap($query->from.'.'.$column);
  344. })->implode(' and ');
  345. $sql .= 'on '.$on.' ';
  346. if ($update) {
  347. $update = collect($update)->map(function ($value, $key) {
  348. return is_numeric($key)
  349. ? $this->wrap($value).' = '.$this->wrap('laravel_source.'.$value)
  350. : $this->wrap($key).' = '.$this->parameter($value);
  351. })->implode(', ');
  352. $sql .= 'when matched then update set '.$update.' ';
  353. }
  354. $sql .= 'when not matched then insert ('.$columns.') values ('.$columns.');';
  355. return $sql;
  356. }
  357. /**
  358. * Prepare the bindings for an update statement.
  359. *
  360. * @param array $bindings
  361. * @param array $values
  362. * @return array
  363. */
  364. public function prepareBindingsForUpdate(array $bindings, array $values)
  365. {
  366. $cleanBindings = Arr::except($bindings, 'select');
  367. return array_values(
  368. array_merge($values, Arr::flatten($cleanBindings))
  369. );
  370. }
  371. /**
  372. * Compile the SQL statement to define a savepoint.
  373. *
  374. * @param string $name
  375. * @return string
  376. */
  377. public function compileSavepoint($name)
  378. {
  379. return 'SAVE TRANSACTION '.$name;
  380. }
  381. /**
  382. * Compile the SQL statement to execute a savepoint rollback.
  383. *
  384. * @param string $name
  385. * @return string
  386. */
  387. public function compileSavepointRollBack($name)
  388. {
  389. return 'ROLLBACK TRANSACTION '.$name;
  390. }
  391. /**
  392. * Get the format for database stored dates.
  393. *
  394. * @return string
  395. */
  396. public function getDateFormat()
  397. {
  398. return 'Y-m-d H:i:s.v';
  399. }
  400. /**
  401. * Wrap a single string in keyword identifiers.
  402. *
  403. * @param string $value
  404. * @return string
  405. */
  406. protected function wrapValue($value)
  407. {
  408. return $value === '*' ? $value : '['.str_replace(']', ']]', $value).']';
  409. }
  410. /**
  411. * Wrap the given JSON selector.
  412. *
  413. * @param string $value
  414. * @return string
  415. */
  416. protected function wrapJsonSelector($value)
  417. {
  418. [$field, $path] = $this->wrapJsonFieldAndPath($value);
  419. return 'json_value('.$field.$path.')';
  420. }
  421. /**
  422. * Wrap the given JSON boolean value.
  423. *
  424. * @param string $value
  425. * @return string
  426. */
  427. protected function wrapJsonBooleanValue($value)
  428. {
  429. return "'".$value."'";
  430. }
  431. /**
  432. * Wrap a table in keyword identifiers.
  433. *
  434. * @param \Illuminate\Database\Query\Expression|string $table
  435. * @return string
  436. */
  437. public function wrapTable($table)
  438. {
  439. if (! $this->isExpression($table)) {
  440. return $this->wrapTableValuedFunction(parent::wrapTable($table));
  441. }
  442. return $this->getValue($table);
  443. }
  444. /**
  445. * Wrap a table in keyword identifiers.
  446. *
  447. * @param string $table
  448. * @return string
  449. */
  450. protected function wrapTableValuedFunction($table)
  451. {
  452. if (preg_match('/^(.+?)(\(.*?\))]$/', $table, $matches) === 1) {
  453. $table = $matches[1].']'.$matches[2];
  454. }
  455. return $table;
  456. }
  457. }