Skip to content

Commit 4f175f3

Browse files
dropAllTables (#161)
* add json and medium/long text column type * Update src/Schema/Grammar.php Co-authored-by: Takayasu Oyama <[email protected]> * Update src/Schema/Grammar.php Co-authored-by: Takayasu Oyama <[email protected]> * Update src/Schema/Grammar.php Co-authored-by: Takayasu Oyama <[email protected]> * add unit tests * Update tests/Schema/BlueprintTest.php * add typeChar * add/alter columns should be separate statements * type hint compileAdd properly * dropAllTables - just delete and recreate the database * phpdoc * only drop if db exists * add dropForeign compilation * fix dropColumn as well * fix type hint * changelog * Update CHANGELOG.md * changelog * Update CHANGELOG.md * added test and changelog * instead of dropping db, move towards proper drop sequence * proper drop sequence with testss * changelog * oh man phpstan * Allow for some tests to be run last * ugh dd * Update src/Schema/Builder.php Co-authored-by: Takayasu Oyama <[email protected]> * compileForeignListing > compileForeignKeys * tidy up dropAllTables * getForeignKeyListing > getForeignKeys * remove unneeded override of getForeignKeys * composer laravel dependency * getTables inheritDoc * fix getForeignKey and getIndexListing > getIndexes * changelog * dup changelog * deprecate instead of delete index listing methods --------- Co-authored-by: Takayasu Oyama <[email protected]>
1 parent 96823b1 commit 4f175f3

File tree

7 files changed

+180
-16
lines changed

7 files changed

+180
-16
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# v6.2.0 (Not Released Yet)
22

33
Added
4-
- `json` `mediumText` `longText` support for `Schema\Builder` (#155)
4+
- `json` `mediumText` `longText` `char` support for `Schema\Builder` (#155) (#158)
55
- `Schema\Grammar::compileDropForeign` to allow dropping foreign key constraints (#163)
6+
- `Schema\Builder::dropAllTables` works properly, dropping foreign keys, indexes, then tables in order of interleaving (#161)
67

78
Changed
89
- `Query\Builder::lock()` no longer throw an error and will be ignored instead (#156)
10+
- `Schema\Builder::getIndexListing()` `Schema\Grammar::compileIndexListing()` converted to `getIndexes()` and `compileIndexes()` to align with standard Laravel methods (#161)
911

1012
Fixed
1113
- `Schema\Grammar::compileAdd()` `Schema\Grammar::compileChange()` `Schema\Grammar::compileChange()` now create separate statements (#159)

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"php": "^8.1",
1212
"ext-grpc": "*",
1313
"ext-json": "*",
14-
"laravel/framework": "^10.34.2",
14+
"laravel/framework": "^10.37.0",
1515
"google/cloud-spanner": "^1.58.4",
1616
"grpc/grpc": "^1.42",
1717
"symfony/cache": "~6",

phpunit.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
stopOnFailure="true">
2828
<testsuite name="all">
2929
<directory suffix="Test.php">./tests</directory>
30+
<directory suffix="TestLast.php">./tests</directory>
3031
</testsuite>
3132
<php>
3233
<env name="SPANNER_EMULATOR_HOST" value="emulator:9010"/>

src/Query/Processor.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,34 @@ public function processColumns($results)
7878
}
7979

8080
/**
81+
* @deprecated Use processIndexes($results) instead.
8182
* @param array $results
8283
* @return array
8384
*/
8485
public function processIndexListing($results)
86+
{
87+
return self::processIndexes($results);
88+
}
89+
90+
/**
91+
* @param array $results
92+
* @return array
93+
*/
94+
public function processIndexes($results)
8595
{
8696
return array_map(function ($result) {
8797
return ((object) $result)->index_name;
8898
}, $results);
8999
}
100+
101+
/**
102+
* @param array $results
103+
* @return array
104+
*/
105+
public function processForeignKeys($results)
106+
{
107+
return array_map(function ($result) {
108+
return ((object) $result)->key_name;
109+
}, $results);
110+
}
90111
}

src/Schema/Builder.php

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
use Closure;
2121
use Colopl\Spanner\Query\Processor;
22+
use Colopl\Spanner\Connection;
2223
use Illuminate\Database\Schema\Builder as BaseBuilder;
24+
use Illuminate\Support\Fluent;
2325

2426
/**
2527
* @property Grammar $grammar
@@ -47,21 +49,26 @@ public function getAllTables()
4749
}
4850

4951
/**
52+
* @inheritDoc Adds a parent key, for tracking interleaving
53+
*
54+
* @return list<array{ name: string, type: string, parent: string }>
55+
*/
56+
public function getTables()
57+
{
58+
return $this->connection->select(
59+
$this->grammar->compileTables()
60+
);
61+
}
62+
63+
/**
64+
* @deprecated Use getIndexes($table) instead
65+
*
5066
* @param string $table
5167
* @return string[]
5268
*/
5369
public function getIndexListing($table)
5470
{
55-
$table = $this->connection->getTablePrefix().$table;
56-
57-
$results = $this->connection->select(
58-
$this->grammar->compileIndexListing(), [$table]
59-
);
60-
61-
/** @var Processor $processor */
62-
$processor = $this->connection->getPostProcessor();
63-
64-
return $processor->processIndexListing($results);
71+
return parent::getIndexes($table);
6572
}
6673

6774
/**
@@ -83,7 +90,7 @@ public function dropIndex($table, $name)
8390
*/
8491
public function dropIndexIfExist($table, $name)
8592
{
86-
if(in_array($name, $this->getIndexListing($table))) {
93+
if(in_array($name, $this->getIndexes($table))) {
8794
$blueprint = $this->createBlueprint($table);
8895
$blueprint->dropIndex($name);
8996
$this->build($blueprint);
@@ -99,4 +106,65 @@ protected function createBlueprint($table, Closure $callback = null)
99106
? ($this->resolver)($table, $callback)
100107
: new Blueprint($table, $callback);
101108
}
109+
110+
/**
111+
* Drop all tables from the database.
112+
*
113+
* @return void
114+
*/
115+
public function dropAllTables()
116+
{
117+
/** @var Connection */
118+
$connection = $this->connection;
119+
$tables = $this->getTables();
120+
$sortedTables = [];
121+
122+
// add parents counter
123+
foreach ($tables as $table) {
124+
$sortedTables[$table['name']] = ['parents' => 0, ...$table];
125+
}
126+
127+
// loop through all tables and count how many parents they have
128+
foreach ($sortedTables as $key => $table) {
129+
if(!$table['parent']) continue;
130+
131+
$current = $table;
132+
while($current['parent']) {
133+
$table['parents'] += 1;
134+
$current = $sortedTables[$current['parent']];
135+
}
136+
$sortedTables[$key] = $table;
137+
}
138+
139+
// sort tables desc based on parent count
140+
usort($sortedTables, fn($a, $b) => $b['parents'] <=> $a['parents']);
141+
142+
// drop foreign keys first (otherwise index queries will include them)
143+
$queries = [];
144+
foreach ($sortedTables as $tableData) {
145+
$tableName = $tableData['name'];
146+
$foreigns = $this->getForeignKeys($tableName);
147+
$blueprint = $this->createBlueprint($tableName);
148+
foreach ($foreigns as $foreign) {
149+
$blueprint->dropForeign($foreign);
150+
}
151+
array_push($queries, ...$blueprint->toSql($connection, $this->grammar));
152+
}
153+
$connection->runDdlBatch($queries);
154+
155+
// drop indexes and tables
156+
$queries = [];
157+
foreach ($sortedTables as $tableData) {
158+
$tableName = $tableData['name'];
159+
$indexes = $this->getIndexes($tableName);
160+
$blueprint = $this->createBlueprint($tableName);
161+
foreach ($indexes as $index) {
162+
if($index == 'PRIMARY_KEY') continue;
163+
$blueprint->dropIndex($index);
164+
}
165+
$blueprint->drop();
166+
array_push($queries, ...$blueprint->toSql($connection, $this->grammar));
167+
}
168+
$connection->runDdlBatch($queries);
169+
}
102170
}

src/Schema/Grammar.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public function compileTableExists()
5858
*/
5959
public function compileTables()
6060
{
61-
return 'select `table_name` as name from information_schema.tables where table_schema = \'\' and table_type = \'BASE TABLE\'';
61+
return 'select `table_name` as name, `table_type` as type, `parent_table_name` as parent from information_schema.tables where table_schema = \'\' and table_type = \'BASE TABLE\'';
6262
}
6363

6464
/**
@@ -86,13 +86,43 @@ public function compileColumnListing()
8686
/**
8787
* Compile the query to determine the list of indexes.
8888
*
89+
* @deprecated Use compileIndexes($table) instead.
90+
*
8991
* @return string
9092
*/
9193
public function compileIndexListing()
9294
{
9395
return 'select index_name as `index_name` from information_schema.indexes where table_schema = \'\' and table_name = ?';
9496
}
9597

98+
/**
99+
* Compile the query to determine the list of indexes.
100+
*
101+
* @param string $table
102+
* @return string
103+
*/
104+
public function compileIndexes($table)
105+
{
106+
return sprintf(
107+
'select index_name as `index_name` from information_schema.indexes where table_schema = \'\' and table_name = %s',
108+
$this->quoteString($table)
109+
);
110+
}
111+
112+
/**
113+
* Compile the query to determine the list of foreign keys.
114+
*
115+
* @param string $table
116+
* @return string
117+
*/
118+
public function compileForeignKeys($table)
119+
{
120+
return sprintf(
121+
'select constraint_name as `key_name` from information_schema.table_constraints where constraint_type = "FOREIGN KEY" and table_schema = \'\' and table_name = %s',
122+
$this->quoteString($table)
123+
);
124+
}
125+
96126
/**
97127
* Compile the query to determine the columns.
98128
*

tests/Schema/BuilderTest.php renamed to tests/Schema/BuilderTestLast.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
use Illuminate\Support\Arr;
2424
use Illuminate\Support\Str;
2525

26-
class BuilderTest extends TestCase
26+
class BuilderTestLast extends TestCase
2727
{
2828
private const TABLE_NAME_CREATED = 'schema_builder_test_table';
2929
private const TABLE_NAME_RELATION_PARENT = 'users';
@@ -243,11 +243,53 @@ public function test_getAllTables(): void
243243

244244
/** @var array{ name: string, type: string } $row */
245245
$row = Arr::first(
246-
$sb->getAllTables(),
246+
$sb->getTables(),
247247
static fn (array $row): bool => $row['name'] === $table,
248248
);
249249

250250
$this->assertSame($table, $row['name']);
251251
$this->assertSame('BASE TABLE', $row['type']);
252252
}
253+
254+
public function test_dropAllTables(): void
255+
{
256+
$conn = $this->getDefaultConnection();
257+
$sb = $conn->getSchemaBuilder();
258+
$table1 = $this->generateTableName(class_basename(__CLASS__));
259+
$sb->create($table1, function (Blueprint $table) {
260+
$table->uuid('id')->primary();
261+
$table->uuid('something');
262+
$table->index('something');
263+
});
264+
265+
$table2 = $this->generateTableName(class_basename(__CLASS__));
266+
$sb->create($table2, function (Blueprint $table) use ($table1) {
267+
$table->uuid('table2_id')->primary();
268+
$table->uuid('other_id');
269+
$table->index('other_id');
270+
$table->foreign('other_id')->references('id')->on($table1);
271+
});
272+
273+
$table3 = $this->generateTableName(class_basename(__CLASS__));
274+
$sb->create($table3, function (Blueprint $table) use ($table2) {
275+
$table->uuid('table2_id');
276+
$table->uuid('table3_id');
277+
$table->primary(['table2_id', 'table3_id']);
278+
$table->interleaveInParent($table2);
279+
});
280+
281+
$table4 = $this->generateTableName(class_basename(__CLASS__));
282+
$sb->create($table4, function (Blueprint $table) use ($table3) {
283+
$table->uuid('table2_id');
284+
$table->uuid('table3_id');
285+
$table->uuid('table4_id');
286+
$table->primary(['table2_id', 'table3_id', 'table4_id']);
287+
$table->interleaveInParent($table3);
288+
});
289+
290+
$sb->dropAllTables();
291+
292+
$tables = $sb->getTables();
293+
$this->assertEmpty($tables);
294+
}
253295
}

0 commit comments

Comments
 (0)