NotHimmel opened a new issue, #2504:
URL: https://github.com/apache/age/issues/2504

   ### What happens
   
   On a session whose `search_path` does not include `ag_catalog`, creating a 
graph fails — even though the call is fully schema qualified:
   
   ```sql
   LOAD 'age';
   SHOW search_path;          --  "$user", public
   
   SELECT ag_catalog.create_graph('g');
   -- ERROR:  operator class "graphid_ops" does not exist for access method 
"btree"
   ```
   
   The whole transaction rolls back, so no partial graph is left behind.
   
   The same failure hits every entry point that reaches `create_label()`:
   
   * `ag_catalog.create_graph()`
   * `ag_catalog.create_vlabel()` / `ag_catalog.create_elabel()`
   * `ag_catalog.load_labels_from_file()` / `load_edges_from_file()` (via 
`get_or_create_label()`)
   * a `CREATE` or `MERGE` clause that mentions a label for the first time — 
this one fails *in the middle of a running query*
   
   ### Expected
   
   `create_graph()` should not depend on the caller's `search_path`. The error 
is also misleading: `graphid_ops` **does** exist (`ag_catalog`, 
`sql/age_main.sql`), it is simply not visible, and the message says `does not 
exist` with no hint about `search_path`. Combined with the `42704 
undefined_object` code, this sends people looking at privileges (`GRANT USAGE 
ON SCHEMA ag_catalog`, `GRANT EXECUTE ...`), which changes nothing.
   
   ### Root cause
   
   `create_index_on_column()` in `src/backend/commands/label_commands.c` passes 
the operator class as an **unqualified** name:
   
   ```c
   index_col->opclass = list_make1(makeString("graphid_ops"));
   ```
   
   `ResolveOpClass()` → `OpclassnameGetOpcid()` resolves an unqualified opclass 
name through `search_path`, so the lookup fails when `ag_catalog` is not on it.
   
   This is the only unqualified name in that code path. Everything else the 
generated DDL references is already immune:
   
   | reference | how it is built | search_path dependent |
   | --- | --- | --- |
   | column types `graphid` / `agtype` | `GRAPHIDOID` / `AGTYPEOID` (OIDs) | no 
|
   | `_graphid()` default | `list_make2(makeString("ag_catalog"), 
makeString("_graphid"))` | no |
   | `agtype_build_map()` default | `list_make2(makeString("ag_catalog"), ...)` 
| no |
   | `_label_id()` | `list_make2(makeString("ag_catalog"), ...)` | no |
   | `int4` / `regclass` | `SystemTypeName()` → `pg_catalog.*` | no |
   | **`graphid_ops`** | **`list_make1(makeString(...))`** | **yes** |
   
   So this looks like an oversight rather than a deliberate choice — the same 
function qualifies three other `ag_catalog` names explicitly.
   
   ### This is a regression
   
   Before the id-column indexes were added in #2117, `create_label()` created 
no index and therefore resolved no name through `search_path`, so 
`create_graph()` worked under any `search_path`.
   
   Commits carrying the unqualified name:
   
   | branch | commit |
   | --- | --- |
   | `master`, `PG19`, `release/PG19/1.8.0` | `5aed9ec` (#2117) |
   | `PG18`, `release/PG18/1.7.0`, `release/PG18/1.8.0` | `2f36b1c` (#2117) |
   | `PG17`, `release/PG17/1.7.0` | `858a0b7` (#2117) |
   | `PG16` | `8c74fd2` (#2375) |
   
   **Affected: 1.7.0 and later. 1.6.0 and earlier are not affected.**
   
   ### Why no existing test catches it
   
   Every file in `regress/sql/` that creates a graph sets `SET search_path TO 
ag_catalog;` — 35/35 on `release/PG18/1.7.0`, 46/47 on `master`, the single 
exception being `agehash.sql`, which only calls 
`ag_catalog._agehash_self_test()` and creates nothing. So no test exercises the 
unqualified-lookup path.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to