Subject: Fix a bug in extension_file_exists()

From: Chao Li <[email-masked]>
Date: Mon, 9 Feb 2026 17:21:17 +0800

Hi,

I just noticed a bug in extension_file_exists():

```
bool
extension_file_exists(const char *extensionName)
{
	bool		result = false;
	List	   *locations;
	DIR		   *dir;
	struct dirent *de;

	locations = get_extension_control_directories();

	foreach_ptr(char, location, locations) // <== Here type char is wrong
	{
		dir = AllocateDir(location);
```

get_extension_control_directories() returns a list of ExtensionLocation, but the loop iterates it as if it contained char *, which is incorrect. As a result, AllocateDir() and ReadDir() are called with the wrong type.

This bug is only triggered on an error path, when PostgreSQL is deciding whether to emit a hint. For example:
```
evantest=# create function f() returns int LANGUAGE plpython3u as $$return 1$$;
ERROR:  language "plpython3u" does not exist
```
No hint is printed.

With this patch applied:
```
evantest=# create function f() returns int LANGUAGE plpython3u as $$return 1$$;
ERROR:  language "plpython3u" does not exist
HINT:  Use CREATE EXTENSION to load the language into the database.
```
So the hint is shown as intended.

Attached is a patch fixing the iteration to use ExtensionLocation and location->loc consistently.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

---

From: Heikki Linnakangas <[email-masked]>
Date: Mon, 9 Feb 2026 12:02:10 +0200

> Attached is a patch fixing the iteration to use ExtensionLocation and location->loc consistently.

Yep, good catch. This went wrong in commit f3c9e341cd, which changed the 
type of objects in the list from "char *" to "ExtensionLocation *". So 
this is master only, stable branches are not affected.

I will push the fix shortly, thanks!

- Heikki

---

From: Matheus Alcantara <[email-masked]>
Date: Mon, 9 Feb 2026 10:19:20 -0300

On 09/02/26 07:02, Heikki Linnakangas wrote:
>> get_extension_control_directories() returns a list of 
>> ExtensionLocation, but the loop iterates it as if it contained char 
>> *, which is incorrect. As a result, AllocateDir() and ReadDir() are 
>> called with the wrong type.
>>
>> This bug is only triggered on an error path, when PostgreSQL is 
>> deciding whether to emit a hint. For example:
>> ```
> Yep, good catch. This went wrong in commit f3c9e341cd, which changed 
> the type of objects in the list from "char *" to "ExtensionLocation 
> *". So this is master only, stable branches are not affected.
> 
Sorry, I miss this change.

Thanks for the patch Chao and Heikki for committing.

-- 
Matheus Alcantara
EDB: https://www.enterprisedb.com

---

From: Chao Li <[email-masked]>
Date: Tue, 10 Feb 2026 08:50:10 +0800

> On Feb 9, 2026, at 18:02, Heikki Linnakangas <[email-masked]> wrote:
> 
> Yep, good catch. This went wrong in commit f3c9e341cd, which changed the type of objects in the list from "char *" to "ExtensionLocation *". So this is master only, stable branches are not affected.
> 

Thanks for pushing.

Best regards,