PostgresGrammar.php 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205
  1. <?php
  2. namespace Illuminate\Database\Schema\Grammars;
  3. use Illuminate\Database\Connection;
  4. use Illuminate\Database\Query\Expression;
  5. use Illuminate\Database\Schema\Blueprint;
  6. use Illuminate\Support\Fluent;
  7. use LogicException;
  8. class PostgresGrammar extends Grammar
  9. {
  10. /**
  11. * If this Grammar supports schema changes wrapped in a transaction.
  12. *
  13. * @var bool
  14. */
  15. protected $transactions = true;
  16. /**
  17. * The possible column modifiers.
  18. *
  19. * @var string[]
  20. */
  21. protected $modifiers = ['Collate', 'Nullable', 'Default', 'VirtualAs', 'StoredAs', 'GeneratedAs', 'Increment'];
  22. /**
  23. * The columns available as serials.
  24. *
  25. * @var string[]
  26. */
  27. protected $serials = ['bigInteger', 'integer', 'mediumInteger', 'smallInteger', 'tinyInteger'];
  28. /**
  29. * The commands to be executed outside of create or alter command.
  30. *
  31. * @var string[]
  32. */
  33. protected $fluentCommands = ['AutoIncrementStartingValues', 'Comment'];
  34. /**
  35. * Compile a create database command.
  36. *
  37. * @param string $name
  38. * @param \Illuminate\Database\Connection $connection
  39. * @return string
  40. */
  41. public function compileCreateDatabase($name, $connection)
  42. {
  43. return sprintf(
  44. 'create database %s encoding %s',
  45. $this->wrapValue($name),
  46. $this->wrapValue($connection->getConfig('charset')),
  47. );
  48. }
  49. /**
  50. * Compile a drop database if exists command.
  51. *
  52. * @param string $name
  53. * @return string
  54. */
  55. public function compileDropDatabaseIfExists($name)
  56. {
  57. return sprintf(
  58. 'drop database if exists %s',
  59. $this->wrapValue($name)
  60. );
  61. }
  62. /**
  63. * Compile the query to determine the tables.
  64. *
  65. * @return string
  66. */
  67. public function compileTables()
  68. {
  69. return 'select c.relname as name, n.nspname as schema, pg_total_relation_size(c.oid) as size, '
  70. ."obj_description(c.oid, 'pg_class') as comment from pg_class c, pg_namespace n "
  71. ."where c.relkind in ('r', 'p') and n.oid = c.relnamespace and n.nspname not in ('pg_catalog', 'information_schema') "
  72. .'order by c.relname';
  73. }
  74. /**
  75. * Compile the query to determine the views.
  76. *
  77. * @return string
  78. */
  79. public function compileViews()
  80. {
  81. return "select viewname as name, schemaname as schema, definition from pg_views where schemaname not in ('pg_catalog', 'information_schema') order by viewname";
  82. }
  83. /**
  84. * Compile the query to determine the user-defined types.
  85. *
  86. * @return string
  87. */
  88. public function compileTypes()
  89. {
  90. return 'select t.typname as name, n.nspname as schema, t.typtype as type, t.typcategory as category, '
  91. ."((t.typinput = 'array_in'::regproc and t.typoutput = 'array_out'::regproc) or t.typtype = 'm') as implicit "
  92. .'from pg_type t join pg_namespace n on n.oid = t.typnamespace '
  93. .'left join pg_class c on c.oid = t.typrelid '
  94. .'left join pg_type el on el.oid = t.typelem '
  95. .'left join pg_class ce on ce.oid = el.typrelid '
  96. ."where ((t.typrelid = 0 and (ce.relkind = 'c' or ce.relkind is null)) or c.relkind = 'c') "
  97. ."and not exists (select 1 from pg_depend d where d.objid in (t.oid, t.typelem) and d.deptype = 'e') "
  98. ."and n.nspname not in ('pg_catalog', 'information_schema')";
  99. }
  100. /**
  101. * Compile the query to determine the columns.
  102. *
  103. * @param string $schema
  104. * @param string $table
  105. * @return string
  106. */
  107. public function compileColumns($schema, $table)
  108. {
  109. return sprintf(
  110. 'select a.attname as name, t.typname as type_name, format_type(a.atttypid, a.atttypmod) as type, '
  111. .'(select tc.collcollate from pg_catalog.pg_collation tc where tc.oid = a.attcollation) as collation, '
  112. .'not a.attnotnull as nullable, '
  113. .'(select pg_get_expr(adbin, adrelid) from pg_attrdef where c.oid = pg_attrdef.adrelid and pg_attrdef.adnum = a.attnum) as default, '
  114. .(version_compare($this->connection?->getServerVersion(), '12.0', '<') ? "'' as generated, " : 'a.attgenerated as generated, ')
  115. .'col_description(c.oid, a.attnum) as comment '
  116. .'from pg_attribute a, pg_class c, pg_type t, pg_namespace n '
  117. .'where c.relname = %s and n.nspname = %s and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid and n.oid = c.relnamespace '
  118. .'order by a.attnum',
  119. $this->quoteString($table),
  120. $this->quoteString($schema)
  121. );
  122. }
  123. /**
  124. * Compile the query to determine the indexes.
  125. *
  126. * @param string $schema
  127. * @param string $table
  128. * @return string
  129. */
  130. public function compileIndexes($schema, $table)
  131. {
  132. return sprintf(
  133. "select ic.relname as name, string_agg(a.attname, ',' order by indseq.ord) as columns, "
  134. .'am.amname as "type", i.indisunique as "unique", i.indisprimary as "primary" '
  135. .'from pg_index i '
  136. .'join pg_class tc on tc.oid = i.indrelid '
  137. .'join pg_namespace tn on tn.oid = tc.relnamespace '
  138. .'join pg_class ic on ic.oid = i.indexrelid '
  139. .'join pg_am am on am.oid = ic.relam '
  140. .'join lateral unnest(i.indkey) with ordinality as indseq(num, ord) on true '
  141. .'left join pg_attribute a on a.attrelid = i.indrelid and a.attnum = indseq.num '
  142. .'where tc.relname = %s and tn.nspname = %s '
  143. .'group by ic.relname, am.amname, i.indisunique, i.indisprimary',
  144. $this->quoteString($table),
  145. $this->quoteString($schema)
  146. );
  147. }
  148. /**
  149. * Compile the query to determine the foreign keys.
  150. *
  151. * @param string $schema
  152. * @param string $table
  153. * @return string
  154. */
  155. public function compileForeignKeys($schema, $table)
  156. {
  157. return sprintf(
  158. 'select c.conname as name, '
  159. ."string_agg(la.attname, ',' order by conseq.ord) as columns, "
  160. .'fn.nspname as foreign_schema, fc.relname as foreign_table, '
  161. ."string_agg(fa.attname, ',' order by conseq.ord) as foreign_columns, "
  162. .'c.confupdtype as on_update, c.confdeltype as on_delete '
  163. .'from pg_constraint c '
  164. .'join pg_class tc on c.conrelid = tc.oid '
  165. .'join pg_namespace tn on tn.oid = tc.relnamespace '
  166. .'join pg_class fc on c.confrelid = fc.oid '
  167. .'join pg_namespace fn on fn.oid = fc.relnamespace '
  168. .'join lateral unnest(c.conkey) with ordinality as conseq(num, ord) on true '
  169. .'join pg_attribute la on la.attrelid = c.conrelid and la.attnum = conseq.num '
  170. .'join pg_attribute fa on fa.attrelid = c.confrelid and fa.attnum = c.confkey[conseq.ord] '
  171. ."where c.contype = 'f' and tc.relname = %s and tn.nspname = %s "
  172. .'group by c.conname, fn.nspname, fc.relname, c.confupdtype, c.confdeltype',
  173. $this->quoteString($table),
  174. $this->quoteString($schema)
  175. );
  176. }
  177. /**
  178. * Compile a create table command.
  179. *
  180. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  181. * @param \Illuminate\Support\Fluent $command
  182. * @return string
  183. */
  184. public function compileCreate(Blueprint $blueprint, Fluent $command)
  185. {
  186. return sprintf('%s table %s (%s)',
  187. $blueprint->temporary ? 'create temporary' : 'create',
  188. $this->wrapTable($blueprint),
  189. implode(', ', $this->getColumns($blueprint))
  190. );
  191. }
  192. /**
  193. * Compile a column addition command.
  194. *
  195. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  196. * @param \Illuminate\Support\Fluent $command
  197. * @return string
  198. */
  199. public function compileAdd(Blueprint $blueprint, Fluent $command)
  200. {
  201. return sprintf('alter table %s %s',
  202. $this->wrapTable($blueprint),
  203. implode(', ', $this->prefixArray('add column', $this->getColumns($blueprint)))
  204. );
  205. }
  206. /**
  207. * Compile the auto-incrementing column starting values.
  208. *
  209. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  210. * @param \Illuminate\Support\Fluent $command
  211. * @return string
  212. */
  213. public function compileAutoIncrementStartingValues(Blueprint $blueprint, Fluent $command)
  214. {
  215. if ($command->column->autoIncrement
  216. && $value = $command->column->get('startingValue', $command->column->get('from'))) {
  217. $table = last(explode('.', $blueprint->getTable()));
  218. return 'alter sequence '.$blueprint->getPrefix().$table.'_'.$command->column->name.'_seq restart with '.$value;
  219. }
  220. }
  221. /**
  222. * Compile a change column command into a series of SQL statements.
  223. *
  224. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  225. * @param \Illuminate\Support\Fluent $command
  226. * @param \Illuminate\Database\Connection $connection
  227. * @return array|string
  228. *
  229. * @throws \RuntimeException
  230. */
  231. public function compileChange(Blueprint $blueprint, Fluent $command, Connection $connection)
  232. {
  233. $columns = [];
  234. foreach ($blueprint->getChangedColumns() as $column) {
  235. $changes = ['type '.$this->getType($column).$this->modifyCollate($blueprint, $column)];
  236. foreach ($this->modifiers as $modifier) {
  237. if ($modifier === 'Collate') {
  238. continue;
  239. }
  240. if (method_exists($this, $method = "modify{$modifier}")) {
  241. $constraints = (array) $this->{$method}($blueprint, $column);
  242. foreach ($constraints as $constraint) {
  243. $changes[] = $constraint;
  244. }
  245. }
  246. }
  247. $columns[] = implode(', ', $this->prefixArray('alter column '.$this->wrap($column), $changes));
  248. }
  249. return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns);
  250. }
  251. /**
  252. * Compile a primary key command.
  253. *
  254. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  255. * @param \Illuminate\Support\Fluent $command
  256. * @return string
  257. */
  258. public function compilePrimary(Blueprint $blueprint, Fluent $command)
  259. {
  260. $columns = $this->columnize($command->columns);
  261. return 'alter table '.$this->wrapTable($blueprint)." add primary key ({$columns})";
  262. }
  263. /**
  264. * Compile a unique key command.
  265. *
  266. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  267. * @param \Illuminate\Support\Fluent $command
  268. * @return string
  269. */
  270. public function compileUnique(Blueprint $blueprint, Fluent $command)
  271. {
  272. $sql = sprintf('alter table %s add constraint %s unique (%s)',
  273. $this->wrapTable($blueprint),
  274. $this->wrap($command->index),
  275. $this->columnize($command->columns)
  276. );
  277. if (! is_null($command->deferrable)) {
  278. $sql .= $command->deferrable ? ' deferrable' : ' not deferrable';
  279. }
  280. if ($command->deferrable && ! is_null($command->initiallyImmediate)) {
  281. $sql .= $command->initiallyImmediate ? ' initially immediate' : ' initially deferred';
  282. }
  283. return $sql;
  284. }
  285. /**
  286. * Compile a plain index key command.
  287. *
  288. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  289. * @param \Illuminate\Support\Fluent $command
  290. * @return string
  291. */
  292. public function compileIndex(Blueprint $blueprint, Fluent $command)
  293. {
  294. return sprintf('create index %s on %s%s (%s)',
  295. $this->wrap($command->index),
  296. $this->wrapTable($blueprint),
  297. $command->algorithm ? ' using '.$command->algorithm : '',
  298. $this->columnize($command->columns)
  299. );
  300. }
  301. /**
  302. * Compile a fulltext index key command.
  303. *
  304. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  305. * @param \Illuminate\Support\Fluent $command
  306. * @return string
  307. *
  308. * @throws \RuntimeException
  309. */
  310. public function compileFulltext(Blueprint $blueprint, Fluent $command)
  311. {
  312. $language = $command->language ?: 'english';
  313. $columns = array_map(function ($column) use ($language) {
  314. return "to_tsvector({$this->quoteString($language)}, {$this->wrap($column)})";
  315. }, $command->columns);
  316. return sprintf('create index %s on %s using gin ((%s))',
  317. $this->wrap($command->index),
  318. $this->wrapTable($blueprint),
  319. implode(' || ', $columns)
  320. );
  321. }
  322. /**
  323. * Compile a spatial index key command.
  324. *
  325. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  326. * @param \Illuminate\Support\Fluent $command
  327. * @return string
  328. */
  329. public function compileSpatialIndex(Blueprint $blueprint, Fluent $command)
  330. {
  331. $command->algorithm = 'gist';
  332. return $this->compileIndex($blueprint, $command);
  333. }
  334. /**
  335. * Compile a foreign key command.
  336. *
  337. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  338. * @param \Illuminate\Support\Fluent $command
  339. * @return string
  340. */
  341. public function compileForeign(Blueprint $blueprint, Fluent $command)
  342. {
  343. $sql = parent::compileForeign($blueprint, $command);
  344. if (! is_null($command->deferrable)) {
  345. $sql .= $command->deferrable ? ' deferrable' : ' not deferrable';
  346. }
  347. if ($command->deferrable && ! is_null($command->initiallyImmediate)) {
  348. $sql .= $command->initiallyImmediate ? ' initially immediate' : ' initially deferred';
  349. }
  350. if (! is_null($command->notValid)) {
  351. $sql .= ' not valid';
  352. }
  353. return $sql;
  354. }
  355. /**
  356. * Compile a drop table command.
  357. *
  358. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  359. * @param \Illuminate\Support\Fluent $command
  360. * @return string
  361. */
  362. public function compileDrop(Blueprint $blueprint, Fluent $command)
  363. {
  364. return 'drop table '.$this->wrapTable($blueprint);
  365. }
  366. /**
  367. * Compile a drop table (if exists) command.
  368. *
  369. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  370. * @param \Illuminate\Support\Fluent $command
  371. * @return string
  372. */
  373. public function compileDropIfExists(Blueprint $blueprint, Fluent $command)
  374. {
  375. return 'drop table if exists '.$this->wrapTable($blueprint);
  376. }
  377. /**
  378. * Compile the SQL needed to drop all tables.
  379. *
  380. * @param array $tables
  381. * @return string
  382. */
  383. public function compileDropAllTables($tables)
  384. {
  385. return 'drop table '.implode(',', $this->escapeNames($tables)).' cascade';
  386. }
  387. /**
  388. * Compile the SQL needed to drop all views.
  389. *
  390. * @param array $views
  391. * @return string
  392. */
  393. public function compileDropAllViews($views)
  394. {
  395. return 'drop view '.implode(',', $this->escapeNames($views)).' cascade';
  396. }
  397. /**
  398. * Compile the SQL needed to drop all types.
  399. *
  400. * @param array $types
  401. * @return string
  402. */
  403. public function compileDropAllTypes($types)
  404. {
  405. return 'drop type '.implode(',', $this->escapeNames($types)).' cascade';
  406. }
  407. /**
  408. * Compile the SQL needed to drop all domains.
  409. *
  410. * @param array $domains
  411. * @return string
  412. */
  413. public function compileDropAllDomains($domains)
  414. {
  415. return 'drop domain '.implode(',', $this->escapeNames($domains)).' cascade';
  416. }
  417. /**
  418. * Compile a drop column command.
  419. *
  420. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  421. * @param \Illuminate\Support\Fluent $command
  422. * @return string
  423. */
  424. public function compileDropColumn(Blueprint $blueprint, Fluent $command)
  425. {
  426. $columns = $this->prefixArray('drop column', $this->wrapArray($command->columns));
  427. return 'alter table '.$this->wrapTable($blueprint).' '.implode(', ', $columns);
  428. }
  429. /**
  430. * Compile a drop primary key command.
  431. *
  432. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  433. * @param \Illuminate\Support\Fluent $command
  434. * @return string
  435. */
  436. public function compileDropPrimary(Blueprint $blueprint, Fluent $command)
  437. {
  438. $table = last(explode('.', $blueprint->getTable()));
  439. $index = $this->wrap("{$blueprint->getPrefix()}{$table}_pkey");
  440. return 'alter table '.$this->wrapTable($blueprint)." drop constraint {$index}";
  441. }
  442. /**
  443. * Compile a drop unique key command.
  444. *
  445. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  446. * @param \Illuminate\Support\Fluent $command
  447. * @return string
  448. */
  449. public function compileDropUnique(Blueprint $blueprint, Fluent $command)
  450. {
  451. $index = $this->wrap($command->index);
  452. return "alter table {$this->wrapTable($blueprint)} drop constraint {$index}";
  453. }
  454. /**
  455. * Compile a drop index command.
  456. *
  457. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  458. * @param \Illuminate\Support\Fluent $command
  459. * @return string
  460. */
  461. public function compileDropIndex(Blueprint $blueprint, Fluent $command)
  462. {
  463. return "drop index {$this->wrap($command->index)}";
  464. }
  465. /**
  466. * Compile a drop fulltext index command.
  467. *
  468. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  469. * @param \Illuminate\Support\Fluent $command
  470. * @return string
  471. */
  472. public function compileDropFullText(Blueprint $blueprint, Fluent $command)
  473. {
  474. return $this->compileDropIndex($blueprint, $command);
  475. }
  476. /**
  477. * Compile a drop spatial index command.
  478. *
  479. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  480. * @param \Illuminate\Support\Fluent $command
  481. * @return string
  482. */
  483. public function compileDropSpatialIndex(Blueprint $blueprint, Fluent $command)
  484. {
  485. return $this->compileDropIndex($blueprint, $command);
  486. }
  487. /**
  488. * Compile a drop foreign key command.
  489. *
  490. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  491. * @param \Illuminate\Support\Fluent $command
  492. * @return string
  493. */
  494. public function compileDropForeign(Blueprint $blueprint, Fluent $command)
  495. {
  496. $index = $this->wrap($command->index);
  497. return "alter table {$this->wrapTable($blueprint)} drop constraint {$index}";
  498. }
  499. /**
  500. * Compile a rename table command.
  501. *
  502. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  503. * @param \Illuminate\Support\Fluent $command
  504. * @return string
  505. */
  506. public function compileRename(Blueprint $blueprint, Fluent $command)
  507. {
  508. $from = $this->wrapTable($blueprint);
  509. return "alter table {$from} rename to ".$this->wrapTable($command->to);
  510. }
  511. /**
  512. * Compile a rename index command.
  513. *
  514. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  515. * @param \Illuminate\Support\Fluent $command
  516. * @return string
  517. */
  518. public function compileRenameIndex(Blueprint $blueprint, Fluent $command)
  519. {
  520. return sprintf('alter index %s rename to %s',
  521. $this->wrap($command->from),
  522. $this->wrap($command->to)
  523. );
  524. }
  525. /**
  526. * Compile the command to enable foreign key constraints.
  527. *
  528. * @return string
  529. */
  530. public function compileEnableForeignKeyConstraints()
  531. {
  532. return 'SET CONSTRAINTS ALL IMMEDIATE;';
  533. }
  534. /**
  535. * Compile the command to disable foreign key constraints.
  536. *
  537. * @return string
  538. */
  539. public function compileDisableForeignKeyConstraints()
  540. {
  541. return 'SET CONSTRAINTS ALL DEFERRED;';
  542. }
  543. /**
  544. * Compile a comment command.
  545. *
  546. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  547. * @param \Illuminate\Support\Fluent $command
  548. * @return string
  549. */
  550. public function compileComment(Blueprint $blueprint, Fluent $command)
  551. {
  552. if (! is_null($comment = $command->column->comment) || $command->column->change) {
  553. return sprintf('comment on column %s.%s is %s',
  554. $this->wrapTable($blueprint),
  555. $this->wrap($command->column->name),
  556. is_null($comment) ? 'NULL' : "'".str_replace("'", "''", $comment)."'"
  557. );
  558. }
  559. }
  560. /**
  561. * Compile a table comment command.
  562. *
  563. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  564. * @param \Illuminate\Support\Fluent $command
  565. * @return string
  566. */
  567. public function compileTableComment(Blueprint $blueprint, Fluent $command)
  568. {
  569. return sprintf('comment on table %s is %s',
  570. $this->wrapTable($blueprint),
  571. "'".str_replace("'", "''", $command->comment)."'"
  572. );
  573. }
  574. /**
  575. * Quote-escape the given tables, views, or types.
  576. *
  577. * @param array $names
  578. * @return array
  579. */
  580. public function escapeNames($names)
  581. {
  582. return array_map(static function ($name) {
  583. return '"'.collect(explode('.', $name))
  584. ->map(fn ($segment) => trim($segment, '\'"'))
  585. ->implode('"."').'"';
  586. }, $names);
  587. }
  588. /**
  589. * Create the column definition for a char type.
  590. *
  591. * @param \Illuminate\Support\Fluent $column
  592. * @return string
  593. */
  594. protected function typeChar(Fluent $column)
  595. {
  596. if ($column->length) {
  597. return "char({$column->length})";
  598. }
  599. return 'char';
  600. }
  601. /**
  602. * Create the column definition for a string type.
  603. *
  604. * @param \Illuminate\Support\Fluent $column
  605. * @return string
  606. */
  607. protected function typeString(Fluent $column)
  608. {
  609. if ($column->length) {
  610. return "varchar({$column->length})";
  611. }
  612. return 'varchar';
  613. }
  614. /**
  615. * Create the column definition for a tiny text type.
  616. *
  617. * @param \Illuminate\Support\Fluent $column
  618. * @return string
  619. */
  620. protected function typeTinyText(Fluent $column)
  621. {
  622. return 'varchar(255)';
  623. }
  624. /**
  625. * Create the column definition for a text type.
  626. *
  627. * @param \Illuminate\Support\Fluent $column
  628. * @return string
  629. */
  630. protected function typeText(Fluent $column)
  631. {
  632. return 'text';
  633. }
  634. /**
  635. * Create the column definition for a medium text type.
  636. *
  637. * @param \Illuminate\Support\Fluent $column
  638. * @return string
  639. */
  640. protected function typeMediumText(Fluent $column)
  641. {
  642. return 'text';
  643. }
  644. /**
  645. * Create the column definition for a long text type.
  646. *
  647. * @param \Illuminate\Support\Fluent $column
  648. * @return string
  649. */
  650. protected function typeLongText(Fluent $column)
  651. {
  652. return 'text';
  653. }
  654. /**
  655. * Create the column definition for an integer type.
  656. *
  657. * @param \Illuminate\Support\Fluent $column
  658. * @return string
  659. */
  660. protected function typeInteger(Fluent $column)
  661. {
  662. return $column->autoIncrement && is_null($column->generatedAs) && ! $column->change ? 'serial' : 'integer';
  663. }
  664. /**
  665. * Create the column definition for a big integer type.
  666. *
  667. * @param \Illuminate\Support\Fluent $column
  668. * @return string
  669. */
  670. protected function typeBigInteger(Fluent $column)
  671. {
  672. return $column->autoIncrement && is_null($column->generatedAs) && ! $column->change ? 'bigserial' : 'bigint';
  673. }
  674. /**
  675. * Create the column definition for a medium integer type.
  676. *
  677. * @param \Illuminate\Support\Fluent $column
  678. * @return string
  679. */
  680. protected function typeMediumInteger(Fluent $column)
  681. {
  682. return $this->typeInteger($column);
  683. }
  684. /**
  685. * Create the column definition for a tiny integer type.
  686. *
  687. * @param \Illuminate\Support\Fluent $column
  688. * @return string
  689. */
  690. protected function typeTinyInteger(Fluent $column)
  691. {
  692. return $this->typeSmallInteger($column);
  693. }
  694. /**
  695. * Create the column definition for a small integer type.
  696. *
  697. * @param \Illuminate\Support\Fluent $column
  698. * @return string
  699. */
  700. protected function typeSmallInteger(Fluent $column)
  701. {
  702. return $column->autoIncrement && is_null($column->generatedAs) && ! $column->change ? 'smallserial' : 'smallint';
  703. }
  704. /**
  705. * Create the column definition for a float type.
  706. *
  707. * @param \Illuminate\Support\Fluent $column
  708. * @return string
  709. */
  710. protected function typeFloat(Fluent $column)
  711. {
  712. if ($column->precision) {
  713. return "float({$column->precision})";
  714. }
  715. return 'float';
  716. }
  717. /**
  718. * Create the column definition for a double type.
  719. *
  720. * @param \Illuminate\Support\Fluent $column
  721. * @return string
  722. */
  723. protected function typeDouble(Fluent $column)
  724. {
  725. return 'double precision';
  726. }
  727. /**
  728. * Create the column definition for a real type.
  729. *
  730. * @param \Illuminate\Support\Fluent $column
  731. * @return string
  732. */
  733. protected function typeReal(Fluent $column)
  734. {
  735. return 'real';
  736. }
  737. /**
  738. * Create the column definition for a decimal type.
  739. *
  740. * @param \Illuminate\Support\Fluent $column
  741. * @return string
  742. */
  743. protected function typeDecimal(Fluent $column)
  744. {
  745. return "decimal({$column->total}, {$column->places})";
  746. }
  747. /**
  748. * Create the column definition for a boolean type.
  749. *
  750. * @param \Illuminate\Support\Fluent $column
  751. * @return string
  752. */
  753. protected function typeBoolean(Fluent $column)
  754. {
  755. return 'boolean';
  756. }
  757. /**
  758. * Create the column definition for an enumeration type.
  759. *
  760. * @param \Illuminate\Support\Fluent $column
  761. * @return string
  762. */
  763. protected function typeEnum(Fluent $column)
  764. {
  765. return sprintf(
  766. 'varchar(255) check ("%s" in (%s))',
  767. $column->name,
  768. $this->quoteString($column->allowed)
  769. );
  770. }
  771. /**
  772. * Create the column definition for a json type.
  773. *
  774. * @param \Illuminate\Support\Fluent $column
  775. * @return string
  776. */
  777. protected function typeJson(Fluent $column)
  778. {
  779. return 'json';
  780. }
  781. /**
  782. * Create the column definition for a jsonb type.
  783. *
  784. * @param \Illuminate\Support\Fluent $column
  785. * @return string
  786. */
  787. protected function typeJsonb(Fluent $column)
  788. {
  789. return 'jsonb';
  790. }
  791. /**
  792. * Create the column definition for a date type.
  793. *
  794. * @param \Illuminate\Support\Fluent $column
  795. * @return string
  796. */
  797. protected function typeDate(Fluent $column)
  798. {
  799. return 'date';
  800. }
  801. /**
  802. * Create the column definition for a date-time type.
  803. *
  804. * @param \Illuminate\Support\Fluent $column
  805. * @return string
  806. */
  807. protected function typeDateTime(Fluent $column)
  808. {
  809. return $this->typeTimestamp($column);
  810. }
  811. /**
  812. * Create the column definition for a date-time (with time zone) type.
  813. *
  814. * @param \Illuminate\Support\Fluent $column
  815. * @return string
  816. */
  817. protected function typeDateTimeTz(Fluent $column)
  818. {
  819. return $this->typeTimestampTz($column);
  820. }
  821. /**
  822. * Create the column definition for a time type.
  823. *
  824. * @param \Illuminate\Support\Fluent $column
  825. * @return string
  826. */
  827. protected function typeTime(Fluent $column)
  828. {
  829. return 'time'.(is_null($column->precision) ? '' : "($column->precision)").' without time zone';
  830. }
  831. /**
  832. * Create the column definition for a time (with time zone) type.
  833. *
  834. * @param \Illuminate\Support\Fluent $column
  835. * @return string
  836. */
  837. protected function typeTimeTz(Fluent $column)
  838. {
  839. return 'time'.(is_null($column->precision) ? '' : "($column->precision)").' with time zone';
  840. }
  841. /**
  842. * Create the column definition for a timestamp type.
  843. *
  844. * @param \Illuminate\Support\Fluent $column
  845. * @return string
  846. */
  847. protected function typeTimestamp(Fluent $column)
  848. {
  849. if ($column->useCurrent) {
  850. $column->default(new Expression('CURRENT_TIMESTAMP'));
  851. }
  852. return 'timestamp'.(is_null($column->precision) ? '' : "($column->precision)").' without time zone';
  853. }
  854. /**
  855. * Create the column definition for a timestamp (with time zone) type.
  856. *
  857. * @param \Illuminate\Support\Fluent $column
  858. * @return string
  859. */
  860. protected function typeTimestampTz(Fluent $column)
  861. {
  862. if ($column->useCurrent) {
  863. $column->default(new Expression('CURRENT_TIMESTAMP'));
  864. }
  865. return 'timestamp'.(is_null($column->precision) ? '' : "($column->precision)").' with time zone';
  866. }
  867. /**
  868. * Create the column definition for a year type.
  869. *
  870. * @param \Illuminate\Support\Fluent $column
  871. * @return string
  872. */
  873. protected function typeYear(Fluent $column)
  874. {
  875. return $this->typeInteger($column);
  876. }
  877. /**
  878. * Create the column definition for a binary type.
  879. *
  880. * @param \Illuminate\Support\Fluent $column
  881. * @return string
  882. */
  883. protected function typeBinary(Fluent $column)
  884. {
  885. return 'bytea';
  886. }
  887. /**
  888. * Create the column definition for a uuid type.
  889. *
  890. * @param \Illuminate\Support\Fluent $column
  891. * @return string
  892. */
  893. protected function typeUuid(Fluent $column)
  894. {
  895. return 'uuid';
  896. }
  897. /**
  898. * Create the column definition for an IP address type.
  899. *
  900. * @param \Illuminate\Support\Fluent $column
  901. * @return string
  902. */
  903. protected function typeIpAddress(Fluent $column)
  904. {
  905. return 'inet';
  906. }
  907. /**
  908. * Create the column definition for a MAC address type.
  909. *
  910. * @param \Illuminate\Support\Fluent $column
  911. * @return string
  912. */
  913. protected function typeMacAddress(Fluent $column)
  914. {
  915. return 'macaddr';
  916. }
  917. /**
  918. * Create the column definition for a spatial Geometry type.
  919. *
  920. * @param \Illuminate\Support\Fluent $column
  921. * @return string
  922. */
  923. protected function typeGeometry(Fluent $column)
  924. {
  925. if ($column->subtype) {
  926. return sprintf('geometry(%s%s)',
  927. strtolower($column->subtype),
  928. $column->srid ? ','.$column->srid : ''
  929. );
  930. }
  931. return 'geometry';
  932. }
  933. /**
  934. * Create the column definition for a spatial Geography type.
  935. *
  936. * @param \Illuminate\Support\Fluent $column
  937. * @return string
  938. */
  939. protected function typeGeography(Fluent $column)
  940. {
  941. if ($column->subtype) {
  942. return sprintf('geography(%s%s)',
  943. strtolower($column->subtype),
  944. $column->srid ? ','.$column->srid : ''
  945. );
  946. }
  947. return 'geography';
  948. }
  949. /**
  950. * Get the SQL for a collation column modifier.
  951. *
  952. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  953. * @param \Illuminate\Support\Fluent $column
  954. * @return string|null
  955. */
  956. protected function modifyCollate(Blueprint $blueprint, Fluent $column)
  957. {
  958. if (! is_null($column->collation)) {
  959. return ' collate '.$this->wrapValue($column->collation);
  960. }
  961. }
  962. /**
  963. * Get the SQL for a nullable column modifier.
  964. *
  965. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  966. * @param \Illuminate\Support\Fluent $column
  967. * @return string|null
  968. */
  969. protected function modifyNullable(Blueprint $blueprint, Fluent $column)
  970. {
  971. if ($column->change) {
  972. return $column->nullable ? 'drop not null' : 'set not null';
  973. }
  974. return $column->nullable ? ' null' : ' not null';
  975. }
  976. /**
  977. * Get the SQL for a default column modifier.
  978. *
  979. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  980. * @param \Illuminate\Support\Fluent $column
  981. * @return string|null
  982. */
  983. protected function modifyDefault(Blueprint $blueprint, Fluent $column)
  984. {
  985. if ($column->change) {
  986. if (! $column->autoIncrement || ! is_null($column->generatedAs)) {
  987. return is_null($column->default) ? 'drop default' : 'set default '.$this->getDefaultValue($column->default);
  988. }
  989. return null;
  990. }
  991. if (! is_null($column->default)) {
  992. return ' default '.$this->getDefaultValue($column->default);
  993. }
  994. }
  995. /**
  996. * Get the SQL for an auto-increment column modifier.
  997. *
  998. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  999. * @param \Illuminate\Support\Fluent $column
  1000. * @return string|null
  1001. */
  1002. protected function modifyIncrement(Blueprint $blueprint, Fluent $column)
  1003. {
  1004. if (! $column->change
  1005. && ! $this->hasCommand($blueprint, 'primary')
  1006. && (in_array($column->type, $this->serials) || ($column->generatedAs !== null))
  1007. && $column->autoIncrement) {
  1008. return ' primary key';
  1009. }
  1010. }
  1011. /**
  1012. * Get the SQL for a generated virtual column modifier.
  1013. *
  1014. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  1015. * @param \Illuminate\Support\Fluent $column
  1016. * @return string|null
  1017. */
  1018. protected function modifyVirtualAs(Blueprint $blueprint, Fluent $column)
  1019. {
  1020. if ($column->change) {
  1021. if (array_key_exists('virtualAs', $column->getAttributes())) {
  1022. return is_null($column->virtualAs)
  1023. ? 'drop expression if exists'
  1024. : throw new LogicException('This database driver does not support modifying generated columns.');
  1025. }
  1026. return null;
  1027. }
  1028. if (! is_null($column->virtualAs)) {
  1029. return " generated always as ({$this->getValue($column->virtualAs)})";
  1030. }
  1031. }
  1032. /**
  1033. * Get the SQL for a generated stored column modifier.
  1034. *
  1035. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  1036. * @param \Illuminate\Support\Fluent $column
  1037. * @return string|null
  1038. */
  1039. protected function modifyStoredAs(Blueprint $blueprint, Fluent $column)
  1040. {
  1041. if ($column->change) {
  1042. if (array_key_exists('storedAs', $column->getAttributes())) {
  1043. return is_null($column->storedAs)
  1044. ? 'drop expression if exists'
  1045. : throw new LogicException('This database driver does not support modifying generated columns.');
  1046. }
  1047. return null;
  1048. }
  1049. if (! is_null($column->storedAs)) {
  1050. return " generated always as ({$this->getValue($column->storedAs)}) stored";
  1051. }
  1052. }
  1053. /**
  1054. * Get the SQL for an identity column modifier.
  1055. *
  1056. * @param \Illuminate\Database\Schema\Blueprint $blueprint
  1057. * @param \Illuminate\Support\Fluent $column
  1058. * @return string|array|null
  1059. */
  1060. protected function modifyGeneratedAs(Blueprint $blueprint, Fluent $column)
  1061. {
  1062. $sql = null;
  1063. if (! is_null($column->generatedAs)) {
  1064. $sql = sprintf(
  1065. ' generated %s as identity%s',
  1066. $column->always ? 'always' : 'by default',
  1067. ! is_bool($column->generatedAs) && ! empty($column->generatedAs) ? " ({$column->generatedAs})" : ''
  1068. );
  1069. }
  1070. if ($column->change) {
  1071. $changes = $column->autoIncrement && is_null($sql) ? [] : ['drop identity if exists'];
  1072. if (! is_null($sql)) {
  1073. $changes[] = 'add '.$sql;
  1074. }
  1075. return $changes;
  1076. }
  1077. return $sql;
  1078. }
  1079. }