From 363b747cda8fa9ea24bdcc072340a4090f592055 Mon Sep 17 00:00:00 2001 From: Taka Oyama Date: Tue, 28 Nov 2023 14:14:10 +0900 Subject: [PATCH] feat: add NUMERIC column support --- src/Query/Processor.php | 4 ++++ src/Schema/Blueprint.php | 35 ++++++++++++++++++++++++++++++++++ src/Schema/Grammar.php | 23 ++++++++++++++++++++++ tests/Query/BuilderTest.php | 21 ++++++++++++++++---- tests/Schema/BlueprintTest.php | 6 ++++++ tests/TestCase.php | 3 +++ tests/test.ddl | 2 ++ 7 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/Query/Processor.php b/src/Query/Processor.php index 0cb9d7e0..d40c074c 100644 --- a/src/Query/Processor.php +++ b/src/Query/Processor.php @@ -17,6 +17,7 @@ namespace Colopl\Spanner\Query; +use Google\Cloud\Spanner\Numeric; use Google\Cloud\Spanner\Timestamp; use Illuminate\Database\Query\Builder; use Illuminate\Database\Query\Processors\Processor as BaseProcessor; @@ -37,6 +38,9 @@ public function processSelect(Builder $query, $results): array $dt->setTimezone(date_default_timezone_get()); $results[$index][$k] = $dt; } + if ($v instanceof Numeric) { + $results[$index][$k] = $v->formatAsString(); + } } } return $results; diff --git a/src/Schema/Blueprint.php b/src/Schema/Blueprint.php index 2f1a8c1c..f286568c 100644 --- a/src/Schema/Blueprint.php +++ b/src/Schema/Blueprint.php @@ -107,6 +107,30 @@ public function binary($column, $length = null) return $this->addColumn('binary', $column, compact('length')); } + /** + * @param string $column + * @param int $total + * @param int $places + * @param bool $unsigned + * @return ColumnDefinition + */ + public function decimal($column, $total = 38, $places = 9, $unsigned = false) + { + if ($total !== 38) { + $this->markAsNotSupported('decimal with precision other than 38'); + } + + if ($places !== 9) { + $this->markAsNotSupported('decimal with scale other than 9'); + } + + if ($unsigned) { + $this->markAsNotSupported('unsigned decimal'); + } + + return parent::decimal($column, $total, $places, $unsigned); + } + /** * @param string $column * @return ColumnDefinition @@ -140,6 +164,17 @@ public function floatArray($column) ]); } + /** + * @param string $column + * @return ColumnDefinition + */ + public function decimalArray($column) + { + return $this->addColumn('array', $column, [ + 'arrayType' => 'decimal' + ]); + } + /** * @param string $column * @param int|string|null $length diff --git a/src/Schema/Grammar.php b/src/Schema/Grammar.php index fa088ece..1b69c67d 100644 --- a/src/Schema/Grammar.php +++ b/src/Schema/Grammar.php @@ -424,6 +424,17 @@ protected function typeDouble(Fluent $column) return 'float64'; } + /** + * Create the column definition for a decimal type. + * + * @param Fluent $column + * @return string + */ + protected function typeDecimal(Fluent $column) + { + return "numeric"; + } + /** * Create the column definition for a date type. * @@ -555,6 +566,7 @@ protected function formatDefaultValue(Fluent $column, string $type, mixed $value 'bool' => $this->formatBoolValue($column, $value), 'date' => $this->formatDateValue($column, $value), 'float64' => $this->formatFloatValue($column, $value), + 'numeric' => $this->formatNumericValue($column, $value), 'int64' => $this->formatIntValue($column, $value), 'string' => $this->formatStringValue($column, $value), 'timestamp' => $this->formatTimestampValue($column, $value), @@ -613,6 +625,17 @@ protected function formatFloatValue(Fluent $column, mixed $value): string return (string)$value; } + /** + * @param Fluent $column + * @param mixed $value + * @return string + */ + protected function formatNumericValue(Fluent $column, mixed $value): string + { + assert(is_numeric($value)); + return (string)$value; + } + /** * @param Fluent $column * @param mixed $value diff --git a/tests/Query/BuilderTest.php b/tests/Query/BuilderTest.php index 923deb85..400e2ca4 100644 --- a/tests/Query/BuilderTest.php +++ b/tests/Query/BuilderTest.php @@ -18,19 +18,17 @@ namespace Colopl\Spanner\Tests\Query; use BadMethodCallException; -use Colopl\Spanner\Connection; use Colopl\Spanner\Query\Builder; -use Colopl\Spanner\Schema\Blueprint; use Colopl\Spanner\Tests\TestCase; use Colopl\Spanner\TimestampBound\ExactStaleness; use Google\Cloud\Spanner\Bytes; use Google\Cloud\Spanner\Duration; +use Google\Cloud\Spanner\Numeric; use Illuminate\Support\Str; -use const Grpc\STATUS_ALREADY_EXISTS; -use Illuminate\Database\Events\QueryExecuted; use Illuminate\Database\QueryException; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; +use const Grpc\STATUS_ALREADY_EXISTS; class BuilderTest extends TestCase { @@ -428,6 +426,21 @@ public function testInterleaveTable(): void $this->assertDatabaseMissing($childTableName, $childUserItems[0]); } + public function test_insert_numeric_types(): void + { + $conn = $this->getDefaultConnection(); + $tableName = self::TABLE_NAME_TEST; + $qb = $conn->table($tableName); + + $row = $this->generateTestRow(); + $qb->insert($row); + + $insertedRow = $qb->get()->first(); + $numeric = $insertedRow['numericTest']; + $this->assertSame('123.456', $numeric); + $this->assertNull($insertedRow['nullableNumericTest']); + } + public function testInsertDatetime(): void { date_default_timezone_set('Asia/Tokyo'); diff --git a/tests/Schema/BlueprintTest.php b/tests/Schema/BlueprintTest.php index 7dd5731d..d9313d1f 100644 --- a/tests/Schema/BlueprintTest.php +++ b/tests/Schema/BlueprintTest.php @@ -36,6 +36,7 @@ public function testCreateTable(): void $table->uuid('id'); $table->integer('int'); $table->float('float'); + $table->decimal('decimal'); $table->string('name'); $table->text('text'); $table->dateTime('started_at'); @@ -52,6 +53,7 @@ public function testCreateTable(): void '`id` string(36) not null', '`int` int64 not null', '`float` float64 not null', + '`decimal` numeric not null', '`name` string(255) not null', '`text` string(max) not null', '`started_at` timestamp not null', @@ -218,6 +220,7 @@ public function test_array_types(): void $table->integerArray('int_array')->nullable(); $table->booleanArray('bool_array')->nullable(); $table->floatArray('float_array')->nullable(); + $table->decimalArray('decimal_array')->nullable(); $table->stringArray('string_array_undef')->nullable(); $table->stringArray('string_array_1', 1)->nullable(); $table->stringArray('string_array_max', 'max')->nullable(); @@ -234,6 +237,7 @@ public function test_array_types(): void '`int_array` array', '`bool_array` array', '`float_array` array', + '`decimal_array` array', '`string_array_undef` array', '`string_array_1` array', '`string_array_max` array', @@ -405,6 +409,7 @@ public function test_default_values(): void $table->bigInteger('bigint')->default(1); $table->float('float')->default(0.1); $table->double('double')->default(0.1); + $table->decimal('decimal')->default(123.456); $table->boolean('bool')->default(true); $table->string('string')->default('a'); $table->text('string_max')->default('a'); @@ -436,6 +441,7 @@ public function test_default_values(): void '`bigint` int64 not null default (1)', '`float` float64 not null default (0.1)', '`double` float64 not null default (0.1)', + '`decimal` numeric not null default (123.456)', '`bool` bool not null default (true)', '`string` string(255) not null default ("a")', '`string_max` string(max) not null default ("a")', diff --git a/tests/TestCase.php b/tests/TestCase.php index 329839ae..4a98154b 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -22,6 +22,7 @@ use Colopl\Spanner\SpannerServiceProvider; use Google\Cloud\Spanner\Bytes; use Google\Cloud\Spanner\Date; +use Google\Cloud\Spanner\Numeric; use Google\Cloud\Spanner\SpannerClient; use Illuminate\Foundation\Application; use Ramsey\Uuid\Uuid; @@ -62,6 +63,8 @@ protected function generateTestRow(): array 'nullableIntTest' => null, 'floatTest' => 123.456, 'nullableFloatTest' => null, + 'numericTest' => new Numeric('123.456'), + 'nullableNumericTest' => null, 'timestampTest' => new \DateTimeImmutable(), 'nullableTimestampTest' => null, 'dateTest' => new Date(new \DateTimeImmutable()), diff --git a/tests/test.ddl b/tests/test.ddl index b032809a..b7bfb70f 100644 --- a/tests/test.ddl +++ b/tests/test.ddl @@ -7,6 +7,8 @@ CREATE TABLE `Test` ( `nullableIntTest` INT64, `floatTest` FLOAT64 NOT NULL, `nullableFloatTest` FLOAT64, + `numericTest` NUMERIC NOT NULL, + `nullableNumericTest` NUMERIC, `timestampTest` TIMESTAMP NOT NULL, `nullableTimestampTest` TIMESTAMP, `dateTest` DATE NOT NULL,