Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema dumps #30

Merged
merged 12 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all 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 .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 @@
{
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;

Check warning on line 91 in src/CockroachDbConnection.php

View check run for this annotation

Codecov / codecov/patch

src/CockroachDbConnection.php#L91

Added line #L91 was not covered by tests
}
}
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));

Check warning on line 53 in src/Schema/CockroachSchemaState.php

View check run for this annotation

Codecov / codecov/patch

src/Schema/CockroachSchemaState.php#L53

Added line #L53 was not covered by tests
}

$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
Loading