Skip to content

Commit

Permalink
Schema dumps (#30)
Browse files Browse the repository at this point in the history
* CockroachSchemaState - add dump/load cache schema

Signed-off-by: Kamil Michalak <[email protected]>

* wip

* Adds fleshed out functionality for schema dumps

* Fix PHP styling

* Fix broken CI

* CI fixes

* Fix tests of older Laravel versions

* Fix breaking tests

* Fixes for CI

* Fix PHP styling

* Another CI fix

* Fix PHPStan

---------

Signed-off-by: Kamil Michalak <[email protected]>
Co-authored-by: Kamil Michalak <[email protected]>
Co-authored-by: peterfox <[email protected]>
  • Loading branch information
3 people authored Jan 14, 2024
1 parent f3383bb commit aa42814
Show file tree
Hide file tree
Showing 15 changed files with 299 additions and 43 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ jobs:
- name: Run CockroachDB Service
run: docker-compose up -d
env:
VERSION: v22.1.7
VERSION: v23.1.13

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
php-version: 8.3
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql
tools: composer:v2
coverage: pcov
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
paths:
- '**.php'
- 'phpstan.neon.dist'
pull_request:
branches: [ main ]

jobs:
phpstan:
Expand All @@ -16,7 +18,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
php-version: 8.3
coverage: none

- name: Install composer dependencies
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
php: [ 8.0, 8.1, 8.2 ]
laravel: [ 10.*, 9.*, 8.* ]
php: [ 8.1, 8.2, 8.3 ]
laravel: [ 10.*, 9.* ]
cockroachdb: [ v22.2.17, v23.1.13 ]
dependencies: [ stable, lowest ]
include:
- laravel: 10.*
testbench: ^8.0
- laravel: 9.*
testbench: ^7.0
- laravel: 8.*
testbench: ^6.24
- php: 8.2
dependencies: lowest
dotenv: ^5.5.0
carbon: ^2.62.1
exclude:
- laravel: 10.*
php: 8.0
- php: 8.3
dependencies: lowest
dotenv: ^5.5.0
carbon: ^2.62.1


name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependencies }} - crdb.${{ matrix.cockroachdb }} ${{ matrix.os }}

Expand Down Expand Up @@ -65,7 +65,7 @@ jobs:
--no-interaction --no-update
- name: Require Minimum Packages for version
if: ${{ matrix.php == '8.2' && matrix.dependencies == 'lowest' }}
if: ${{ (matrix.php == '8.2' || matrix.php == '8.3') && matrix.dependencies == 'lowest' }}
run: >
composer require
"vlucas/phpdotenv:${{ matrix.dotenv }}" "nesbot/carbon:${{ matrix.carbon }}"
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
[![Total Downloads](https://img.shields.io/packagist/dt/ylsideas/cockroachdb-laravel.svg?style=flat-square)](https://packagist.org/packages/ylsideas/cockroachdb-laravel)
[![codecov](https://codecov.io/github/ylsideas/cockroachdb-laravel/branch/main/graph/badge.svg?token=GCCY3KZHXQ)](https://codecov.io/github/ylsideas/cockroachdb-laravel)
[![Help Fund](https://img.shields.io/github/sponsors/peterfox?style=flat-square)](https://github.com/sponsors/peterfox)
[![License](http://poser.pugx.org/ylsideas/cockroachdb-laravel/license)](https://packagist.org/packages/ylsideas/cockroachdb-laravel)
[![PHP Version Require](http://poser.pugx.org/ylsideas/cockroachdb-laravel/require/php)](https://packagist.org/packages/ylsideas/cockroachdb-laravel)

A driver/grammar for Laravel that works with CockroachDB. While CockroachDB is compatible with Postgresql, this support
is not 1 to 1 meaning you may run into issues, this driver hopes to resolve those problems as much as possible.

Laravel 8 through to Laravel 10 is supported and tested against CockroachDB 2.5.
Laravel 9 through to Laravel 10 is supported and tested against CockroachDB 22 & 23.

### Supporting Open Source

Expand Down Expand Up @@ -84,6 +86,10 @@ Cockroach Serverless requires you to provide a cluster with connection.
Laravel doesn't provide this out of the box, so, it's being implemented as an extra `cluster` parameter in the
database config. Just pass the cluster identification from CockroachDB Serverless.

### Schema Dumps
You may use schema dumps. I'm not 100% sure the functionality is correct in line with other drivers.
Please raise an issue if it isn't working as expect for you.

```php
'crdb' => [
'driver' => 'crdb',
Expand Down Expand Up @@ -125,7 +131,7 @@ docker-composer up -d
If you need to you may run the docker compose file with different cockroachdb
versions
```shell
VERSION=v21.2.15 docker-compose up -d
VERSION=v23.1.13 docker-compose up -d
```

Then run the following PHP script to create a test database and user
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
}
],
"require": {
"php": "^8.0",
"php": "^8.1",
"spatie/laravel-package-tools": "^1.9.2",
"illuminate/contracts": "10.*|9.*|8.*"
},
Expand Down
54 changes: 32 additions & 22 deletions src/CockroachDbConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,70 @@
namespace YlsIdeas\CockroachDb;

use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\Grammar;
use Illuminate\Database\Grammar as BaseGrammar;
use Illuminate\Database\PDO\PostgresDriver;
use Illuminate\Database\PostgresConnection;
use Illuminate\Database\Schema\PostgresSchemaState;
use Illuminate\Filesystem\Filesystem;
use YlsIdeas\CockroachDb\Builder\CockroachDbBuilder;
use YlsIdeas\CockroachDb\Processor\CockroachDbProcessor;
use YlsIdeas\CockroachDb\Builder\CockroachDbBuilder as DbBuilder;
use YlsIdeas\CockroachDb\Processor\CockroachDbProcessor as DbProcessor;
use YlsIdeas\CockroachDb\Query\CockroachGrammar as QueryGrammar;
use YlsIdeas\CockroachDb\Schema\CockroachGrammar as SchemaGrammar;
use YlsIdeas\CockroachDb\Schema\CockroachSchemaState as SchemaState;

class CockroachDbConnection extends PostgresConnection implements ConnectionInterface
{
/**
* Get the default query grammar instance.
*
* @return Grammar
* @return BaseGrammar
*/
protected function getDefaultQueryGrammar()
protected function getDefaultQueryGrammar(): BaseGrammar
{
return $this->withTablePrefix(new QueryGrammar());
return $this->withTablePrefix($this->setConnection(new QueryGrammar()));
}

/**
* Get a schema builder instance for the connection.
*
* @return \Illuminate\Database\Schema\PostgresBuilder
* @return DbBuilder
*/
public function getSchemaBuilder()
public function getSchemaBuilder(): DbBuilder
{
if ($this->schemaGrammar === null) {
$this->useDefaultSchemaGrammar();
}

return new CockroachDbBuilder($this);
return new DbBuilder($this);
}

/**
* Get the default schema grammar instance.
*
* @return Grammar
* @return BaseGrammar
*/
protected function getDefaultSchemaGrammar(): Grammar
protected function getDefaultSchemaGrammar(): BaseGrammar
{
return $this->withTablePrefix(new SchemaGrammar());
return $this->withTablePrefix($this->setConnection(new SchemaGrammar()));
}

/**
* Get the schema state for the connection.
*
* @param \Illuminate\Filesystem\Filesystem|null $files
* @param callable|null $processFactory
* @return \Illuminate\Database\Schema\PostgresSchemaState
* @return SchemaState
* @phpstan-ignore-next-line base class has fixed type that we can't correct
*/
public function getSchemaState(Filesystem $files = null, callable $processFactory = null): PostgresSchemaState
public function getSchemaState(Filesystem $files = null, callable $processFactory = null)
{
return new PostgresSchemaState($this, $files, $processFactory);
return new SchemaState($this, $files, $processFactory);
}

/**
* Get the default post processor instance.
*
* @return CockroachDbProcessor
* @return DbProcessor
*/
protected function getDefaultPostProcessor(): CockroachDbProcessor
protected function getDefaultPostProcessor(): DbProcessor
{
return new CockroachDbProcessor();
return new DbProcessor();
}

/**
Expand All @@ -80,4 +78,16 @@ protected function getDoctrineDriver()
{
return new PostgresDriver();
}

/**
* Required to set the connection. This isn't compatible with older Laravel versions
*/
protected function setConnection(BaseGrammar $grammar): BaseGrammar
{
if (method_exists($grammar, 'setConnection')) {
return $grammar->setConnection($this);
}

return $grammar;
}
}
59 changes: 59 additions & 0 deletions src/Schema/CockroachSchemaState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace YlsIdeas\CockroachDb\Schema;

use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\SchemaState;

class CockroachSchemaState extends SchemaState
{
/**
* Dump the database's schema into a file.
*
* @param \Illuminate\Database\Connection $connection
* @param string $path
* @return void
*/
public function dump(Connection $connection, $path): void
{
$query = $connection->getPdo()->query('SHOW CREATE ALL TABLES');
$query->execute();

$file = collect($query->fetchAll(\PDO::FETCH_COLUMN))->join(PHP_EOL);

$migrationRows = $connection
->table($this->migrationTable)
->get()
->map(fn (\stdClass $row) => (array) $row);

$statements = $connection->pretend(function (Connection $connection) use ($migrationRows) {
$connection->table($this->migrationTable)
->insert($migrationRows->all());
});

if ($statements !== []) {
$file .= PHP_EOL . $statements[0]['query'];
}

$this->files->put($path, $file);
}

/**
* Load the given schema file into the database.
*
* @param string $path
* @return void
* @throws FileNotFoundException
*/
public function load($path): void
{
$fileContents = $this->files->get($path);
if ($fileContents === '') {
throw new \RuntimeException(sprintf('file %s is empty', $path));
}

$pdo = $this->connection->getPdo();
$pdo->exec($fileContents);
}
}
83 changes: 83 additions & 0 deletions tests/Integration/Database/DumpAndLoadSchemaTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace YlsIdeas\CockroachDb\Tests\Integration\Database;

use Illuminate\Support\Facades\File;

class DumpAndLoadSchemaTest extends DatabaseTestCase
{
protected function setUp(): void
{
parent::setUp();
}

public function test_exporting_an_sql_dump()
{
File::ensureDirectoryExists(database_path('schema-test'));

if ($this->app['config']->get('database.default') !== 'testing') {
$this->artisan('db:wipe', ['--drop-views' => true]);
}

$options = [
'--path' => realpath(__DIR__.'/stubs/schema-dump-migrations'),
'--realpath' => true,
];

$this->artisan('migrate', $options);

$this->beforeApplicationDestroyed(function () use ($options) {
File::delete(database_path('schema-test/crdb-schema.sql'));

$this->artisan('migrate:rollback', $options);
});

$this->artisan('schema:dump', [
'--database' => 'crdb',
'--path' => database_path('schema-test/crdb-schema.sql'),
])
->assertSuccessful();

$this->assertFileExists(database_path('schema-test/crdb-schema.sql'));
}

public function test_importing_an_sql_dump()
{
// Make sure the schema direct exists first
File::ensureDirectoryExists(database_path('schema-test'));
File::copy(__DIR__ . '/stubs/schema-dump.sql', database_path('schema-test/crdb-schema.sql'));

if ($this->app['config']->get('database.default') !== 'testing') {
$this->artisan('db:wipe', ['--drop-views' => true]);
}

$options = [
'--path' => realpath(__DIR__.'/stubs/schema-dump-migrations'),
'--realpath' => true,
];

$this->beforeApplicationDestroyed(function () use ($options) {
File::delete(database_path('schema/crdb-schema.sql'));

$this->artisan('migrate:rollback', $options);
});

$this->artisan('migrate', [
...$options,
...['--schema-path' => database_path('schema-test/crdb-schema.sql')],
]);

$this->assertDatabaseCount('migrations', 2);

$this->assertDatabaseHas('migrations', [
'migration' => '2014_10_12_000000_create_members_table',
]);

$this->assertDatabaseHas('migrations', [
'migration' => '2014_10_12_000000_create_users_table',
]);

$this->assertDatabaseCount('users', 0);
$this->assertDatabaseCount('members', 0);
}
}
2 changes: 1 addition & 1 deletion tests/Integration/Database/MigrateWithRealpathTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected function setUp(): void
}

$options = [
'--path' => realpath(__DIR__.'/stubs/'),
'--path' => realpath(__DIR__.'/stubs/simple-migrations'),
'--realpath' => true,
];

Expand Down
2 changes: 1 addition & 1 deletion tests/Integration/Database/MigratorEventsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class MigratorEventsTest extends TestCase
protected function migrateOptions()
{
return [
'--path' => realpath(__DIR__.'/stubs/'),
'--path' => realpath(__DIR__.'/stubs/simple-migrations'),
'--realpath' => true,
];
}
Expand Down
Loading

0 comments on commit aa42814

Please sign in to comment.