Skip to content

Commit

Permalink
Support Firebird
Browse files Browse the repository at this point in the history
  • Loading branch information
staudenmeir committed Mar 8, 2024
1 parent d0ae740 commit f308d38
Show file tree
Hide file tree
Showing 16 changed files with 132 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
php: [ 8.3, 8.2, 8.1 ]
database: [ mysql, mariadb, pgsql, sqlite, sqlsrv, singlestore ]
database: [ mysql, mariadb, pgsql, sqlite, sqlsrv, singlestore, firebird ]
release: [ stable, lowest ]
include:
- php: 8.3
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Supports Laravel 5.5+.
- SQL Server 2008+
- Oracle 9.2+
- SingleStore 8.1+
- Firebird

## Installation

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"illuminate/database": "^10.0"
},
"require-dev": {
"harrygulliford/laravel-firebird": "^3.2",
"orchestra/testbench": "^8.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10.1",
Expand Down
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ services:
- test
volumes:
- .docker/singlestore/init.sql:/init.sql
firebird:
image: jacobalberty/firebird:latest
environment:
FIREBIRD_DATABASE: 'test.fdb'
ISC_PASSWORD: 'password'
EnableLegacyClientAuth: 'true'
networks:
- test

networks:
test:
Expand Down
19 changes: 19 additions & 0 deletions src/Connections/FirebirdConnection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Staudenmeir\LaravelCte\Connections;

use Staudenmeir\LaravelCte\Query\FirebirdBuilder;
use HarryGulliford\Firebird\FirebirdConnection as Base;

class FirebirdConnection extends Base
{
/**
* Get a new query builder instance.
*
* @return \Illuminate\Database\Query\Builder
*/
public function query()
{
return new FirebirdBuilder($this);
}
}
6 changes: 5 additions & 1 deletion src/Connectors/ConnectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Database\Connection;
use Illuminate\Database\Connectors\ConnectionFactory as Base;
use InvalidArgumentException;
use Staudenmeir\LaravelCte\Connections\FirebirdConnection;
use Staudenmeir\LaravelCte\Connections\MySqlConnection;
use Staudenmeir\LaravelCte\Connections\OracleConnection;
use Staudenmeir\LaravelCte\Connections\PostgresConnection;
Expand All @@ -28,7 +29,9 @@ class ConnectionFactory extends Base
*/
protected function createConnection($driver, $connection, $database, $prefix = '', array $config = [])
{
if ($driver !== 'singlestore' && $resolver = Connection::getResolver($driver)) {
$resolver = Connection::getResolver($driver);

if (!in_array($driver, ['singlestore', 'firebird']) && $resolver) {
return $resolver($connection, $database, $prefix, $config); // @codeCoverageIgnore
}

Expand All @@ -39,6 +42,7 @@ protected function createConnection($driver, $connection, $database, $prefix = '
'sqlsrv' => new SqlServerConnection($connection, $database, $prefix, $config),
'oracle' => new OracleConnection($connection, $database, $prefix, $config),
'singlestore' => new SingleStoreConnection($connection, $database, $prefix, $config),
'firebird' => new FirebirdConnection($connection, $database, $prefix, $config),
default => throw new InvalidArgumentException("Unsupported driver [{$driver}]"),
};
}
Expand Down
2 changes: 2 additions & 0 deletions src/Eloquent/QueriesExpressions.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Staudenmeir\LaravelCte\Eloquent;

use Staudenmeir\LaravelCte\Query\Builder;
use Staudenmeir\LaravelCte\Query\FirebirdBuilder;
use Staudenmeir\LaravelCte\Query\OracleBuilder;
use Staudenmeir\LaravelCte\Query\SingleStoreBuilder;

Expand All @@ -20,6 +21,7 @@ protected function newBaseQueryBuilder()
return match ($connection->getDriverName()) {
'oracle' => new OracleBuilder($connection),
'singlestore' => new SingleStoreBuilder($connection),
'firebird' => new FirebirdBuilder($connection),
default => new Builder($connection),
};
}
Expand Down
11 changes: 11 additions & 0 deletions src/Query/FirebirdBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Staudenmeir\LaravelCte\Query;

use HarryGulliford\Firebird\Query\Builder as Base;
use Staudenmeir\LaravelCte\Query\Traits\BuildsExpressionQueries;

class FirebirdBuilder extends Base
{
use BuildsExpressionQueries;
}
11 changes: 11 additions & 0 deletions src/Query/Grammars/FirebirdGrammar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Staudenmeir\LaravelCte\Query\Grammars;

use HarryGulliford\Firebird\Query\Grammars\FirebirdGrammar as Base;
use Staudenmeir\LaravelCte\Query\Grammars\Traits\CompilesFirebirdExpressions;

class FirebirdGrammar extends Base
{
use CompilesFirebirdExpressions;
}
3 changes: 2 additions & 1 deletion src/Query/Grammars/Traits/CompilesExpressions.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Str;
use Staudenmeir\LaravelCte\Query\Builder as CteBuilder;
use Staudenmeir\LaravelCte\Query\FirebirdBuilder;
use Staudenmeir\LaravelCte\Query\SingleStoreBuilder;

trait CompilesExpressions
Expand Down Expand Up @@ -108,7 +109,7 @@ public function compileSelect(Builder $query)
{
$sql = parent::compileSelect($query);

if ($query instanceof CteBuilder || $query instanceof SingleStoreBuilder) {
if ($query instanceof CteBuilder || $query instanceof SingleStoreBuilder || $query instanceof FirebirdBuilder) {
if ($query->unionExpressions) {
$sql = $this->compileExpressions($query, $query->unionExpressions) . " $sql";
}
Expand Down
25 changes: 25 additions & 0 deletions src/Query/Grammars/Traits/CompilesFirebirdExpressions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Staudenmeir\LaravelCte\Query\Grammars\Traits;

use Illuminate\Database\Query\Builder;

trait CompilesFirebirdExpressions
{
use CompilesExpressions;

/**
* Compile an insert statement using a subquery into SQL.
*
* @param \Illuminate\Database\Query\Builder $query
* @param array $columns
* @param string $sql
* @return string
*/
public function compileInsertUsing(Builder $query, array $columns, string $sql)
{
$insert = "insert into {$this->wrapTable($query->from)} ({$this->columnize($columns)}) ";

return "$insert{$this->compileExpressions($query, $query->expressions)} $sql";
}
}
2 changes: 2 additions & 0 deletions src/Query/Traits/BuildsExpressionQueries.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Database\Query\Grammars\Grammar;
use Illuminate\Database\Query\Processors\Processor;
use RuntimeException;
use Staudenmeir\LaravelCte\Query\Grammars\FirebirdGrammar;
use Staudenmeir\LaravelCte\Query\Grammars\MySqlGrammar;
use Staudenmeir\LaravelCte\Query\Grammars\OracleGrammar;
use Staudenmeir\LaravelCte\Query\Grammars\PostgresGrammar;
Expand Down Expand Up @@ -78,6 +79,7 @@ protected function getQueryGrammar(Connection $connection)
'sqlsrv' => new SqlServerGrammar(),
'oracle' => new OracleGrammar(),
'singlestore' => new SingleStoreGrammar(),
'firebird' => new FirebirdGrammar(),
default => throw new RuntimeException('This database is not supported.'), // @codeCoverageIgnore
};

Expand Down
19 changes: 12 additions & 7 deletions tests/EloquentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ public function testOuterUnion()

public function testInsertUsing()
{
$query = User::selectRaw('(select max(id) from posts) + id as id')
$id = match ($this->connection) {
'firebird' => '(select max("id") from "posts") + "id" as "id"',
default => '(select max(id) from posts) + id as id',
};

$query = User::selectRaw($id)
->addSelect('id as post_id')
->selectRaw('1 as views')
->where('id', '>', 1);
Expand All @@ -91,7 +96,7 @@ public function testInsertUsing()

public function testUpdate()
{
if ($this->connection === 'mariadb') {
if (in_array($this->connection, ['mariadb', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -106,7 +111,7 @@ public function testUpdate()

public function testUpdateWithJoin()
{
if ($this->connection === 'mariadb') {
if (in_array($this->connection, ['mariadb', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -121,7 +126,7 @@ public function testUpdateWithJoin()

public function testUpdateWithLimit()
{
if (in_array($this->connection, ['mariadb', 'sqlsrv', 'singlestore'])) {
if (in_array($this->connection, ['mariadb', 'sqlsrv', 'singlestore', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -138,7 +143,7 @@ public function testUpdateWithLimit()

public function testDelete()
{
if ($this->connection === 'mariadb') {
if (in_array($this->connection, ['mariadb', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -151,7 +156,7 @@ public function testDelete()

public function testDeleteWithJoin()
{
if ($this->connection === 'mariadb') {
if (in_array($this->connection, ['mariadb', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -165,7 +170,7 @@ public function testDeleteWithJoin()

public function testDeleteWithLimit()
{
if (in_array($this->connection, ['mariadb', 'sqlsrv'])) {
if (in_array($this->connection, ['mariadb', 'sqlsrv', 'firebird'])) {
$this->markTestSkipped();
}

Expand Down
31 changes: 18 additions & 13 deletions tests/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ public function testWithExpression()

public function testWithRecursiveExpression()
{
if ($this->connection === 'singlestore') {
$query = 'select 1 as number from `users` limit 1 union all select number + 1 from numbers where number < 3';
} else {
$query = 'select 1 union all select number + 1 from numbers where number < 3';
}
$query = match ($this->connection) {
'singlestore' => 'select 1 as number from `users` limit 1 union all select number + 1 from numbers where number < 3',
'firebird' => 'select 1 from RDB$DATABASE union all select "number" + 1 from "numbers" where "number" < 3',
default => 'select 1 union all select number + 1 from numbers where number < 3',
};

$rows = DB::table('numbers')
->withRecursiveExpression('numbers', $query, ['number'])
Expand Down Expand Up @@ -117,16 +117,21 @@ public function testOuterUnion()
->where('id', 2)
)
->withExpression('u', DB::table('users'))
->orderBy('id')
->when($this->connection !== 'firebird', fn (BaseBuilder $query) => $query->orderBy('id'))
->get();

$this->assertEquals([1, 2], $rows->pluck('id')->all());
}

public function testInsertUsing()
{
$id = match ($this->connection) {
'firebird' => '(select max("id") from "posts") + "id" as "id"',
default => '(select max(id) from posts) + id as id',
};

$query = DB::table('users')
->selectRaw('(select max(id) from posts) + id as id')
->selectRaw($id)
->addSelect('id as post_id')
->selectRaw('1 as views')
->where('id', '>', 1);
Expand Down Expand Up @@ -161,7 +166,7 @@ public function testInsertUsingWithRecursionLimit()

public function testUpdate()
{
if ($this->connection === 'mariadb') {
if (in_array($this->connection, ['mariadb', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -177,7 +182,7 @@ public function testUpdate()

public function testUpdateWithJoin()
{
if ($this->connection === 'mariadb') {
if (in_array($this->connection, ['mariadb', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -193,7 +198,7 @@ public function testUpdateWithJoin()

public function testUpdateWithLimit()
{
if (in_array($this->connection, ['mariadb', 'sqlsrv', 'singlestore'])) {
if (in_array($this->connection, ['mariadb', 'sqlsrv', 'singlestore', 'firebird'])) {
$this->markTestSkipped();
}

Expand Down Expand Up @@ -227,7 +232,7 @@ public function testUpdateFrom()

public function testDelete()
{
if ($this->connection === 'mariadb') {
if (in_array($this->connection, ['mariadb', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -241,7 +246,7 @@ public function testDelete()

public function testDeleteWithJoin()
{
if ($this->connection === 'mariadb') {
if (in_array($this->connection, ['mariadb', 'firebird'])) {
$this->markTestSkipped();
}

Expand All @@ -256,7 +261,7 @@ public function testDeleteWithJoin()

public function testDeleteWithLimit()
{
if (in_array($this->connection, ['mariadb', 'sqlsrv'])) {
if (in_array($this->connection, ['mariadb', 'sqlsrv', 'firebird'])) {
$this->markTestSkipped();
}

Expand Down
6 changes: 4 additions & 2 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Staudenmeir\LaravelCte\Tests;

use HarryGulliford\Firebird\FirebirdServiceProvider;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
Expand All @@ -21,7 +22,8 @@ protected function setUp(): void

parent::setUp();

Schema::dropAllTables();
Schema::dropIfExists('users');
Schema::dropIfExists('posts');

Schema::create('users', function (Blueprint $table) {
$table->unsignedBigInteger('id')->unique();
Expand Down Expand Up @@ -75,6 +77,6 @@ protected function getEnvironmentSetUp($app)

protected function getPackageProviders($app)
{
return [SingleStoreProvider::class];
return [SingleStoreProvider::class, FirebirdServiceProvider::class];
}
}
10 changes: 10 additions & 0 deletions tests/config/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,14 @@
PDO::ATTR_EMULATE_PREPARES => true,
]) : [],
],
'firebird' => [
'driver' => 'firebird',
'host' => 'firebird',
'port' => '3050',
'database' => '/firebird/data/test.fdb',
'username' => 'sysdba',
'password' => 'password',
'charset' => 'UTF8',
'role' => null,
],
];

0 comments on commit f308d38

Please sign in to comment.