daffie / pgsql
Provides the PostgreSQL database driver with JSON entity storage.
Package info
Type:drupal-module
pkg:composer/daffie/pgsql
Requires
- daffie/drupal-core-pgsql-module-remover: ^1.0
- drupal/core: >=11.4 <12
This package is auto-updated.
Last update: 2026-07-25 15:27:42 UTC
README
pgsql is a Drupal database driver — a self-contained copy of core's pgsql
driver — that stores content-entity field data in PostgreSQL jsonb columns on
the entity base table ("embedded" storage) instead of in the relational data,
revision and dedicated field tables.
The driver remains a full PostgreSQL SQL backend. Every non-entity table
(cache_*, sessions, key_value*, config, watchdog, queue,
sequences, the search index, …) and every entity table that is not opted in
behaves exactly like core pgsql: normal columns, normal SQL, normal indexes.
Each pgsql query/DDL class delegates to its base (*Base) class — the vendored
core pgsql implementation — by default and only diverges when the target table
is a registered JSON-storage entity base table or one of its embedded children.
It is a drop-in replacement for core's pgsql module: it exposes the same
Drupal\pgsql\… namespace, so an existing site can switch to it by pointing the
database connection's namespace/autoload at this module — no changes to
site code or configuration are required. The driver passes the full Drupal
core test suite, so it behaves identically to core pgsql for every
non-opted-in table while transparently adding the JSON entity-storage layer on
top.
Requirements
- Drupal 11.4 and PostgreSQL. This module replaces core's
pgsqlmodule — it ships its own copy of the driver (the*Baseclasses) under the sameDrupal\pgsql\…namespace — so it does not depend on, and cannot coexist with, the corepgsqlmodule (see the note below). - A set of core modifications that route entity storage through the driver's JSONB layer. See docs/core-modifications.md.
Note: The Drupal core
pgsqlmodule is automatically deleted by the required packagedaffie/drupal-core-pgsql-module-remover. This module takes over theDrupal\pgsql\…namespace, so the core module must be removed to avoid a class collision.
Install with a Drupal recommended project
Based on the DDEV Quickstart for Drupal 11.
Create the project directory and configure DDEV:
mkdir -p my-drupal11-site && cd my-drupal11-site ddev config --project-type=drupal11 --docroot=web --database "postgres:18"Start DDEV (this may take a minute):
ddev startInstall Drupal and the driver via Composer:
ddev composer create-project drupal/recommended-project ddev composer require drush/drush ddev composer require daffie/pgsqlRun the Drupal installation and launch the site:
ddev drush site:install --account-name=admin --account-pass=admin -y ddev launch # or launch already logged in: ddev launch $(ddev drush uli)
Connection settings
$databases['default']['default'] = [
'driver' => 'pgsql',
'namespace' => 'Drupal\\pgsql\\Driver\\Database\\pgsql',
'autoload' => 'modules/custom/pgsql/src/Driver/Database/pgsql/',
// … host / database / username / password as for pgsql.
];
For test runs, point SIMPLETEST_DB at the driver:
pgsql://user:pass@host/db?module=pgsql.
Documentation
| Document | Contents |
|---|---|
| docs/storage.md | The JSONB storage layout, the embedded-table registry, and the driver's schema/read/write classes |
| docs/entity-queries.md | The entity query backend (conditions, sorts, revisions, translations, aggregates, relationships, workspaces) |
| docs/views.md | Views integration: config remapping, views data, query execution, plugins |
| docs/core-modifications.md | The core patches the driver relies on, and the per-module overrides the driver ships |
| docs/testing.md | Running the test suites, the test-branching policy, known limitations |
Module layout
pgsql.info.yml Module definition.
pgsql.services.yml Table-information registry + entity query factory.
src/Driver/Database/pgsql/ The database driver: Connection, Schema, Select,
Insert, Update, Upsert, TableInformation,
EmbeddedTableData + the write/insert traits.
src/EntityQuery/ JSONB-aware entity query backend
(QueryFactory, Query, QueryAggregate, Tables).
src/Plugin/views/ Views query, join, field, row, filter, argument
and relationship plugin overrides.
src/Hook/ Hook implementations wiring the overrides in
(entity types, views data/plugins, search,
node access).
src/modules/ Driver-specific replacements for handlers of
other core modules (user, taxonomy, views,
media, menu_link_content, content_moderation,
search, workspaces).
src/*Trait.php Shared JSONB SQL builders (unwind subqueries,
pending-revision queries, comment/taxonomy
helpers).
src/PgsqlServiceProvider.php Container wiring that depends on optional
modules (workspaces query, cookie auth).
tests/ Kernel tests for the JSONB storage behaviour.
assets/scaffold/ Files placed into a site by core-composer-scaffold
(see below); currently the settings.php snippet
that registers the driver's namespace/autoload.
assets/scaffold/
Holds files that drupal/core-composer-scaffold appends to, or copies into, a
site during composer install. They are not loaded by the module at
runtime — Composer places them where the site expects them.
pgsql-driver.settings.php— a settings.php snippet (no opening<?phptag; it is appended todefault.settings.php) that registers the driver'snamespaceandautoloadkeys on the default connection. Because the corepgsqlmodule is removed, the replacement driver must be autoloadable before the module system boots — otherwise any tool that pre-configures the database connection (e.g. DDEV'ssettings.ddev.php) fails withClass "Drupal\pgsql\Driver\Database\pgsql\Connection" not found. The snippet probesmodules/contrib/pgsqlthenmodules/pgsqland wires up whichever it finds.
The mapping that tells Composer where to place these files belongs in the
extra.drupal-scaffold.file-mapping section of this module's composer.json
(the settings snippet's header comment assumes that entry).
What is stored where (summary)
For an opted-in content entity type with base table e:
- The id, uuid, revision id and every single-value base field stored directly
in the base table remain real columns, laid out exactly as core
pgsqlwould create them. Lookups, joins and unique constraints on keys stay native. - Depending on the entity type, one or more
jsonbcolumns onecarry the rest (the__dataprefix is reserved for the driver):
| Entity type | Columns | Write semantics |
|---|---|---|
| non-revisionable, non-translatable | __data | replace |
| revisionable | __data_all_revisions | append (revision history) |
__data_current_revision | replace (default revision) | |
__data_latest_revision | replace (newest revision) | |
| translatable | __data_translations | replace |
- The data, revision and dedicated field tables (
e_field_data,e_revision,e__<field>, …) are not physically created. They exist as logical tables: entries in thepgsql_table_informationregistry that record whichjsonbcolumn and document key hold their rows.
See docs/storage.md for the full layout and document shapes.