Skip to content

PostgreSQL 18: PgSqlDriver getColumns() fails with "invalid attnum ... for relation pg_attrdef" #329

@osipkaadnium

Description

@osipkaadnium

Version: nette/database v3.2.8

Bug Description

Nette\Database\Drivers\PgSqlDriver::getColumns() fails during PostgreSQL table structure introspection on PostgreSQL 18.

The problem happens when using Nette\Database\Explorer, for example:

$explorer->table('users')->fetch();

The query itself is not the issue. The exception is thrown before the actual table query is executed, while Nette Database is loading table metadata via Structure / DiscoveredConventions.

The error is:

Nette\Database\DriverException #XX000
SQLSTATE[XX000]: Internal error: 7 ERROR: invalid attnum 10 for relation "pg_attrdef"

The stack trace points to:

Nette\Database\Drivers\PgSqlDriver.php
Nette\Database\Structure.php
Nette\Database\Conventions\DiscoveredConventions.php
Nette\Database\Explorer.php

The issue seems to be caused by this expression in PgSqlDriver::getColumns():
pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass)::varchar AS default

and also here:
substring(pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass) from 'nextval[(]''"?([^''"]+)')

With PostgreSQL 18 this fails with invalid attnum ... for relation "pg_attrdef".

Steps To Reproduce

  1. Use PostgreSQL 18
  2. Use nette/database v3.2.8.
  3. Create a table with default values, for example a typical users table with UUID / timestamp defaults.
  4. Use Nette\Database\Explorer and call: $explorer->table('users')->fetch();
  5. Nette Database tries to introspect table columns and fails with:
    SQLSTATE[XX000]: Internal error: 7 ERROR: invalid attnum 10 for relation "pg_attrdef"

In my case this happened in an authentication provider on this line:
$this->databaseExplorer->table('users')->order('created_at ASC')->fetch();

PostgreSQL itself was running correctly. Direct SQL queries against the users table worked. The failure only happened through Nette Database Explorer metadata introspection.

Expected Behavior

Nette\Database\Explorer should be able to introspect PostgreSQL 18 tables without throwing an internal PostgreSQL error.

Possible Solution

In src/Database/Drivers/PgSqlDriver.php, getColumns() should probably use ad.adrelid as the second argument of pg_get_expr() instead of 'pg_catalog.pg_attrdef'::regclass.

This local patch fixed the problem:

- pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass)::varchar AS default,
+ pg_catalog.pg_get_expr(ad.adbin, ad.adrelid)::varchar AS default,

- substring(pg_catalog.pg_get_expr(ad.adbin, 'pg_catalog.pg_attrdef'::regclass) from 'nextval[(]''"?([^''"]+)')
+ substring(pg_catalog.pg_get_expr(ad.adbin, ad.adrelid) from 'nextval[(]''"?([^''"]+)')

There is already another expression in the same query that uses ad.adrelid:
strpos(pg_catalog.pg_get_expr(ad.adbin, ad.adrelid), 'nextval')

So this change also makes the query internally consistent.

After applying this change locally, Explorer->table('users')->fetch() started working correctly with PostgreSQL 18.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions