Problem/Motivation

#3343634: Add "json" as core data type to Schema and Database API provides the ability to use JSON columns. After that's done, we'll need the ability to add indexes for the data in those columns.

There are two ways to achieve this: indexes on SQL expressions or indexes on generated columns. Expression indexes were attempted in the issue mentioned above, but they're not supported by MariaDB, and it's not realistic to exclude such a large user base from this.

Therefore, the only remaining option is to add support for generated columns, which can then be part of indexes just like regular ones.

References:
MySql: https://dev.mysql.com/doc/refman/9.7/en/create-table-generated-columns.html
MariaDb: https://mariadb.com/docs/server/reference/sql-statements/data-definition...
PostgreSql: https://www.postgresql.org/docs/current/ddl-generated-columns.html
SQLite: https://sqlite.org/gencol.html

Proposed resolution

Allow creating generated columns in the DB Schema Definition API.

Stored vs. virtual

A stored generated column computes its value on write and keeps it on disk, like a regular column. A virtual one computes it on read and stores nothing.

Virtual columns are worth having because one of the major points of this issue is indexing. MySQL, MariaDB and SQLite can all index a virtual generated column, so you pay for the index but not for a second copy of the data. For a JSON column that's a real saving: the extracted value can be large, and it's already in the JSON document.

PostgreSQL is the exception. It only supports virtual generated columns from version 18, and it cannot index them (which also rules out unique constraints on them). Portable code should therefore use stored, and that's what the API docs recommend.

Remaining tasks

Nope.

API changes

  • Column::generated() adds the definition of a generated table column.
  • Drupal\Core\Database\SchemaDefinition\GeneratedColumnStorage enumerates the possible storage options for the generated column.

Data model changes

Nope.

Release notes snippet

TBD.

Issue fork drupal-3586688

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

amateescu created an issue. See original summary.

amateescu’s picture

Title: [PP-1] Add support for generated columns to the DB Schema API » Add support for generated columns to the DB Schema API

This doesn't really need to be postponed.

mondrake’s picture

Issue summary: View changes

mondrake’s picture

mondrake’s picture

Status: Active » Needs review

All 3 core supported dbs implemented. The MR is reviewable, accepting that is built on top of #3411490: Replace array-based DB Schema API with a value object structure.

mondrake’s picture

Title: Add support for generated columns to the DB Schema API » [PP-1] Add support for generated columns to the DB Schema API
Issue summary: View changes
Status: Needs review » Postponed
mondrake’s picture

Title: [PP-1] Add support for generated columns to the DB Schema API » Add support for generated columns to the DB Schema API
Status: Postponed » Active
mondrake’s picture

mondrake’s picture

Title: Add support for generated columns to the DB Schema API » [PP-1] Add support for generated columns to the DB Schema API
Status: Active » Postponed
amateescu’s picture

Title: [PP-1] Add support for generated columns to the DB Schema API » Add support for generated columns to the DB Schema API
Status: Postponed » Needs work

The blocker is in :)

mondrake’s picture

Issue summary: View changes
Status: Needs work » Needs review
daffie’s picture

Status: Needs review » Needs work
daffie’s picture

Issue summary: View changes
amateescu’s picture

Status: Needs work » Needs review

Addressed all the reviews, and I think this is ready now.

mondrake’s picture

Filed #3616311: Deprecate Column::$dbSpecificExtra and Table::$dbSpecificExtra so we can start discussing about how to make the new API really db abstract.

daffie’s picture

Status: Needs review » Needs work

amateescu’s picture

Status: Needs work » Needs review

Removed the pgsql guard for virtual columns from the main MR, and started a new one for 11.x with it included.

mondrake’s picture

Looks very good to me now, cannot RTBC but +1 on that.

daffie’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record updates, +Needs issue summary update

All my remarks have been addressed on the PR.
The PR is for me RTBC.
However the IS and the CR need to be updated.

mondrake’s picture

Issue summary: View changes
mondrake’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record updates, -Needs issue summary update

Rebased and updated CR and IS.

mondrake’s picture

Title: Add support for generated columns to the DB Schema API » Add support for generated columns to the DB Schema Definition API
daffie’s picture

Status: Needs review » Reviewed & tested by the community

The updated IS and CR look good to me.
For me is it RTBC.

catch’s picture

Very excited for the possibilities this opens up.

Some of the sqlite schema introspection code is a bit painful, but unless there's some way to not expend that effort at all I doubt it can be simplified.

Apart from that, I only have one question:


 *       Prefer stored over virtual for portability: PostgreSQL supports virtual
 *       columns only from version 18 and cannot index them.

What's the reason we'd ever want a virtual generated column? When reading this, I was wondering if we should add it at all, but then I realised it would probably be harder to add support later if we hard-coded everything to stored, because we'd then have to change the APIs we're adding here. So maybe that's the answer but it would be good to flesh out in the issue summary.

amateescu’s picture

Issue summary: View changes

Virtual generated columns are very useful when disk space is a concern. MySQL, MariaDB and SQLite can all add indexes over them, so in that case you only pay the storage price for the index, not for the data as well.

Fleshed this out in the issue summary, and addressed the feedback on the MR :)

  • catch committed 0a09761f on 11.x
    feat: #3586688 Add support for generated columns to the DB Schema...

  • catch committed fa4fd791 on main
    feat: #3586688 Add support for generated columns to the DB Schema...
catch’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Fixed

Thanks for the issue summary update + the MR changes. I can't find anything else to complain about here, so let's do it. Committed/pushed to main and 11.x, thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

driskell’s picture

Just wanted to share some experience here.

Generated columns are great but if you add an index to one in MySQL or MariaDB the metadata format twists slightly and becomes completely incompatible with the INSTANT alteration algorithm.

The impact is that for super large tables the resulting rebuild for any and all alterations (like adding a new column) would take hours and significant disk explosion instead of being instantaneous. Potentially meaning seemingly innocuous update code times out and takes out the site. Worse for Galera as the Total Order Isolation would subsequently take ALL connections offline until it complete.

Might be worth a word of warning somewhere as I’ve seen others bitten a few times with this.

Specifically adding an index to a generated columns is the killer. All table alterations become rebuilds and you lose all benefit from modern MySQL and MariaDB

amateescu’s picture

That's very useful info, thanks! Do you also have some numbers for what "super large" means.. tens of millions rows, or less?

driskell’s picture

Where I’ve seen the issue it’s been millions but it’s more I think the table size - e.g. gigabytes.

It just worth noting that with all modern engines the INSTANT kicks in most of the time so it does metadata only change and takes effect without impacting DML. But if INSTANT can’t be used and neither can INPLACE (for index on generated it can’t INPLACE either) it will hit COPY - that means copying gigabytes to a new table with no concurrent DML.

Of course there’s an element of you end up with COPY eventually anyway as there’s only so many INSTANT before it hits a limit but that’s at least one-off but if you make a table incompatible with INSTANT every single operation copies it.

mondrake’s picture