Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(spanner): add PG.OID type support for parameterized queries #6788

Merged
merged 7 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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