Problem/Motivation

MySQL and MariaDb (sort of) supports JSON fields.

We don't have the type defined in \Drupal\Core\Database\Driver\mysql\Schema::getFieldTypeMap() which makes working with them trickier than it should be.

There's https://www.drupal.org/project/json_field but if you use that and then try to use the DbDumpCommand it breaks because we're missing information in the schema type map.

Proposed resolution

Add the information to the schema type map.

Remaining tasks

Add a test.

User interface changes

None

API changes

None

Data model changes

None

Release notes snippet

Issue fork drupal-3143512

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

alexpott created an issue. See original summary.

alexpott’s picture

Issue summary: View changes
Status: Active » Needs review
StatusFileSize
new481 bytes

This patch allows you to use the DbDumpCommand and https://www.drupal.org/project/json_field without issue.

alexpott’s picture

Title: Support JSON fields on MySQL » Support JSON fields in MySQL schema
alexpott’s picture

StatusFileSize
new440 bytes
new482 bytes

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.

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.

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.

damienmckenna’s picture

StatusFileSize
new490 bytes

Rerolled.

damienmckenna’s picture

damienmckenna’s picture

Is it possible to alter the database type map through hooks, or is it too low-level? i.e. could json_field handle this change directly without needing to extend the MySQL driver?

Would there be any downside to committing this without fully extending the data type system to support JSON?

What are the next steps? Should we start with this being a recommended patch for sites that want to use JSON Field?

alexpott’s picture

@DamienMcKenna well as part of the requirements for Drupal 10 is that all DBs support json in some way. So potentially this issue will have to be broadened to work out how to support all core supported db drivers. There is no alter to the field type map - it's very low level.

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.

damienmckenna’s picture

Version: 9.5.x-dev » 10.1.x-dev
Category: Task » Feature request
damienmckenna’s picture

More simply - when a field is created using JSON Field and this patch, should the column in the database have the column "type" set to "json"? Because it's showing up as "longtext" in MariaDB 10.4 in ddev:

MariaDB [db]> describe node__field_json;
+------------------+------------------+------+-----+---------+-------+
| Field            | Type             | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+-------+
| bundle           | varchar(128)     | NO   | MUL |         |       |
| deleted          | tinyint(4)       | NO   | PRI | 0       |       |
| entity_id        | int(10) unsigned | NO   | PRI | NULL    |       |
| revision_id      | int(10) unsigned | NO   | MUL | NULL    |       |
| langcode         | varchar(32)      | NO   | PRI |         |       |
| delta            | int(10) unsigned | NO   | PRI | NULL    |       |
| field_json_value | longtext         | YES  |     | NULL    |       |
+------------------+------------------+------+-----+---------+-------+
7 rows in set (0.001 sec)
damienmckenna’s picture

The queries work as expected in MariaDB:

MariaDB [db]> SELECT entity_id, JSON_VALUE(field_json_value, '$.type') as 'type' FROM node__field_json;
+-----------+------+
| entity_id | type |
+-----------+------+
|         4 | 1    |
+-----------+------+
1 row in set (0.001 sec)
MariaDB [db]> SELECT * FROM node__field_json WHERE JSON_VALUE(field_json_value, '$.type') = 1;
+---------+---------+-----------+-------------+----------+-------+------------------+
| bundle  | deleted | entity_id | revision_id | langcode | delta | field_json_value |
+---------+---------+-----------+-------------+----------+-------+------------------+
| article |       0 |         4 |           6 | en       |     0 | {"type":true}    |
+---------+---------+-----------+-------------+----------+-------+------------------+
1 row in set (0.001 sec)
alexpott’s picture

@DamienMcKenna the field type created by the json_field module depends on how you configure that module. If you select "JSON (stored as raw JSON in database)" then
the table looks like this:

mysql> desc node__field_test;
+------------------+------------------+------+-----+---------+-------+
| Field            | Type             | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+-------+
| bundle           | varchar(128)     | NO   | MUL |         |       |
| deleted          | tinyint(4)       | NO   | PRI | 0       |       |
| entity_id        | int(10) unsigned | NO   | PRI | NULL    |       |
| revision_id      | int(10) unsigned | NO   | MUL | NULL    |       |
| langcode         | varchar(32)      | NO   | PRI |         |       |
| delta            | int(10) unsigned | NO   | PRI | NULL    |       |
| field_test_value | json             | YES  |     | NULL    |       |
+------------------+------------------+------+-----+---------+-------+
7 rows in set (0.00 sec)
alexpott’s picture

And this table cannot be exported by core's database dump command because we're missing the mapping in Schema::getFieldTypeMap()

alexpott’s picture

MariaDB does not store native json in the same way as MySQL - it stores it as longtext so it is unaffected by this issue... https://mariadb.com/kb/en/json-data-type/

damienmckenna’s picture

Thanks. I was doing some final testing of JSON Field prior to a 1.0 release and was confused by what I saw in ddev's default MariaDB database, but this explains it.

rgpublic’s picture

Wow, that's a bit unexpected and disappointing. If I understand correctly, this means that in MariaDB the whole new JSON support is in fact just a glorified string operation, right? I'm also surprised that they claim that their benchmarks don't show a performance difference. I would have thought that it's better to store sth. preparsed in binary form - especially when you try to lookup something.

alexpott’s picture

FWIW we could add test coverage for this in \Drupal\Tests\system\Kernel\Scripts\DbDumpCommandTest - but actually since #3210913: DbDumpCommand fails when data type is not a mapped Drupal schema field name landed this is less of an issue as when we don't have a type in the map we'll just use what the database tells us.

That said, I still think we should do this issue.

kevinquillen’s picture

Exciting to see this happening.

Does this mean that now we can do this instead of use a serialized blob store?

      'data' => [
        'type' => 'blob',
        'mysql_type' => 'json',
        'pgsql_type' => 'jsonb',

Other than this patch and query functions, is there anything else to be aware of?

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

Moving to NW for the test coverage. If the solution changes per the current discussion please update issue summary also.

rgpublic’s picture

I wonder if it wouldn't be nice to have a "shortcut" so instead of this:

data' => [
        'type' => 'blob',
        'mysql_type' => 'json',
        'pgsql_type' => 'jsonb',
]

just this would be possible:

data' => [
        'type' => 'json',
]
gapple’s picture

PostgreSQL has two datatypes, json and jsonb with slightly different behaviour - it looks like MySQL's json type is more similar to PostgreSQL's jsonb, in that it does some normalization like removing duplicate object keys and extra whitespace when a value is stored in the field.

Should the MySQL driver define both to match the current patch for the PostgresSQL driver?

  'json:normal'     => 'JSON',
  'jsonb:normal'    => 'JSON',

The differing behaviour is probably something to note in documentation, and that jsonb will probably provide the best consistency of behaviour between db backends.

anybody’s picture

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

Looks the most compatible way is to provide only json:normal OOB and use mysql_type and pgsql_type to vary

andypost’s picture

andypost’s picture

Status: Needs work » Needs review
StatusFileSize
new3.24 KB

Here's a patch with test for returned data types

The issue I found comparing to sqlite and pgsql is that syntax for ->> operator is bit different

- sqlite, pgsql - select [field] -> "key1" ->> "key2"
- mysql is select [field] -> '$.key1.key1' or braces for array index select [field] -> '$.key1[0]'

andypost’s picture

StatusFileSize
new701 bytes
new3.23 KB

Fix cs

andypost’s picture

StatusFileSize
new2.83 KB
new2.1 KB

simplified tests

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Tests appear green

Verified the test locally running on ddev with mysql:8.0 I get
Undefined array key "json:normal"

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 32: 3143512-32.patch, failed testing. View results

smustgrave’s picture

Status: Needs work » Reviewed & tested by the community

Random unrelated failure that was popping up today.

bradjones1’s picture

Status: Reviewed & tested by the community » 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.

agoradesign made their first commit to this issue’s fork.