Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wDf1D-003DSb-1X for pgsql-hackers@arkaria.postgresql.org; Fri, 17 Apr 2026 08:57:43 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wDf0D-009CNm-1g for pgsql-hackers@arkaria.postgresql.org; Fri, 17 Apr 2026 08:56:41 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wDf0D-009CNd-0j for pgsql-hackers@lists.postgresql.org; Fri, 17 Apr 2026 08:56:41 +0000 Received: from mail-wr1-x42b.google.com ([2a00:1450:4864:20::42b]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wDf0A-00000001d5N-3ndN for pgsql-hackers@lists.postgresql.org; Fri, 17 Apr 2026 08:56:40 +0000 Received: by mail-wr1-x42b.google.com with SMTP id ffacd0b85a97d-43d7213b6ebso264814f8f.3 for ; Fri, 17 Apr 2026 01:56:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776416197; x=1777020997; darn=lists.postgresql.org; h=content-transfer-encoding:to:subject:from:content-language :user-agent:mime-version:date:message-id:from:to:cc:subject:date :message-id:reply-to; bh=jyQw8ZCeEhpnqn0PEoyE3k97cBuBixrRWzMZZYYnbbI=; b=McA5BbXjDEBndPmOOa4cjhfQCw/zUbZ5k2VpwSe9Wu0mlPIGsO8wAaxvOZvv7egSWr ODCfuq5zBs0fdmMcPZfTY1PtXA2vIgdXMqBky+42gCHDnl3WGtHRUgWfqEJDk1u5V8DQ kLjc5Z91KwB44VPUE7G68Ny1J4Zz4bvDyuWp0ds7TLtNi+LBMkbuUPhMfPah5VnkCkMl l6MPjnojjaHqzIOaKX6+NO75HVtTuVDtfY0Pywsf+Og2G4ubX3Uk6TEnaJpGefvn64XU u/gm8TWcEOamjcjovQYT781gFEAkwa9CtKtIRxgBkLZiADKkO9KngdxJlB0dOHwWVzTs jawg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776416197; x=1777020997; h=content-transfer-encoding:to:subject:from:content-language :user-agent:mime-version:date:message-id:x-gm-gg:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=jyQw8ZCeEhpnqn0PEoyE3k97cBuBixrRWzMZZYYnbbI=; b=MzFg7WbzAwGE4SjvSp0XXoiQBF00Q1incO8L1+cskCdWW66CoXCoJODl9mGlVNP/Av oJLhjsxMxTyl9eDD2QYU6HxGL6vHz5dlqacaMOU+vi9eVJmwA5guBhX32CqzBTz8A88r bODZS7Lz0yBrWBpg5o1FK9/H5qq5cQU6OCdNE2MHa2tuC17FdlUqCUkD3tpSSlSP/GB3 rBeXnmi5oou1uITOCFHBspmbfi815kV8pmg7n7OKpURdzRNI/Anujh6l/fiEQ7fy80Wh G48v8KIle7VbhTaIH77OZT+XK/41CSwQOHo4NJXRnk7SAmuF5zJNg8g5oMFleyuKJQsG e7iw== X-Gm-Message-State: AOJu0Yx9q0i9XIgGewN5fE0r9qflMTxNv4HHiuPf0hn2C6TUIps+fC+n lrCuDtqMjpZCFpAhRelJgep6cTt7m5XpmxNf7MCdjhBe4TmmuRORxhEh4UCAoQ== X-Gm-Gg: AeBDiesRpRkF1a5gM6+sKrg8gjnQ5KshqLTbKCjzlGSWI2YaMm2kJmcaX3+AtA7svpO jQrUczPyDvI/7QAfskW4gyiOiZP4PkJitYwD46+yPYkJxmY6WJyebPwYa13EtwRTMNzqw4dBVFh +zeCHdHhhJONcTF+1QH7WyAgDy/bD9vhHPKgHW1PJdZXvtjCs2kkPqy6a9dThpSa504XQJWSzWl Jj/G6ivI9jPmFcIRvCBxIKqLu9yIJi0j/kdFCmMDWwBuRZ611qUJofV+fS+tHQxe/eATseg8RvV 1TvChy7jfVgsY5aKawTyt2701EKBkoXOYqnqvAQxxv/tYHxSvPJTt6iR9os5HTpEAjycGpPcmJ7 WePDUOVpPdr6DwHtmDs7zi7vDnTCj1NWozclVHfoVdy24xUbDvyq3J/YbATdPaIPrGVJFfQ48E9 ZiZ7tPoXp509hJMnqyY0AiPaSuUqEtHh03inZwjrGzpS+molVO X-Received: by 2002:adf:fd4c:0:b0:43f:e414:4c6a with SMTP id ffacd0b85a97d-43fe4144cabmr1766530f8f.0.1776416196240; Fri, 17 Apr 2026 01:56:36 -0700 (PDT) Received: from [192.168.15.160] ([80.251.191.198]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4e4d112sm2965157f8f.29.2026.04.17.01.56.35 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 17 Apr 2026 01:56:35 -0700 (PDT) Message-ID: Date: Fri, 17 Apr 2026 10:56:34 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: en-US From: Andrei Lepikhov Subject: A very quick observation of dangling pointers in Postgres pathlists To: PostgreSQL Hackers , Ashutosh Bapat Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi, It looks like a community decision has been developing that Postgres should separate optimisation features into 'conventional' and 'magic' classes [1]. This has raised my concern that hidden contracts about pathlists' state and ordering could lead to subtle bugs if an extension optimisation goes too far. I think this topic is of interest because of the growing number of features that impact path choice, such as ‘disable node’ or pg_plan_advice. Also, emerging techniques that involve two or more levels of plan trees, like ‘eager aggregation’, might catch another dangling pointer hidden in path lists for a while. Don’t forget complicated cases with FDW and Custom nodes too. For this purpose, a tiny debugging extension module, pg_pathcheck [2], has been invented. It uses create_upper_paths_hook and planner_shutdown_hook. The extension walks the entire Path tree, starting from the top PlannerInfo, then recurses into glob::subroots, traversing each RelOptInfo and each pathlist. Also, it traverses the path→subpath subtrees to ensure that potentially quite complex path trees are covered when implemented as a single RelOptInfo. For each pointer it visits, it checks if the NodeTag matches a known Path type. If not, the memory was freed (and, with CLOBBER_FREED_MEMORY, set to 0x7F) or reused for something else. This approach is not free of caveats. For example, most Path nodes and many Plan nodes fall within the 128-byte gap of the minimal allocated chunk. That means freeing one path allows the optimiser to immediately allocate another Path node at a potentially different query tree level. I had such a case at least once in production. It was actually hard to realise, reproduce, and fix. Running make check-world tests with the debug module loaded at startup revealed many cases in which RelOptInfo structures contain dangling pointers. What exactly do we see there? The pathlist contents at the moment of an ‘Invalid’ path detection: * ProjectionPath, Invalid — by far the most common, on JOIN RelOptInfos. * ProjectionPath, Invalid, SortPath. * AggPath, Invalid. * NestPath, Invalid * HashPath, Invalid * cheapest_startup_path referencing a dangling pointer, on what looks like a join of two partitions. * cheapest_startup_path referencing a dangling pointer on a plain base RelOptInfo. The best-known problematic code example causing this issue is apply_scanjoin_target_to_paths(), and the current_rel/final_rel game from commit 0927d2f46dd. Quickly fixing it, I see some more combinations have emerged: * UniquePath, Invalid * MergePath, Invalid * SubqueryScanPath, Invalid * SetOpPath, Invalid * GatherPath, Path, Invalid * AppendPath, AggPath, Invalid, AggPath * HashPath, Invalid * AppendPath, HashPath, Invalid These new invalid references occur outside the originally identified code path, showing that fixing one place does not address the broader issue (maybe my fixes were wrong?). While some claim that the cost-dominance principle ('the cheapest path is never invalid') provides safety, I have not found any acknowledgment of this. As the planner is expanded, undocumented rules leave the system vulnerable. The purpose of this email is basically to highlight the issue and raise a discussion on how to solve it. Ashutosh designed a 'smart pointer' approach, which seems the most balanced and bulletproof way. Another approach: 'used' flag seems less interesting as well as local memory contexts - we should always remember about multi-children cases that need freeing unnecessary paths in-place to reduce memory consumption. But before diving into the code and identifying origins of these cases, I’d like to know: is it an actual problem, or is the cost-dominance contract enough? [1] https://www.postgresql.org/message-id/CA+TgmoaPgXYYEivQWxyVV=eYhN+T9JAgS9Xe4m7g9wVitVPF8g@mail.gmail.com [2] https://github.com/danolivo/pg_pathcheck -- regards, Andrei Lepikhov, pgEdge