Skip to content

Commit

Permalink
feat(spanner): add PG.OID type support for parameterized queries (#6788)
Browse files Browse the repository at this point in the history
* feat(spanner): add PG.OID type support for parameterized queries

* test(spanner): add testing for PG.OID type

* fix(spanner): fix typo

* test(spanner): fix typo

* test(spanner): add missing dependency

* fix(spanner): fix typo

---------

Co-authored-by: larkee <[email protected]>
Co-authored-by: Saransh Dhingra <[email protected]>
  • Loading branch information
3 people authored May 27, 2024
1 parent fcd0cfe commit 273683d
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 4 deletions.
1 change: 1 addition & 0 deletions Spanner/src/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
102 changes: 102 additions & 0 deletions Spanner/src/PgOid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Cloud\Spanner;

use Google\Cloud\Spanner\V1\TypeAnnotationCode;

/**
* 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:
* ```
* use Google\Cloud\Spanner\SpannerClient;
*
* $spanner = new SpannerClient();
* $pgOid = $spanner->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;
}
}
15 changes: 15 additions & 0 deletions Spanner/src/SpannerClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 12 additions & 3 deletions Spanner/src/ValueMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -64,6 +65,7 @@ class ValueMapper
self::TYPE_JSON,
self::TYPE_PG_NUMERIC,
self::TYPE_PG_JSONB,
self::TYPE_PG_OID,
self::TYPE_FLOAT32,
];

Expand All @@ -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,
];

/*
Expand All @@ -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,
];

/*
Expand All @@ -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,
];

/**
Expand Down Expand Up @@ -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:
Expand Down
37 changes: 37 additions & 0 deletions Spanner/tests/Snippet/PgOidTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Cloud\Spanner\Tests\Snippet;

use Google\Cloud\Core\Testing\Snippet\SnippetTestCase;
use Google\Cloud\Spanner\PgOid;

/**
* @group spanner
*/
class PgOidTest extends SnippetTestCase
{
public function testClass()
{
$expected = new PgOid('123');
$snippet = $this->snippetFromClass(PgOid::class);
$res = $snippet->invoke('pgOid');

$this->assertInstanceOf(PgOid::class, $res->returnVal());
$this->assertEquals($expected, $res->returnVal());
}
}
2 changes: 2 additions & 0 deletions Spanner/tests/Snippet/SpannerClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -261,6 +262,7 @@ public function factoriesProvider()
[Numeric::class, 'numeric'],
[PgNumeric::class, 'pgNumeric'],
[PgJsonB::class, 'pgJsonb'],
[PgOid::class, 'pgOid'],
];
}

Expand Down
36 changes: 35 additions & 1 deletion Spanner/tests/System/PgQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 [
Expand Down Expand Up @@ -639,7 +669,9 @@ function (array $res) {

return $res;
}
]
],
// pg_oid
[[5,4,3,2,1]],
];
}

Expand Down Expand Up @@ -685,6 +717,7 @@ public function arrayTypesEmptyProvider()
[Database::TYPE_DATE],
[Database::TYPE_PG_NUMERIC],
[Database::TYPE_PG_JSONB],
[Database::TYPE_PG_OID],
];
}

Expand Down Expand Up @@ -722,6 +755,7 @@ public function arrayTypesNullProvider()
[Database::TYPE_DATE],
[Database::TYPE_PG_NUMERIC],
[Database::TYPE_PG_JSONB],
[Database::TYPE_PG_OID],
];
}

Expand Down
1 change: 1 addition & 0 deletions Spanner/tests/Unit/ArrayTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function typesProvider()
// types (w/ typeAnnotation)
[Database::TYPE_PG_NUMERIC],
[Database::TYPE_PG_JSONB],
[Database::TYPE_PG_OID],
];
}

Expand Down
7 changes: 7 additions & 0 deletions Spanner/tests/Unit/SpannerClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
Expand Down
27 changes: 27 additions & 0 deletions Spanner/tests/Unit/ValueMapperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 = [
Expand Down

0 comments on commit 273683d

Please sign in to comment.