Problem/Motivation

I am trying to create a field type that leverages the JSON and JSONB data types in Postgres. My goal is to have a field that can store JSON objects who's properties can be indexed. This is a Postgres specific feature.

Proposed resolution

Add json:normal and jsonb:normal mapped to json and jsonb Drupal field types for pgsql driver, Schema::getFieldTypeMap().

      'json:normal' => 'json',
      'jsonb:normal' => 'jsonb',
      );
    return $map;
  }

json vs. jsonb
The json and jsonb data types accept almost identical sets of values as input. The major practical difference is one of efficiency. The json data type stores an exact copy of the input text, which processing functions must reparse on each execution; while jsonb data is stored in a decomposed binary format that makes it slightly slower to input due to added conversion overhead, but significantly faster to process, since no reparsing is needed. jsonb also supports indexing, which can be a significant advantage.
https://www.postgresql.org/docs/current/datatype-json.html

Remaining tasks

  • Review field type map and approach

Data model changes

json and jsonb field types are available for use in schema for pgsql driver.

Release notes snippet

Basically I want to be able to paste JSON or parse a CSV with another custom module and store the object in this field with the intent of being able to query this data, treating it like a NoSQL store. Since Mongo isn't fully implemented, and would require two databases running instead of one, Postgres seems like a natural fit.

Comments

kevinquillen’s picture

Issue summary: View changes
larowlan’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, pgsql-json-jsonb-support.patch, failed testing.

kevinquillen’s picture

Looks like this feature is coming to MySQL as well:

http://mysqlserverteam.com/json-labs-release-native-json-data-type-and-b...

bzrudi71’s picture

Category: Support request » Feature request
Issue tags: +PostgreSQL
Parent issue: » #2157455: [Meta] Make Drupal 8 work with PostgreSQL or remove support from core before release

Tagging...

bzrudi71’s picture

Title: Support JSON and JSONB data types for Postgres » PostgreSQL: Support JSON and JSONB data types
kevinquillen’s picture

Related... ATEN just posted this and it feels relevant.

http://atendesigngroup.com/blog/speeding-complex-drupal-data-loads-custo...

If they were able to store as JSON in MYSQL or JSONB/JSON in Postgres it would save them a lot of steps.

I was able to do some queries, like this one, with some example data:

db_query("SELECT point
      FROM (SELECT jsonb_array_elements(field_jsondata_value) point FROM {node__field_jsondata}) points
      WHERE point->'DataType' = :type
        AND point->'TimeFrame' = :frame",
      array(':type' => '"Percent"', ':frame' => '"2009-2010"'));

jsonb_array_elements is a Postgres function, one of which makes this field type worth using. The field in question stored 2351 JSON objects, and this query executed and returned a response to me in less than a second (~250 records).

I am working at posting my module that supports this field type this week.

bzrudi71’s picture

The only problem I see here is that we are currently going to bump PG min version to 9.1.2 but JSON support starts with 9.2 ;-) Please see #2477413: Increase minimum version requirement for Postgres to 9.1.2.

kevinquillen’s picture

Correct - JSON starts at 9.2, JSONB introduced with 9.4.

As long as the minimum version is getting bumped, it would be nice if at least JSON was supported (9.2). How does this affect testing?

andypost’s picture

Version: 8.0.0-beta9 » 8.1.x-dev
Status: Needs work » Postponed
Related issues: +#2477413: Increase minimum version requirement for Postgres to 9.1.2

Suppose this no more issue, we just need to define "pgsql_type" for field

Fix for contrib module #2480201: Clean schema implementation for field schema

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

andypost’s picture

Status: Postponed » Active
andypost’s picture

9.1 no more supported https://www.postgresql.org/support/versioning/ so looks it's time to up suggested version to 9.2
This needs check across main distrosbut anyway this issue can have BC implementation for this types

andypost’s picture

Status: Active » Needs review
StatusFileSize
new643 bytes

Sort of that may work

mradcliffe’s picture

It might be good to add a method/property to the abstract Connection class for detecting that capability with a default of FALSE. Then it could be added to PostgreSQL and MySQL drivers. Since that class is abstract we should be able to add that to the API, right?

andypost’s picture

@mradcliffe Good idea, getCapabilities() makes sense! But then we need some flags to return.
For example this may be helpful for #2839683: Add support distinct on construction

daffie’s picture

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

mradcliffe’s picture

Jotting down some notes from a sandbox project of mine.

We can check JSON capabilities with something like the following. I wrote this for Drupal 7. :-)

  • mysql: 'SELECT JSON_VALID(:value)', [':value' => 'null']
  • pgsql: 'SELECT pg_typeof(:value::jsonb)', [':value' => '{}']

And if an exception is thrown, then we can set/cache false or something.

mradcliffe’s picture

Status: Postponed » Active

Implementing this should be unblocked now, but might be duplicated by work done in #3046696: Move from serialized columns to JSON encoded data wherever possible, or use allowed_classes.

Since minimum version is still 9.1, some sort of capabilities detection needs to be added to DBTNG.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

andypost’s picture

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

andypost’s picture

Status: Active » Needs work
Issue tags: +Needs reroll
anmolgoyal74’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new656 bytes

Re-rolled for 9.2.x

andypost’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

It just needs tests which could be extended for mysql (sqlite not clear)

+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
@@ -455,7 +455,12 @@ public function getFieldTypeMap() {
+    if (version_compare($this->connection->version(), '9.2', '>=')) {
+      /**  */

This condition no longer needed for 9.0+
https://www.drupal.org/node/2847322

mradcliffe’s picture

Issue tags: +Europe2020

I'm working on adding some tests for this today.

mradcliffe’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new3 KB
new3.55 KB
new3.49 KB

I uploaded a test-only patch, and then a patch that makes the suggested changes from @andypost in #32.

I removed the Needs tests tag.

I updated the issue summary with the template so the current proposed resolution is clear.

mradcliffe’s picture

+++ b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
@@ -275,6 +275,80 @@ public function testSchema() {
+         ->values(['test_field' => $insertData])
+         ->execute();

Resolved coding standard issue in this patch. No major changes.

mradcliffe’s picture

Thank you, interdiff. I need to look at it before uploading. :(

I created the patch from the test-only codebase. I re-applied the patch and fixed the code standard issue.

andypost’s picture

Status: Needs review » Reviewed & tested by the community

Looks ready for commiter

mradcliffe’s picture

Re-running tests. I think the patch is failing because json and jsonb fail spell check and need exceptions.

daffie’s picture

Status: Reviewed & tested by the community » Needs work
mradcliffe’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new3.82 KB
new191 bytes

Adds jsonb to dictionary. Stays at RTBC if it passes.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs framework manager review

fwiw https://www.drupal.org/project/json_field exists and implements a field type for Postgres's jsonb format as well as the json format.

I think for something to get core inclusion we need to implement it for all core drivers - ie. #3109340: [policy] Decide whether to require json support for all database drivers for Drupal 10 needs to get agreed first and then we need to decide how this is implemented. Going to discuss with other framework managers.

catch’s picture

Status: Needs review » Postponed

I think we should at least postpone this on #3109340: [policy] Decide whether to require json support for all database drivers for Drupal 10. I don't think we have to implement support for each database driver simultaneously in the same issue, but we should have viable patches for each so that we can see what it looks like - i.e. whether we need an abstraction layer or not to enable modules to use json/jsonb regardless of driver.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture

Status: Postponed » Needs work

Policy landed

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

anybody’s picture

gapple’s picture

I commented in the MySQL issue, that MySQL has a single JSON type that is most similar to Postgres' jsonb, which could possibly cause some confusion or unexpected behaviour if Drupal's json:normal field type acts differently between database engines.
The META issue might be the place to discuss the expected DB behaviour (raw vs. normalized), and whether Postgres' convention should be adopted by Drupal. (The Postgres documentation does recommend that jsonb be used as the default, unless legacy needs require the json data to not be normalized).

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture

re-roll and changed json:binary for jsonb

Also it could use pgsql_type in spec to provide this variation to keep it inline with Mysql - Re #49

Patch also removes try/catch and ref to policy issue, added type-hints

andypost’s picture

Title: PostgreSQL: Support JSON and JSONB data types » PostgreSQL: Support JSON data type
StatusFileSize
new5.26 KB
new3.29 KB

I removed jsonb as it could be used providing pgsql_type and to keep the driver inline with others

Also copied the test to test every returned datatype from #3373370: Support JSON fields in Sqlite schema

The difference is that bool returned as "true" when sqlite returns 1 (so pgsql results are typed)

andypost’s picture

StatusFileSize
new800 bytes
new3.29 KB
andypost’s picture

StatusFileSize
new707 bytes
new3.28 KB

fix CS

andypost’s picture

StatusFileSize
new3.19 KB
new2.63 KB

simplified test

andypost’s picture

StatusFileSize
new622 bytes
new2.62 KB

fix cs

anybody’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

Thanks @andypost for pushing this forward an unifying on json:normal

The PostgreSQL documentation says about json vs. jsonb:

The json and jsonb data types accept almost identical sets of values as input. The major practical difference is one of efficiency. The json data type stores an exact copy of the input text, which processing functions must reparse on each execution; while jsonb data is stored in a decomposed binary format that makes it slightly slower to input due to added conversion overhead, but significantly faster to process, since no reparsing is needed. jsonb also supports indexing, which can be a significant advantage.

(https://www.postgresql.org/docs/current/datatype-json.html)

I added that to the issue summary.

As the implemented data type here is now the same as for the other database engines (see meta issue), I'll set this RTBC.

But we should discuss, how to proceed with jsonb and potentially jsonpath (see https://www.postgresql.org/docs/current/datatype-json.html#DATATYPE-JSON...) for PostgreSQL.
Should that simply be provided by a contrib module? Or as a follow-up here?

anybody’s picture

Moving "Needs framework manager review" tag to the parent meta issue: #3343634: Add "json" as core data type to Schema and Database API

daffie’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php
    @@ -466,6 +466,8 @@ public function getFieldTypeMap() {
    +      'json:normal' => 'json',
    

    Lets use the JSONB field instead of the older JSON field.

  2. +++ b/core/modules/pgsql/tests/src/Kernel/pgsql/SchemaTest.php
    @@ -243,4 +243,57 @@ public function testPgsqlExtensionExists(): void {
    +    foreach ($test_data as $data) {
    +      $path = $data[0];
    +      $expected = $data[1];
    

    Can we rewrite this to foreach ($test_data as $path => $expected) {

  3. +++ b/core/modules/pgsql/tests/src/Kernel/pgsql/SchemaTest.php
    @@ -243,4 +243,57 @@ public function testPgsqlExtensionExists(): void {
    +    $query->addExpression('test_field -> :list -> 1', NULL, [
    +      ':list' => 'list',
    +    ]);
    

    Can we change this to: $query->addExpression('test_field -> 'list' -> 1');

  4. In the other 2 related issues the added testing has much overlap. Can we put that in a single test instead of duplecating the code.
  5. In the other 2 related issues the value of $test_data is different. Can we make the testing values the same?
andypost’s picture

I don't think we can use jsonb out of box because it doing normalization and removing duplicate keys (even in arrays)

So if someone willing to use it they always can set it by pgsql_type

Still not clear if we can provide alternative to addExpression() method

+++ b/core/modules/pgsql/tests/src/Kernel/pgsql/SchemaTest.php
@@ -243,4 +243,57 @@ public function testPgsqlExtensionExists(): void {
+        $query->addExpression('[test_field] -> :path1 ->> :path2', NULL, [
...
+        $query->addExpression('[test_field] ->> :path', NULL, [
...
+    $query->addExpression('test_field -> :list -> 1', NULL, [

++ to move 2 arguments case out of loop to make it more clear

but the biggest question is the last case where 1 (array index) can't be passed as argument and that's why I did hardcoding it into query

daffie’s picture

I don't think we can use jsonb out of box because it doing normalization and removing duplicate keys (even in arrays)

I do not have a problem with removing duplicate keys. For me that is a plus. Doing normalization is also a plus for me. Far more important for me is the fact that you can do indexing on JSONB fields. Without an index we will be doing full table scans and that is a no-go for me.

bradjones1’s picture

I would agree that indexing is a huge consideration here and is a strong +1 for jsonb. It is also more read performant, per the docs, despite being slightly slower on initial input.

The normalization and de-duplicating array keys doesn't feel like a blocker; is there a practical example of where this would be an issue for current code?

The point about being able to opt to the alternative implementation (json vs. jsonb) is a good one but I think whatever we go with needs to be a sensible default for the 90+% usecase. It wouldn't hurt also to add in some assertions for input data that could be triggered at least in development, e.g. if you attempt to store data that will be truncated (e.g., duplicate keys) if the default implementation is jsonb.

gapple’s picture

+1 for jsonb

as I noted in #49:
- the postgres docs recommend jsonb
- mysql's json type doesn't preserve duplicate keys, does order object keys, and performs whitespace normalization

I don't think removing duplicate keys should be a concern - any value in a PHP data type won't have duplicate keys, and if an input to json_decode has a duplicate key only the last value will be preserved (same behaviour as postgres & mysql). So, anything currently using json_encode/json_decode and a text field is already not able to have duplicate object keys.
JSON objects are defined as being unordered, so any implementation using it shouldn't rely on order being preserved. It's probably only worth a documentation note that order preservation shouldn't be expected when storing and retrieving from json database fields. The options to use a text field and rely on PHP's encode/decode behaviour, or set pgsql_type to json are available if key order is an issue for a particular case.

bradjones1’s picture

Status: Needs work » Closed (duplicate)

Per #3343634-14: Add "json" as core data type to Schema and Database API this is being consolidated into the parent issue for a single change.

Also appears the jsonb change was made on the parent, resolving #63.