Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/CockroachDbConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Grammar as BaseGrammar;
use Illuminate\Database\PDO\PostgresDriver;
use Illuminate\Database\PostgresConnection;
use Illuminate\Filesystem\Filesystem;
use YlsIdeas\CockroachDb\Builder\CockroachDbBuilder as DbBuilder;
use YlsIdeas\CockroachDb\Driver\CockroachDBDriver;
use YlsIdeas\CockroachDb\Processor\CockroachDbProcessor as DbProcessor;
use YlsIdeas\CockroachDb\Query\CockroachGrammar as QueryGrammar;
use YlsIdeas\CockroachDb\Schema\CockroachGrammar as SchemaGrammar;
Expand Down Expand Up @@ -76,7 +76,7 @@ protected function getDefaultPostProcessor(): DbProcessor
*/
protected function getDoctrineDriver()
{
return new PostgresDriver();
return new CockroachDBDriver();
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/Driver/CockroachDBDriver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace YlsIdeas\CockroachDb\Driver;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Illuminate\Database\PDO\PostgresDriver;
use YlsIdeas\CockroachDb\Schema\CockroachSchemaManager;

class CockroachDBDriver extends PostgresDriver
{
public function getSchemaManager(Connection $conn, AbstractPlatform $platform): AbstractSchemaManager
{
return new CockroachSchemaManager($conn, $platform);
}
}
16 changes: 16 additions & 0 deletions src/Schema/CockroachGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,20 @@ public function compileDropUnique(Blueprint $blueprint, Fluent $command)

return "drop index {$this->wrapTable($blueprint)}@{$index} cascade";
}

public function compileColumns($database, $schema, $table): string
{
return sprintf(
'select a.attname as name, t.typname as type_name, format_type(a.atttypid, a.atttypmod) as type, '
.'(select tc.collcollate from pg_catalog.pg_collation tc where tc.oid = a.attcollation) as collation, '
.'not a.attnotnull as nullable, '
.'(select pg_get_expr(adbin, adrelid) from pg_attrdef where c.oid = pg_attrdef.adrelid and pg_attrdef.adnum = a.attnum) as default, '
.'col_description(c.oid, a.attnum) as comment '
.'from pg_attribute a, pg_class c, pg_type t, pg_namespace n '
.'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 and a.attisdropped = false '
.'order by a.attnum',
$this->quoteString($table),
$this->quoteString($schema)
);
}
}
93 changes: 93 additions & 0 deletions src/Schema/CockroachSchemaManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace YlsIdeas\CockroachDb\Schema;

use Doctrine\DBAL\Result;
use Doctrine\DBAL\Schema\Identifier;
use Doctrine\DBAL\Schema\PostgreSQLSchemaManager;

class CockroachSchemaManager extends PostgreSQLSchemaManager
{
protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result
{
$sql = 'SELECT';

if ($tableName === null) {
$sql .= ' c.relname AS table_name, n.nspname AS schema_name,';
}

$sql .= <<<'SQL'
a.attnum,
quote_ident(a.attname) AS field,
t.typname AS type,
format_type(a.atttypid, a.atttypmod) AS complete_type,
(SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,
(SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type,
(SELECT format_type(t2.typbasetype, t2.typtypmod) FROM
pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type,
a.attnotnull AS isnotnull,
(SELECT 't'
FROM pg_index
WHERE c.oid = pg_index.indrelid
AND pg_index.indkey[0] = a.attnum
AND pg_index.indisprimary = 't'
) AS pri,
(SELECT pg_get_expr(adbin, adrelid)
FROM pg_attrdef
WHERE c.oid = pg_attrdef.adrelid
AND pg_attrdef.adnum=a.attnum
) AS default,
(SELECT pg_description.description
FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid
) AS comment
FROM pg_attribute a
INNER JOIN pg_class c
ON c.oid = a.attrelid
INNER JOIN pg_type t
ON t.oid = a.atttypid
INNER JOIN pg_namespace n
ON n.oid = c.relnamespace
LEFT JOIN pg_depend d
ON d.objid = c.oid
AND d.deptype = 'e'
AND d.classid = (SELECT oid FROM pg_class WHERE relname = 'pg_class')
SQL;

$conditions = array_merge([
'a.attnum > 0',
"c.relkind = 'r'",
'd.refobjid IS NULL',
'a.attisdropped = false',
], $this->buildQueryConditions($tableName));

$sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY a.attnum';

return $this->_conn->executeQuery($sql);
}

/**
* @param string|null $tableName
*
* @return list<string>
*/
private function buildQueryConditions($tableName): array
{
$conditions = [];

if ($tableName !== null) {
if (strpos($tableName, '.') !== false) {
[$schemaName, $tableName] = explode('.', $tableName);
$conditions[] = 'n.nspname = ' . $this->_platform->quoteStringLiteral($schemaName);
} else {
$conditions[] = 'n.nspname = ANY(current_schemas(false))';
}

$identifier = new Identifier($tableName);
$conditions[] = 'c.relname = ' . $this->_platform->quoteStringLiteral($identifier->getName());
}

$conditions[] = "n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')";

return $conditions;
}
}
67 changes: 67 additions & 0 deletions tests/Integration/Database/MigrationRenamingTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace YlsIdeas\CockroachDb\Tests\Integration\Database;

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class MigrationRenamingTest extends DatabaseTestCase
{
private const TEST_TABLE = 'test_table';

protected function setUp(): void
{
parent::setUp();

Schema::create(self::TEST_TABLE, function (Blueprint $table) {
$table->id();
$table->string('column_y');
$table->string('column_x');
});
}

public function test_rename_column_to_previously_deleted_one(): void
{
Schema::table(self::TEST_TABLE, function (Blueprint $table) {
$table->dropColumn('column_y');
});

Schema::table(self::TEST_TABLE, function (Blueprint $table) {
$table->renameColumn('column_x', 'column_y');
});

$tableColumns = Schema::getColumns(self::TEST_TABLE);
$this->assertCount(2, $tableColumns);
$this->assertEquals('id', $tableColumns[0]['name']);
$this->assertEquals('column_y', $tableColumns[1]['name']);
}

public function test_rename_column_with_any_previously_deleted_one(): void
{
Schema::table(self::TEST_TABLE, function (Blueprint $table) {
$table->dropColumn('column_y');
});

Schema::table(self::TEST_TABLE, function (Blueprint $table) {
$table->renameColumn('column_x', 'column_z');
});

$tableColumns = Schema::getColumns(self::TEST_TABLE);
$this->assertCount(2, $tableColumns);
$this->assertEquals('id', $tableColumns[0]['name']);
$this->assertEquals('column_z', $tableColumns[1]['name']);
}

public function test_rename_column_without_deleted_ones(): void
{
Schema::table(self::TEST_TABLE, function (Blueprint $table) {
$table->renameColumn('column_x', 'column_z');
});

$tableColumns = Schema::getColumns(self::TEST_TABLE);
$this->assertCount(3, $tableColumns);
$this->assertEquals('id', $tableColumns[0]['name']);
$this->assertEquals('column_y', $tableColumns[1]['name']);
$this->assertEquals('column_z', $tableColumns[2]['name']);
}
}