diff --git a/CHANGELOG.md b/CHANGELOG.md
index 082c6754..7cdf774c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,13 @@
# v6.2.0 (Not Released Yet)
Added
-- `json` `mediumText` `longText` support for `Schema\Builder` (#155)
+- `json` `mediumText` `longText` `char` support for `Schema\Builder` (#155) (#158)
- `Schema\Grammar::compileDropForeign` to allow dropping foreign key constraints (#163)
+- `Schema\Builder::dropAllTables` works properly, dropping foreign keys, indexes, then tables in order of interleaving (#161)
Changed
- `Query\Builder::lock()` no longer throw an error and will be ignored instead (#156)
+- `Schema\Builder::getIndexListing()` `Schema\Grammar::compileIndexListing()` converted to `getIndexes()` and `compileIndexes()` to align with standard Laravel methods (#161)
Fixed
- `Schema\Grammar::compileAdd()` `Schema\Grammar::compileChange()` `Schema\Grammar::compileChange()` now create separate statements (#159)
diff --git a/composer.json b/composer.json
index 268654f1..99d4d42f 100644
--- a/composer.json
+++ b/composer.json
@@ -11,7 +11,7 @@
"php": "^8.1",
"ext-grpc": "*",
"ext-json": "*",
- "laravel/framework": "^10.34.2",
+ "laravel/framework": "^10.37.0",
"google/cloud-spanner": "^1.58.4",
"grpc/grpc": "^1.42",
"symfony/cache": "~6",
diff --git a/phpunit.xml b/phpunit.xml
index 274c1c37..8426cc50 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -27,6 +27,7 @@
stopOnFailure="true">
./tests
+ ./tests
diff --git a/src/Query/Processor.php b/src/Query/Processor.php
index ec9106ec..589c95ca 100644
--- a/src/Query/Processor.php
+++ b/src/Query/Processor.php
@@ -78,13 +78,34 @@ public function processColumns($results)
}
/**
+ * @deprecated Use processIndexes($results) instead.
* @param array $results
* @return array
*/
public function processIndexListing($results)
+ {
+ return self::processIndexes($results);
+ }
+
+ /**
+ * @param array $results
+ * @return array
+ */
+ public function processIndexes($results)
{
return array_map(function ($result) {
return ((object) $result)->index_name;
}, $results);
}
+
+ /**
+ * @param array $results
+ * @return array
+ */
+ public function processForeignKeys($results)
+ {
+ return array_map(function ($result) {
+ return ((object) $result)->key_name;
+ }, $results);
+ }
}
diff --git a/src/Schema/Builder.php b/src/Schema/Builder.php
index 1e922692..cc40b360 100644
--- a/src/Schema/Builder.php
+++ b/src/Schema/Builder.php
@@ -19,7 +19,9 @@
use Closure;
use Colopl\Spanner\Query\Processor;
+use Colopl\Spanner\Connection;
use Illuminate\Database\Schema\Builder as BaseBuilder;
+use Illuminate\Support\Fluent;
/**
* @property Grammar $grammar
@@ -47,21 +49,26 @@ public function getAllTables()
}
/**
+ * @inheritDoc Adds a parent key, for tracking interleaving
+ *
+ * @return list
+ */
+ public function getTables()
+ {
+ return $this->connection->select(
+ $this->grammar->compileTables()
+ );
+ }
+
+ /**
+ * @deprecated Use getIndexes($table) instead
+ *
* @param string $table
* @return string[]
*/
public function getIndexListing($table)
{
- $table = $this->connection->getTablePrefix().$table;
-
- $results = $this->connection->select(
- $this->grammar->compileIndexListing(), [$table]
- );
-
- /** @var Processor $processor */
- $processor = $this->connection->getPostProcessor();
-
- return $processor->processIndexListing($results);
+ return parent::getIndexes($table);
}
/**
@@ -83,7 +90,7 @@ public function dropIndex($table, $name)
*/
public function dropIndexIfExist($table, $name)
{
- if(in_array($name, $this->getIndexListing($table))) {
+ if(in_array($name, $this->getIndexes($table))) {
$blueprint = $this->createBlueprint($table);
$blueprint->dropIndex($name);
$this->build($blueprint);
@@ -99,4 +106,65 @@ protected function createBlueprint($table, Closure $callback = null)
? ($this->resolver)($table, $callback)
: new Blueprint($table, $callback);
}
+
+ /**
+ * Drop all tables from the database.
+ *
+ * @return void
+ */
+ public function dropAllTables()
+ {
+ /** @var Connection */
+ $connection = $this->connection;
+ $tables = $this->getTables();
+ $sortedTables = [];
+
+ // add parents counter
+ foreach ($tables as $table) {
+ $sortedTables[$table['name']] = ['parents' => 0, ...$table];
+ }
+
+ // loop through all tables and count how many parents they have
+ foreach ($sortedTables as $key => $table) {
+ if(!$table['parent']) continue;
+
+ $current = $table;
+ while($current['parent']) {
+ $table['parents'] += 1;
+ $current = $sortedTables[$current['parent']];
+ }
+ $sortedTables[$key] = $table;
+ }
+
+ // sort tables desc based on parent count
+ usort($sortedTables, fn($a, $b) => $b['parents'] <=> $a['parents']);
+
+ // drop foreign keys first (otherwise index queries will include them)
+ $queries = [];
+ foreach ($sortedTables as $tableData) {
+ $tableName = $tableData['name'];
+ $foreigns = $this->getForeignKeys($tableName);
+ $blueprint = $this->createBlueprint($tableName);
+ foreach ($foreigns as $foreign) {
+ $blueprint->dropForeign($foreign);
+ }
+ array_push($queries, ...$blueprint->toSql($connection, $this->grammar));
+ }
+ $connection->runDdlBatch($queries);
+
+ // drop indexes and tables
+ $queries = [];
+ foreach ($sortedTables as $tableData) {
+ $tableName = $tableData['name'];
+ $indexes = $this->getIndexes($tableName);
+ $blueprint = $this->createBlueprint($tableName);
+ foreach ($indexes as $index) {
+ if($index == 'PRIMARY_KEY') continue;
+ $blueprint->dropIndex($index);
+ }
+ $blueprint->drop();
+ array_push($queries, ...$blueprint->toSql($connection, $this->grammar));
+ }
+ $connection->runDdlBatch($queries);
+ }
}
diff --git a/src/Schema/Grammar.php b/src/Schema/Grammar.php
index 6a926061..f1567e0f 100644
--- a/src/Schema/Grammar.php
+++ b/src/Schema/Grammar.php
@@ -58,7 +58,7 @@ public function compileTableExists()
*/
public function compileTables()
{
- return 'select `table_name` as name from information_schema.tables where table_schema = \'\' and table_type = \'BASE TABLE\'';
+ 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\'';
}
/**
@@ -86,6 +86,8 @@ public function compileColumnListing()
/**
* Compile the query to determine the list of indexes.
*
+ * @deprecated Use compileIndexes($table) instead.
+ *
* @return string
*/
public function compileIndexListing()
@@ -93,6 +95,34 @@ public function compileIndexListing()
return 'select index_name as `index_name` from information_schema.indexes where table_schema = \'\' and table_name = ?';
}
+ /**
+ * Compile the query to determine the list of indexes.
+ *
+ * @param string $table
+ * @return string
+ */
+ public function compileIndexes($table)
+ {
+ return sprintf(
+ 'select index_name as `index_name` from information_schema.indexes where table_schema = \'\' and table_name = %s',
+ $this->quoteString($table)
+ );
+ }
+
+ /**
+ * Compile the query to determine the list of foreign keys.
+ *
+ * @param string $table
+ * @return string
+ */
+ public function compileForeignKeys($table)
+ {
+ return sprintf(
+ 'select constraint_name as `key_name` from information_schema.table_constraints where constraint_type = "FOREIGN KEY" and table_schema = \'\' and table_name = %s',
+ $this->quoteString($table)
+ );
+ }
+
/**
* Compile the query to determine the columns.
*
diff --git a/tests/Schema/BuilderTest.php b/tests/Schema/BuilderTestLast.php
similarity index 83%
rename from tests/Schema/BuilderTest.php
rename to tests/Schema/BuilderTestLast.php
index 73b91d3f..ceaebe77 100644
--- a/tests/Schema/BuilderTest.php
+++ b/tests/Schema/BuilderTestLast.php
@@ -23,7 +23,7 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
-class BuilderTest extends TestCase
+class BuilderTestLast extends TestCase
{
private const TABLE_NAME_CREATED = 'schema_builder_test_table';
private const TABLE_NAME_RELATION_PARENT = 'users';
@@ -243,11 +243,53 @@ public function test_getAllTables(): void
/** @var array{ name: string, type: string } $row */
$row = Arr::first(
- $sb->getAllTables(),
+ $sb->getTables(),
static fn (array $row): bool => $row['name'] === $table,
);
$this->assertSame($table, $row['name']);
$this->assertSame('BASE TABLE', $row['type']);
}
+
+ public function test_dropAllTables(): void
+ {
+ $conn = $this->getDefaultConnection();
+ $sb = $conn->getSchemaBuilder();
+ $table1 = $this->generateTableName(class_basename(__CLASS__));
+ $sb->create($table1, function (Blueprint $table) {
+ $table->uuid('id')->primary();
+ $table->uuid('something');
+ $table->index('something');
+ });
+
+ $table2 = $this->generateTableName(class_basename(__CLASS__));
+ $sb->create($table2, function (Blueprint $table) use ($table1) {
+ $table->uuid('table2_id')->primary();
+ $table->uuid('other_id');
+ $table->index('other_id');
+ $table->foreign('other_id')->references('id')->on($table1);
+ });
+
+ $table3 = $this->generateTableName(class_basename(__CLASS__));
+ $sb->create($table3, function (Blueprint $table) use ($table2) {
+ $table->uuid('table2_id');
+ $table->uuid('table3_id');
+ $table->primary(['table2_id', 'table3_id']);
+ $table->interleaveInParent($table2);
+ });
+
+ $table4 = $this->generateTableName(class_basename(__CLASS__));
+ $sb->create($table4, function (Blueprint $table) use ($table3) {
+ $table->uuid('table2_id');
+ $table->uuid('table3_id');
+ $table->uuid('table4_id');
+ $table->primary(['table2_id', 'table3_id', 'table4_id']);
+ $table->interleaveInParent($table3);
+ });
+
+ $sb->dropAllTables();
+
+ $tables = $sb->getTables();
+ $this->assertEmpty($tables);
+ }
}