Skip to content

Commit

Permalink
feat(Spanner): Add support for PG JSONB (#5482)
Browse files Browse the repository at this point in the history
* Add support for JSONB datatype for PG dialect

* Added unit tests for JSONB

* Added system tests for PG_JSONB

* Change the JsonB class to PgJsonB, fix some tests

* Added snippet tests for JSONB

* Lint fixes

* Fixed unit tests for PHP 5.6

* Addressed PR comments

* Addressed PR comments

Co-authored-by: Brent Shaffer <[email protected]>
  • Loading branch information
saranshdhingra and bshaffer authored Oct 8, 2022
1 parent fb7bdb8 commit a7a8bb3
Show file tree
Hide file tree
Showing 13 changed files with 450 additions and 30 deletions.
1 change: 1 addition & 0 deletions Spanner/src/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class Database
const TYPE_STRUCT = TypeCode::STRUCT;
const TYPE_NUMERIC = TypeCode::NUMERIC;
const TYPE_PG_NUMERIC = 'pgNumeric';
const TYPE_PG_JSONB = 'pgJsonb';
const TYPE_JSON = TypeCode::JSON;

/**
Expand Down
114 changes: 114 additions & 0 deletions Spanner/src/PgJsonb.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php
/**
* Copyright 2022 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\Core\JsonTrait;
use Google\Cloud\Spanner\V1\TypeAnnotationCode;
use JsonSerializable;

/**
* Represents a value with a data type of
* [PG JSONB](https://cloud.google.com/spanner/docs/reference/postgresql/data-types) for the
* Postgres Dialect database.
*
* Example:
* ```
* use Google\Cloud\Spanner\SpannerClient;
*
* $spanner = new SpannerClient();
* $pgJsonb = $spanner->pgJsonb('{}');
* ```
*/
class PgJsonb implements ValueInterface, TypeAnnotationInterface
{
use JsonTrait;

/**
* @var string|null
*/
private $value;

/**
* @param string|array|JsonSerializable|null $value The value to be used as the JSONB string.
*/
public function __construct($value)
{
// null shouldn't be casted to an empty string
if (!is_null($value)) {
if (is_array($value) || $value instanceof JsonSerializable) {
$value = self::jsonEncode($value);
} else {
$value = (string) $value;
}
}
$this->value = $value;
}

/**
* Get the underlying value.
*
* @return string|null
*/
public function get()
{
return $this->value;
}

/**
* Get the type.
*
* @access private
* @return int
*/
public function type()
{
return ValueMapper::TYPE_JSON;
}

/**
* Get the type annotation code.
* This is to be used along type, to differentiate the value from TypeCode::JSON.
*
* @access private
* @return int
*/
public function typeAnnotation()
{
return TypeAnnotationCode::PG_JSONB;
}

/**
* Format the value as a string.
*
* @return string
*/
public function formatAsString()
{
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 @@ -630,6 +630,21 @@ public function pgNumeric($value)
return new PgNumeric($value);
}

/**
* Represents a value with a data type of
* [PG JSONB](https://cloud.google.com/spanner/docs/reference/postgresql/data-types) for the
* Postgres Dialect database.
*
* Example:
* ```
* $pgJsonb = $spanner->pgJsonb('{}');
* ```
*/
public function pgJsonb($value)
{
return new PgJsonb($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
11 changes: 11 additions & 0 deletions Spanner/src/ValueMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ValueMapper
const TYPE_NUMERIC = TypeCode::NUMERIC;
const TYPE_JSON = TypeCode::JSON;
const TYPE_PG_NUMERIC = 'pgNumeric';
const TYPE_PG_JSONB = 'pgJsonb';

/**
* @var array
Expand All @@ -60,6 +61,7 @@ class ValueMapper
self::TYPE_NUMERIC,
self::TYPE_JSON,
self::TYPE_PG_NUMERIC,
self::TYPE_PG_JSONB,
];

/*
Expand All @@ -73,6 +75,7 @@ class ValueMapper
*/
private static $typeToClassMap = [
self::TYPE_PG_NUMERIC => PgNumeric::class,
self::TYPE_PG_JSONB => PgJsonb::class,
];

/*
Expand All @@ -83,6 +86,7 @@ class ValueMapper
*/
private static $typeCodes = [
self::TYPE_PG_NUMERIC => self::TYPE_NUMERIC,
self::TYPE_PG_JSONB => self::TYPE_JSON,
];

/*
Expand All @@ -93,6 +97,7 @@ class ValueMapper
*/
private static $typeAnnotations = [
self::TYPE_PG_NUMERIC => TypeAnnotationCode::PG_NUMERIC,
self::TYPE_PG_JSONB => TypeAnnotationCode::PG_JSONB,
];

/**
Expand Down Expand Up @@ -317,6 +322,12 @@ private function decodeValue($value, array $type)
}
break;

case self::TYPE_JSON:
if (isset($type['typeAnnotation']) && $type['typeAnnotation'] === TypeAnnotationCode::PG_JSONB) {
$value = new PgJsonb($value);
}
break;

case self::TYPE_FLOAT64:
// NaN, Infinite and -Infinite are possible FLOAT64 values,
// but when the gRPC response is decoded, they are represented
Expand Down
37 changes: 37 additions & 0 deletions Spanner/tests/Snippet/PgJsonbTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
/**
* Copyright 2022 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\PgJsonb;

/**
* @group spanner
*/
class PgJsonbTest extends SnippetTestCase
{
public function testClass()
{
$expected = new PgJsonb('{}');
$snippet = $this->snippetFromClass(PgJsonb::class);
$res = $snippet->invoke('pgJsonb');

$this->assertInstanceOf(PgJsonb::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\PgJsonb;
use Prophecy\Argument;

/**
Expand Down Expand Up @@ -259,6 +260,7 @@ public function factoriesProvider()
[CommitTimestamp::class, 'commitTimestamp'],
[Numeric::class, 'numeric'],
[PgNumeric::class, 'pgNumeric'],
[PgJsonB::class, 'pgJsonb'],
];
}

Expand Down
Loading

0 comments on commit a7a8bb3

Please sign in to comment.