Skip to content

Commit 6e3cd5d

Browse files
[12.x] Online (concurrently) index creation for PostgreSQL and SqlServer (#56625)
* [12.x] Online Index creation option * [12.x] SqlServer * [12.x] PostgreSql * Update IndexDefinition.php * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent bebbf49 commit 6e3cd5d

File tree

7 files changed

+160
-15
lines changed

7 files changed

+160
-15
lines changed

src/Illuminate/Database/Schema/Grammars/PostgresGrammar.php

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ public function compilePrimary(Blueprint $blueprint, Fluent $command)
323323
*
324324
* @param \Illuminate\Database\Schema\Blueprint $blueprint
325325
* @param \Illuminate\Support\Fluent $command
326-
* @return string
326+
* @return string[]
327327
*/
328328
public function compileUnique(Blueprint $blueprint, Fluent $command)
329329
{
@@ -333,12 +333,29 @@ public function compileUnique(Blueprint $blueprint, Fluent $command)
333333
$uniqueStatement .= ' nulls '.($command->nullsNotDistinct ? 'not distinct' : 'distinct');
334334
}
335335

336-
$sql = sprintf('alter table %s add constraint %s %s (%s)',
337-
$this->wrapTable($blueprint),
338-
$this->wrap($command->index),
339-
$uniqueStatement,
340-
$this->columnize($command->columns)
341-
);
336+
if ($command->online || $command->algorithm) {
337+
$createIndexSql = sprintf('create unique index %s%s on %s%s (%s)',
338+
$command->online ? 'concurrently ' : '',
339+
$this->wrap($command->index),
340+
$this->wrapTable($blueprint),
341+
$command->algorithm ? ' using '.$command->algorithm : '',
342+
$this->columnize($command->columns)
343+
);
344+
345+
$sql = sprintf('alter table %s add constraint %s unique using index %s',
346+
$this->wrapTable($blueprint),
347+
$this->wrap($command->index),
348+
$this->wrap($command->index)
349+
);
350+
} else {
351+
$sql = sprintf(
352+
'alter table %s add constraint %s %s (%s)',
353+
$this->wrapTable($blueprint),
354+
$this->wrap($command->index),
355+
$uniqueStatement,
356+
$this->columnize($command->columns)
357+
);
358+
}
342359

343360
if (! is_null($command->deferrable)) {
344361
$sql .= $command->deferrable ? ' deferrable' : ' not deferrable';
@@ -348,7 +365,7 @@ public function compileUnique(Blueprint $blueprint, Fluent $command)
348365
$sql .= $command->initiallyImmediate ? ' initially immediate' : ' initially deferred';
349366
}
350367

351-
return $sql;
368+
return isset($createIndexSql) ? [$createIndexSql, $sql] : [$sql];
352369
}
353370

354371
/**
@@ -360,7 +377,8 @@ public function compileUnique(Blueprint $blueprint, Fluent $command)
360377
*/
361378
public function compileIndex(Blueprint $blueprint, Fluent $command)
362379
{
363-
return sprintf('create index %s on %s%s (%s)',
380+
return sprintf('create index %s%s on %s%s (%s)',
381+
$command->online ? 'concurrently ' : '',
364382
$this->wrap($command->index),
365383
$this->wrapTable($blueprint),
366384
$command->algorithm ? ' using '.$command->algorithm : '',
@@ -385,7 +403,8 @@ public function compileFulltext(Blueprint $blueprint, Fluent $command)
385403
return "to_tsvector({$this->quoteString($language)}, {$this->wrap($column)})";
386404
}, $command->columns);
387405

388-
return sprintf('create index %s on %s using gin ((%s))',
406+
return sprintf('create index %s%s on %s using gin ((%s))',
407+
$command->online ? 'concurrently ' : '',
389408
$this->wrap($command->index),
390409
$this->wrapTable($blueprint),
391410
implode(' || ', $columns)
@@ -421,7 +440,8 @@ protected function compileIndexWithOperatorClass(Blueprint $blueprint, Fluent $c
421440
{
422441
$columns = $this->columnizeWithOperatorClass($command->columns, $command->operatorClass);
423442

424-
return sprintf('create index %s on %s%s (%s)',
443+
return sprintf('create index %s%s on %s%s (%s)',
444+
$command->online ? 'concurrently ' : '',
425445
$this->wrap($command->index),
426446
$this->wrapTable($blueprint),
427447
$command->algorithm ? ' using '.$command->algorithm : '',

src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,11 @@ public function compilePrimary(Blueprint $blueprint, Fluent $command)
272272
*/
273273
public function compileUnique(Blueprint $blueprint, Fluent $command)
274274
{
275-
return sprintf('create unique index %s on %s (%s)',
275+
return sprintf('create unique index %s on %s (%s)%s',
276276
$this->wrap($command->index),
277277
$this->wrapTable($blueprint),
278-
$this->columnize($command->columns)
278+
$this->columnize($command->columns),
279+
$command->online ? ' with (online = on)' : ''
279280
);
280281
}
281282

@@ -288,10 +289,11 @@ public function compileUnique(Blueprint $blueprint, Fluent $command)
288289
*/
289290
public function compileIndex(Blueprint $blueprint, Fluent $command)
290291
{
291-
return sprintf('create index %s on %s (%s)',
292+
return sprintf('create index %s on %s (%s)%s',
292293
$this->wrap($command->index),
293294
$this->wrapTable($blueprint),
294-
$this->columnize($command->columns)
295+
$this->columnize($command->columns),
296+
$command->online ? ' with (online = on)' : ''
295297
);
296298
}
297299

src/Illuminate/Database/Schema/IndexDefinition.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* @method $this deferrable(bool $value = true) Specify that the unique index is deferrable (PostgreSQL)
1111
* @method $this initiallyImmediate(bool $value = true) Specify the default time to check the unique index constraint (PostgreSQL)
1212
* @method $this nullsNotDistinct(bool $value = true) Specify that the null values should not be treated as distinct (PostgreSQL)
13+
* @method $this online(bool $value = true) Specify that index creation should not lock the table (PostgreSQL/SqlServer)
1314
*/
1415
class IndexDefinition extends Fluent
1516
{

tests/Database/DatabasePostgresSchemaGrammarTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,17 @@ public function testAddingUniqueKeyWithNullsDistinct()
307307
$this->assertSame('alter table "users" add constraint "bar" unique nulls distinct ("foo")', $statements[0]);
308308
}
309309

310+
public function testAddingUniqueKeyOnline()
311+
{
312+
$blueprint = new Blueprint($this->getConnection(), 'users');
313+
$blueprint->unique('foo')->online();
314+
$statements = $blueprint->toSql();
315+
316+
$this->assertCount(2, $statements);
317+
$this->assertSame('create unique index concurrently "users_foo_unique" on "users" ("foo")', $statements[0]);
318+
$this->assertSame('alter table "users" add constraint "users_foo_unique" unique using index "users_foo_unique"', $statements[1]);
319+
}
320+
310321
public function testAddingIndex()
311322
{
312323
$blueprint = new Blueprint($this->getConnection(), 'users');
@@ -327,6 +338,16 @@ public function testAddingIndexWithAlgorithm()
327338
$this->assertSame('create index "baz" on "users" using hash ("foo", "bar")', $statements[0]);
328339
}
329340

341+
public function testAddingIndexOnline()
342+
{
343+
$blueprint = new Blueprint($this->getConnection(), 'users');
344+
$blueprint->index('foo', 'baz')->online();
345+
$statements = $blueprint->toSql();
346+
347+
$this->assertCount(1, $statements);
348+
$this->assertSame('create index concurrently "baz" on "users" ("foo")', $statements[0]);
349+
}
350+
330351
public function testAddingFulltextIndex()
331352
{
332353
$blueprint = new Blueprint($this->getConnection(), 'users');
@@ -357,6 +378,16 @@ public function testAddingFulltextIndexWithLanguage()
357378
$this->assertSame('create index "users_body_fulltext" on "users" using gin ((to_tsvector(\'spanish\', "body")))', $statements[0]);
358379
}
359380

381+
public function testAddingFulltextIndexOnline()
382+
{
383+
$blueprint = new Blueprint($this->getConnection(), 'users');
384+
$blueprint->fulltext('body')->online();
385+
$statements = $blueprint->toSql();
386+
387+
$this->assertCount(1, $statements);
388+
$this->assertSame('create index concurrently "users_body_fulltext" on "users" using gin ((to_tsvector(\'english\', "body")))', $statements[0]);
389+
}
390+
360391
public function testAddingFulltextIndexWithFluency()
361392
{
362393
$blueprint = new Blueprint($this->getConnection(), 'users');
@@ -377,6 +408,16 @@ public function testAddingSpatialIndex()
377408
$this->assertSame('create index "geo_coordinates_spatialindex" on "geo" using gist ("coordinates")', $statements[0]);
378409
}
379410

411+
public function testAddingSpatialIndexOnline()
412+
{
413+
$blueprint = new Blueprint($this->getConnection(), 'geo');
414+
$blueprint->spatialIndex('coordinates')->online();
415+
$statements = $blueprint->toSql();
416+
417+
$this->assertCount(1, $statements);
418+
$this->assertSame('create index concurrently "geo_coordinates_spatialindex" on "geo" using gist ("coordinates")', $statements[0]);
419+
}
420+
380421
public function testAddingFluentSpatialIndex()
381422
{
382423
$blueprint = new Blueprint($this->getConnection(), 'geo');
@@ -407,6 +448,16 @@ public function testAddingSpatialIndexWithOperatorClassMultipleColumns()
407448
$this->assertSame('create index "my_index" on "geo" using gist ("coordinates" point_ops, "location" point_ops)', $statements[0]);
408449
}
409450

451+
public function testAddingSpatialIndexWithOperatorClassOnline()
452+
{
453+
$blueprint = new Blueprint($this->getConnection(), 'geo');
454+
$blueprint->spatialIndex('coordinates', 'my_index', 'point_ops')->online();
455+
$statements = $blueprint->toSql();
456+
457+
$this->assertCount(1, $statements);
458+
$this->assertSame('create index concurrently "my_index" on "geo" using gist ("coordinates" point_ops)', $statements[0]);
459+
}
460+
410461
public function testAddingRawIndex()
411462
{
412463
$blueprint = new Blueprint($this->getConnection(), 'users');
@@ -417,6 +468,16 @@ public function testAddingRawIndex()
417468
$this->assertSame('create index "raw_index" on "users" ((function(column)))', $statements[0]);
418469
}
419470

471+
public function testAddingRawIndexOnline()
472+
{
473+
$blueprint = new Blueprint($this->getConnection(), 'users');
474+
$blueprint->rawIndex('(function(column))', 'raw_index')->online();
475+
$statements = $blueprint->toSql();
476+
477+
$this->assertCount(1, $statements);
478+
$this->assertSame('create index concurrently "raw_index" on "users" ((function(column)))', $statements[0]);
479+
}
480+
420481
public function testAddingIncrementingID()
421482
{
422483
$blueprint = new Blueprint($this->getConnection(), 'users');

tests/Database/DatabaseSqlServerSchemaGrammarTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,16 @@ public function testAddingUniqueKey()
283283
$this->assertSame('create unique index "bar" on "users" ("foo")', $statements[0]);
284284
}
285285

286+
public function testAddingUniqueKeyOnline()
287+
{
288+
$blueprint = new Blueprint($this->getConnection(), 'users');
289+
$blueprint->unique('foo', 'bar')->online();
290+
$statements = $blueprint->toSql();
291+
292+
$this->assertCount(1, $statements);
293+
$this->assertSame('create unique index "bar" on "users" ("foo") with (online = on)', $statements[0]);
294+
}
295+
286296
public function testAddingIndex()
287297
{
288298
$blueprint = new Blueprint($this->getConnection(), 'users');
@@ -293,6 +303,16 @@ public function testAddingIndex()
293303
$this->assertSame('create index "baz" on "users" ("foo", "bar")', $statements[0]);
294304
}
295305

306+
public function testAddingIndexOnline()
307+
{
308+
$blueprint = new Blueprint($this->getConnection(), 'users');
309+
$blueprint->index(['foo', 'bar'], 'baz')->online();
310+
$statements = $blueprint->toSql();
311+
312+
$this->assertCount(1, $statements);
313+
$this->assertSame('create index "baz" on "users" ("foo", "bar") with (online = on)', $statements[0]);
314+
}
315+
296316
public function testAddingSpatialIndex()
297317
{
298318
$blueprint = new Blueprint($this->getConnection(), 'geo');

tests/Integration/Database/Postgres/PostgresSchemaBuilderTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,27 @@ public function testGetRawIndex()
205205

206206
$this->assertSame([], collect($indexes)->firstWhere('name', 'table_raw_index')['columns']);
207207
}
208+
209+
public function testCreateIndexesOnline()
210+
{
211+
Schema::create('public.table', function (Blueprint $table) {
212+
$table->id();
213+
$table->timestamps();
214+
$table->string('title', 200);
215+
$table->text('body');
216+
217+
$table->unique('title')->online();
218+
$table->index(['created_at'])->online();
219+
$table->fullText(['body'])->online();
220+
$table->rawIndex("DATE_TRUNC('year'::text,created_at)", 'table_raw_index')->online();
221+
});
222+
223+
$indexes = Schema::getIndexes('public.table');
224+
$indexNames = collect($indexes)->pluck('name');
225+
226+
$this->assertContains('public_table_title_unique', $indexNames);
227+
$this->assertContains('public_table_created_at_index', $indexNames);
228+
$this->assertContains('public_table_body_fulltext', $indexNames);
229+
$this->assertContains('table_raw_index', $indexNames);
230+
}
208231
}

tests/Integration/Database/SqlServer/DatabaseSqlServerSchemaBuilderTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,22 @@ public function testComputedColumnsListing()
7373
$userColumns = Schema::getColumns('users');
7474
$this->assertNull($userColumns[1]['generation']);
7575
}
76+
77+
public function testCreateIndexesOnline()
78+
{
79+
Schema::create('table', function (Blueprint $table) {
80+
$table->id();
81+
$table->timestamps();
82+
$table->string('title', 200);
83+
84+
$table->unique('title')->online();
85+
$table->index(['created_at'])->online();
86+
});
87+
88+
$indexes = Schema::getIndexes('table');
89+
$indexNames = collect($indexes)->pluck('name');
90+
91+
$this->assertContains('table_title_unique', $indexNames);
92+
$this->assertContains('table_created_at_index', $indexNames);
93+
}
7694
}

0 commit comments

Comments
 (0)