Skip to content

bug: CREATE VIEW fails with "unrecognized node type: 63" when the query contains an explicit JOIN #229

Description

@oscarbritse

Summary

When pg_ducklake is loaded, any CREATE VIEW statement whose query body
contains an explicit JOIN, LEFT JOIN, RIGHT JOIN, or LATERAL clause
crashes with:

ERROR: unrecognized node type: 63

The view is not created. The same query works fine as a plain SELECT.

Reproduction

-- Setup
CREATE TABLE t1 (id int PRIMARY KEY);
CREATE TABLE t2 (id int PRIMARY KEY, label text);

-- ✅ WORKS — plain SELECT with explicit JOIN is fine
SELECT t1.id, t2.label
FROM t1
LEFT JOIN t2 ON t2.id = t1.id;

-- ✅ WORKS — CREATE VIEW with implicit comma join
CREATE VIEW test_implicit_join AS
SELECT t1.id, t2.label
FROM t1, t2
WHERE t2.id = t1.id;

-- ❌ FAILS — CREATE VIEW with explicit LEFT JOIN
-- ERROR: unrecognized node type: 63
CREATE VIEW test_left_join AS
SELECT t1.id, t2.label
FROM t1
LEFT JOIN t2 ON t2.id = t1.id;

Root cause (hypothesis)

PostgreSQL represents every explicit JOIN / LEFT JOIN / LATERAL in the
query parse tree as a JoinExpr node (T_JoinExpr = 63).

pg_ducklake installs a DDL hook that walks the query tree of CREATE VIEW
statements to discover DuckLake table references. That walker's node-dispatch
switch appears to have no case for JoinExpr, so it falls through to the
default branch and calls ereport(ERROR, ...) with the raw node tag integer,
producing the "unrecognized node type: 63" message.

The implicit comma-join (FROM t1, t2 WHERE ...) avoids the problem because
it produces a FromExpr whose fromlist holds plain RangeTblRef entries —
no JoinExpr node is generated at all.

Suggested fix

Add a JoinExpr case to the walker that recurses into:

  • joinexpr->larg (left relation)
  • joinexpr->rarg (right relation)
  • joinexpr->quals (the ON expression, if any)

This is the standard pattern used by PostgreSQL's own tree-walkers (see
pg_walker.c / expression_tree_walker).

Workaround

Until fixed, rewrite explicit JOINs as implicit comma joins:

-- Instead of:  FROM t1 LEFT JOIN t2 ON t2.id = t1.id
-- Write:        FROM t1, t2 WHERE t2.id = t1.id

Note that this only works for inner-join semantics. There is no comma-join
equivalent for LEFT JOIN (rows with no match on the right are silently
dropped), so views that require outer joins cannot be created while
pg_ducklake is loaded.

Version: pg_ducklake 1.0.0, PostgreSQL 18.4

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