Skip to content
Open
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
19 changes: 17 additions & 2 deletions packages/database/src/QueryStatements/IntegerStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,29 @@ public function compile(DatabaseDialect $dialect): string
$this->default !== null ? "DEFAULT {$this->default}" : '',
$this->nullable ? '' : 'NOT NULL',
),
default => sprintf(
// Postgres has no unsigned integer type, so omit the keyword there.
DatabaseDialect::POSTGRESQL => sprintf(
'%s %s %s %s',
$name,
$this->type(),
$this->default !== null ? "DEFAULT {$this->default}" : '',
$this->nullable ? '' : 'NOT NULL',
),
DatabaseDialect::MYSQL => sprintf(
'%s %s %s %s %s',
$name,
is_int($this->size) ? DatabaseIntegerSize::fromBytes($this->size)->toString() : $this->size->toString(),
$this->type(),
$this->unsigned ? 'UNSIGNED' : '',
$this->default !== null ? "DEFAULT {$this->default}" : '',
$this->nullable ? '' : 'NOT NULL',
),
};
}

private function type(): string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer this to be a local variable instead of a method

{
return is_int($this->size)
? DatabaseIntegerSize::fromBytes($this->size)->toString()
: $this->size->toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public static function provide_fk_create_table_database_drivers(): Generator
<<<SQL
CREATE TABLE "books" (
"id" SERIAL PRIMARY KEY,
"author_id" INTEGER NOT NULL,
"author_id" INTEGER NOT NULL,
CONSTRAINT "fk_authors_books_author_id" FOREIGN KEY(author_id) REFERENCES authors(id) ON DELETE CASCADE ON UPDATE NO ACTION,
"name" VARCHAR(255) NOT NULL
);
Expand Down Expand Up @@ -165,7 +165,7 @@ public static function provide_fk_create_table_database_drivers_explicit(): Gene
<<<SQL
CREATE TABLE "books" (
"id" SERIAL PRIMARY KEY,
"author_id" INTEGER NOT NULL,
"author_id" INTEGER NOT NULL,
CONSTRAINT "fk_authors_books_author_id" FOREIGN KEY(author_id) REFERENCES authors(id) ON DELETE CASCADE ON UPDATE NO ACTION,
"name" VARCHAR(255) NOT NULL
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace Tests\Tempest\Integration\Database\QueryStatements;

use PHPUnit\Framework\Attributes\Test;
use Tempest\Database\Config\DatabaseDialect;
use Tempest\Database\MigratesUp;
use Tempest\Database\Migrations\CreateMigrationsTable;
use Tempest\Database\QueryStatement;
use Tempest\Database\QueryStatements\CreateTableStatement;
use Tempest\Database\QueryStatements\DatabaseIntegerSize;
use Tempest\Database\QueryStatements\IntegerStatement;
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;

/**
* @internal
*/
final class IntegerStatementTest extends FrameworkIntegrationTestCase
{
#[Test]
public function unsigned_columns_are_created_on_whichever_database_is_configured(): void
{
$migration = new class() implements MigratesUp {
private(set) string $name = '0000_create_unsigned_integers_table';

public function up(): QueryStatement
{
return new CreateTableStatement('unsigned_integers')
->primary()
->integer('small', unsigned: true, size: DatabaseIntegerSize::SMALL)
->integer('regular', unsigned: true)
->integer('big', unsigned: true, size: DatabaseIntegerSize::BIG)
->integer('nullable_with_default', unsigned: true, nullable: true, default: 1);
}
};

$this->database->migrate(CreateMigrationsTable::class, $migration);

$this->expectNotToPerformAssertions();
}

#[Test]
public function postgres_is_not_told_about_a_keyword_it_does_not_have(): void
{
$statement = new IntegerStatement('votes', unsigned: true)->compile(DatabaseDialect::POSTGRESQL);

$this->assertStringNotContainsString('UNSIGNED', $statement);
$this->assertStringContainsString('INTEGER', $statement);
}

#[Test]
public function mysql_keeps_its_unsigned_range(): void
{
// MySQL supports `UNSIGNED`; dropping it globally would silently halve the requested range.
$this->assertStringContainsString(
'UNSIGNED',
new IntegerStatement('votes', unsigned: true)->compile(DatabaseDialect::MYSQL),
);

$this->assertStringNotContainsString(
'UNSIGNED',
new IntegerStatement('votes')->compile(DatabaseDialect::MYSQL),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you test that there's no unsigned when the flag is set to true? But then you test for exactly this in the previous test. I don't really understand the use of this assertion?

);
}

#[Test]
public function the_size_chooses_the_type(): void
{
foreach ([DatabaseDialect::MYSQL, DatabaseDialect::POSTGRESQL] as $dialect) {
$this->assertStringContainsString(
'SMALLINT',
new IntegerStatement('n', size: DatabaseIntegerSize::SMALL)->compile($dialect),
);

$this->assertStringContainsString(
'BIGINT',
new IntegerStatement('n', size: DatabaseIntegerSize::BIG)->compile($dialect),
);

// A byte count is rounded up to the size that can hold it.
$this->assertStringContainsString(
'BIGINT',
new IntegerStatement('n', size: 8)->compile($dialect),
);
}
}
}
Loading