diff --git a/Spanner/src/Database.php b/Spanner/src/Database.php index 57c035cfc9e3..69b2e13a6177 100644 --- a/Spanner/src/Database.php +++ b/Spanner/src/Database.php @@ -122,6 +122,7 @@ class Database const TYPE_PG_NUMERIC = 'pgNumeric'; const TYPE_PG_JSONB = 'pgJsonb'; const TYPE_JSON = TypeCode::JSON; + const TYPE_PG_OID = 'pgOid'; /** * @var ConnectionInterface diff --git a/Spanner/src/PgOid.php b/Spanner/src/PgOid.php new file mode 100644 index 000000000000..8a3d0b9a1ced --- /dev/null +++ b/Spanner/src/PgOid.php @@ -0,0 +1,102 @@ +pgOid('123'); + * ``` + */ +class PgOid implements ValueInterface, TypeAnnotationInterface +{ + /** + * @var string|null + */ + private ?string $value; + + /** + * @param string|null $value The OID value. + */ + public function __construct(?string $value) + { + $this->value = $value; + } + + /** + * Get the underlying value. + * + * @return string|null + */ + public function get(): ?string + { + return $this->value; + } + + /** + * Get the type. + * + * @access private + * @return int + */ + public function type(): int + { + return ValueMapper::TYPE_INT64; + } + + /** + * Get the type annotation code. + * This is to be used along type, to differentiate the value from TypeCode::INT64. + * + * @access private + * @return int + */ + public function typeAnnotation(): int + { + return TypeAnnotationCode::PG_OID; + } + + /** + * Format the value as a string. + * + * @return string + */ + public function formatAsString(): ?string + { + return (string) $this->value; + } + + /** + * Format the value as a string. + * + * @return string + */ + public function __toString() + { + return (string) $this->value; + } +} diff --git a/Spanner/src/SpannerClient.php b/Spanner/src/SpannerClient.php index 78efabf19312..cb459122df90 100644 --- a/Spanner/src/SpannerClient.php +++ b/Spanner/src/SpannerClient.php @@ -823,6 +823,21 @@ public function pgJsonb($value) return new PgJsonb($value); } + /** + * Represents a value with a data type of + * [PG OID](https://cloud.google.com/spanner/docs/reference/postgresql/data-types) for the + * Postgres Dialect database. + * + * Example: + * ``` + * $pgOid = $spanner->pgOid('123'); + * ``` + */ + public function pgOid($value) + { + return new PgOid($value); + } + /** * Create an Int64 object. This can be used to work with 64 bit integers as * a string value while on a 32 bit platform. diff --git a/Spanner/src/ValueMapper.php b/Spanner/src/ValueMapper.php index c54605170b74..57771a434d6e 100644 --- a/Spanner/src/ValueMapper.php +++ b/Spanner/src/ValueMapper.php @@ -45,6 +45,7 @@ class ValueMapper const TYPE_JSON = TypeCode::JSON; const TYPE_PG_NUMERIC = 'pgNumeric'; const TYPE_PG_JSONB = 'pgJsonb'; + const TYPE_PG_OID = 'pgOid'; /** * @var array @@ -64,6 +65,7 @@ class ValueMapper self::TYPE_JSON, self::TYPE_PG_NUMERIC, self::TYPE_PG_JSONB, + self::TYPE_PG_OID, self::TYPE_FLOAT32, ]; @@ -79,6 +81,7 @@ class ValueMapper private static $typeToClassMap = [ self::TYPE_PG_NUMERIC => PgNumeric::class, self::TYPE_PG_JSONB => PgJsonb::class, + self::TYPE_PG_OID => PgOid::class, ]; /* @@ -90,6 +93,7 @@ class ValueMapper private static $typeCodes = [ self::TYPE_PG_NUMERIC => self::TYPE_NUMERIC, self::TYPE_PG_JSONB => self::TYPE_JSON, + self::TYPE_PG_OID => self::TYPE_INT64, ]; /* @@ -101,6 +105,7 @@ class ValueMapper private static $typeAnnotations = [ self::TYPE_PG_NUMERIC => TypeAnnotationCode::PG_NUMERIC, self::TYPE_PG_JSONB => TypeAnnotationCode::PG_JSONB, + self::TYPE_PG_OID => TypeAnnotationCode::PG_OID, ]; /** @@ -280,9 +285,13 @@ private function decodeValue($value, array $type) switch ($type['code']) { case self::TYPE_INT64: - $value = $this->returnInt64AsObject - ? new Int64($value) - : (int) $value; + if (isset($type['typeAnnotation']) && $type['typeAnnotation'] === TypeAnnotationCode::PG_OID) { + $value = new PgOid($value); + } else { + $value = $this->returnInt64AsObject + ? new Int64($value) + : (int) $value; + } break; case self::TYPE_TIMESTAMP: diff --git a/Spanner/tests/Snippet/PgOidTest.php b/Spanner/tests/Snippet/PgOidTest.php new file mode 100644 index 000000000000..7cdf215a3ad5 --- /dev/null +++ b/Spanner/tests/Snippet/PgOidTest.php @@ -0,0 +1,37 @@ +snippetFromClass(PgOid::class); + $res = $snippet->invoke('pgOid'); + + $this->assertInstanceOf(PgOid::class, $res->returnVal()); + $this->assertEquals($expected, $res->returnVal()); + } +} diff --git a/Spanner/tests/Snippet/SpannerClientTest.php b/Spanner/tests/Snippet/SpannerClientTest.php index d0f316779e80..564f75698cc1 100644 --- a/Spanner/tests/Snippet/SpannerClientTest.php +++ b/Spanner/tests/Snippet/SpannerClientTest.php @@ -39,6 +39,7 @@ use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Numeric; use Google\Cloud\Spanner\PgNumeric; +use Google\Cloud\Spanner\PgOid; use Google\Cloud\Spanner\PgJsonb; use Prophecy\Argument; @@ -261,6 +262,7 @@ public function factoriesProvider() [Numeric::class, 'numeric'], [PgNumeric::class, 'pgNumeric'], [PgJsonB::class, 'pgJsonb'], + [PgOid::class, 'pgOid'], ]; } diff --git a/Spanner/tests/System/PgQueryTest.php b/Spanner/tests/System/PgQueryTest.php index ccea014c7ef7..dd374e517544 100644 --- a/Spanner/tests/System/PgQueryTest.php +++ b/Spanner/tests/System/PgQueryTest.php @@ -542,6 +542,36 @@ public function testBindJsonbParameterNull() $this->assertCount($currentCount + 1, iterator_to_array($res)); } + public function testBindPgOidParameter() + { + $db = self::$database; + + $res = $db->execute('SELECT $1', [ + 'parameters' => [ + 'p1' => 1, + ], + 'types' => [ + 'p1' => Database::TYPE_PG_OID + ] + ]); + $this->assertCount(1, iterator_to_array($res)); + } + + public function testBindPgOidParameterNull() + { + $db = self::$database; + + $res = $db->execute('SELECT $1', [ + 'parameters' => [ + 'p1' => null, + ], + 'types' => [ + 'p1' => Database::TYPE_PG_OID + ] + ]); + $this->assertCount(1, iterator_to_array($res)); + } + public function arrayTypesProvider() { return [ @@ -639,7 +669,9 @@ function (array $res) { return $res; } - ] + ], + // pg_oid + [[5,4,3,2,1]], ]; } @@ -685,6 +717,7 @@ public function arrayTypesEmptyProvider() [Database::TYPE_DATE], [Database::TYPE_PG_NUMERIC], [Database::TYPE_PG_JSONB], + [Database::TYPE_PG_OID], ]; } @@ -722,6 +755,7 @@ public function arrayTypesNullProvider() [Database::TYPE_DATE], [Database::TYPE_PG_NUMERIC], [Database::TYPE_PG_JSONB], + [Database::TYPE_PG_OID], ]; } diff --git a/Spanner/tests/Unit/ArrayTypeTest.php b/Spanner/tests/Unit/ArrayTypeTest.php index 91524972c0e7..d04b0f2d7fd2 100644 --- a/Spanner/tests/Unit/ArrayTypeTest.php +++ b/Spanner/tests/Unit/ArrayTypeTest.php @@ -46,6 +46,7 @@ public function typesProvider() // types (w/ typeAnnotation) [Database::TYPE_PG_NUMERIC], [Database::TYPE_PG_JSONB], + [Database::TYPE_PG_OID], ]; } diff --git a/Spanner/tests/Unit/SpannerClientTest.php b/Spanner/tests/Unit/SpannerClientTest.php index 3f93170d22f1..beeb17033339 100644 --- a/Spanner/tests/Unit/SpannerClientTest.php +++ b/Spanner/tests/Unit/SpannerClientTest.php @@ -33,6 +33,7 @@ use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\InstanceConfiguration; use Google\Cloud\Spanner\PgJsonb; +use Google\Cloud\Spanner\PgOid; use Google\Cloud\Spanner\KeyRange; use Google\Cloud\Spanner\KeySet; use Google\Cloud\Spanner\Numeric; @@ -433,6 +434,12 @@ public function testPgJsonB() $this->assertInstanceOf(PgJsonb::class, $objVal); } + public function testPgOid() + { + $oidVal = $this->client->pgOid('123'); + $this->assertInstanceOf(PgOid::class, $oidVal); + } + public function testInt64() { $i64 = $this->client->int64('123'); diff --git a/Spanner/tests/Unit/ValueMapperTest.php b/Spanner/tests/Unit/ValueMapperTest.php index c33e7402904f..9b9780f3a2a0 100644 --- a/Spanner/tests/Unit/ValueMapperTest.php +++ b/Spanner/tests/Unit/ValueMapperTest.php @@ -158,6 +158,23 @@ public function testFormatParamsForExecuteSqlJsonB() $this->assertEquals(TypeAnnotationCode::PG_JSONB, $res['paramTypes']['json']['typeAnnotation']); } + public function testFormatParamsForExecuteSqlOid() + { + $val = '123'; + $params = [ + 'oid' => $val + ]; + $types = [ + 'oid' => Database::TYPE_PG_OID + ]; + + $res = $this->mapper->formatParamsForExecuteSql($params, $types); + + $this->assertEquals($val, $res['params']['oid']); + $this->assertEquals(TypeCode::INT64, $res['paramTypes']['oid']['code']); + $this->assertEquals(TypeAnnotationCode::PG_OID, $res['paramTypes']['oid']['typeAnnotation']); + } + public function testFormatParamsForExecuteSqlValueInterface() { $val = 'hello world'; @@ -1035,6 +1052,16 @@ public function testDecodeValuesJsonB() $this->assertEquals('{\"rating\":9,\"open\":true}', $res['rowName']); } + public function testDecodeValuesOid() + { + $res = $this->mapper->decodeValues( + $this->createField(Database::TYPE_PG_OID), + $this->createRow('123'), + Result::RETURN_ASSOCIATIVE + ); + $this->assertEquals('123', $res['rowName']); + } + public function testDecodeValuesAnonymousField() { $fields = [