Copilot commented on code in PR #2417:
URL: https://github.com/apache/age/pull/2417#discussion_r3376462439


##########
Makefile:
##########
@@ -183,7 +183,8 @@ REGRESS = scan \
           direct_field_access \
           security \
           reserved_keyword_alias \
-          agtype_jsonb_cast
+          agtype_jsonb_cast \\
+          containment_selectivity

Review Comment:
   `REGRESS` list line continuation is malformed: `agtype_jsonb_cast \\` leaves 
an extra literal `\` token in the variable value (because one backslash escapes 
the newline and the other remains), which can cause `make installcheck` to try 
to run a bogus test name or otherwise break parsing. Use a single trailing 
backslash for continuation.



##########
age--1.7.0--y.y.y.sql:
##########
@@ -464,6 +464,1841 @@ $$;
 -- agtype <-> jsonb bidirectional casts
 --
 
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json(--

Review Comment:
   This function body appears to have been accidentally inlined with a large 
chunk of SQL comments/DDL starting inside a single-quoted string (`AS 'SELECT 
ag_catalog.agtype_to_json(-- ...`). As written, the quote never closes and the 
upgrade script will fail to parse, preventing `ALTER EXTENSION age UPDATE` 
entirely. Please regenerate/restore this upgrade template section so the 
`agtype_to_jsonb` definition is a normal one-liner (and remove the duplicated 
pasted blocks that follow).



##########
age--1.7.0--y.y.y.sql:
##########
@@ -464,6 +464,1841 @@ $$;
 -- agtype <-> jsonb bidirectional casts
 --
 
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json(--
+-- agtype <-> jsonb bidirectional casts
+--
+
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json($1)::jsonb';
+
+CREATE CAST (agtype AS jsonb)
+    WITH FUNCTION ag_catalog.agtype_to_jsonb(agtype);
+
+-- jsonb -> agtype (explicit)
+CREATE FUNCTION ag_catalog.jsonb_to_agtype(jsonb)
+    RETURNS agtype
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT $1::text::agtype';
+
+CREATE CAST (jsonb AS agtype)
+    WITH FUNCTION ag_catalog.jsonb_to_agtype(jsonb);
+
+--
+-- S4: VLE SRF signature change
+--
+-- The age_vle SRF now emits start_id and end_id as scalar graphid columns
+-- alongside the existing `edges` column. This allows the cypher transformer
+-- to rewrite terminal-edge match quals as plain integer equalities,
+-- removing the per-row age_match_vle_terminal_edge and age_match_two_vle_edges
+-- function calls from VLE query plans.  Both qual functions are dropped.
+--
+-- BREAKING CHANGE for any external SQL that called age_vle(...) directly
+-- and relied on `RETURNS SETOF agtype`, or called age_match_vle_terminal_edge
+-- / age_match_two_vle_edges directly.  Internal AGE callers (the cypher
+-- transformer) are not affected.
+--
+DROP FUNCTION IF EXISTS ag_catalog.age_match_vle_terminal_edge(variadic "any");
+DROP FUNCTION IF EXISTS ag_catalog.age_match_two_vle_edges(agtype, agtype);
+
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype);
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype, agtype);
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Composite types for vertex and edge
+--
+CREATE TYPE ag_catalog.vertex AS (
+    id graphid,
+    label agtype,
+    properties agtype
+);
+
+CREATE TYPE ag_catalog.edge AS (
+    id graphid,
+    label agtype,
+    end_id graphid,
+    start_id graphid,
+    properties agtype
+);
+
+--
+-- vertex/edge to agtype cast functions
+--
+CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Implicit casts from vertex/edge to agtype
+--
+CREATE CAST (vertex AS agtype)
+    WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
+AS IMPLICIT;
+
+CREATE CAST (edge AS agtype)
+    WITH FUNCTION ag_catalog.edge_to_agtype(edge)
+AS IMPLICIT;
+
+CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_json(edge)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS json)
+    WITH FUNCTION ag_catalog.vertex_to_json(vertex);
+
+CREATE CAST (edge AS json)
+    WITH FUNCTION ag_catalog.edge_to_json(edge);
+
+CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS jsonb)
+    WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
+
+CREATE CAST (edge AS jsonb)
+    WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
+
+--
+-- Equality operators for vertex and edge (compare by id)
+--
+CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.vertex_eq,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.vertex_ne(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.vertex_ne,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_eq(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.edge_eq,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_ne(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.edge_ne,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+--
+-- Drop and recreate _label_name with new return type (cstring -> agtype)
+--
+DROP FUNCTION IF EXISTS ag_catalog._label_name(oid, graphid);
+
+CREATE FUNCTION ag_catalog._label_name(graph_oid oid, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_vertex with new signature (cstring -> 
agtype for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_vertex(graphid, cstring, 
agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_vertex(graphid, agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_edge with new signature (cstring -> agtype 
for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_edge(graphid, graphid, 
graphid, cstring, agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid,
+                                              agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Helper function for optimized startNode/endNode
+--
+CREATE FUNCTION ag_catalog._get_vertex_by_graphid(text, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+-- Internal selftest for the agehash open-addressing hashtable. Returns "OK"
+-- on success or "FAIL: ..." with a diagnostic message. Intended for the
+-- agehash regression test only.
+CREATE FUNCTION ag_catalog._agehash_self_test()
+    RETURNS text
+    LANGUAGE c
+    VOLATILE
+    PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';)::jsonb';
+
+CREATE CAST (agtype AS jsonb)
+    WITH FUNCTION ag_catalog.agtype_to_jsonb(agtype);
+
+-- jsonb -> agtype (explicit)
+CREATE FUNCTION ag_catalog.jsonb_to_agtype(jsonb)
+    RETURNS agtype
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT --
+-- agtype <-> jsonb bidirectional casts
+--
+
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json($1)::jsonb';
+
+CREATE CAST (agtype AS jsonb)
+    WITH FUNCTION ag_catalog.agtype_to_jsonb(agtype);
+
+-- jsonb -> agtype (explicit)
+CREATE FUNCTION ag_catalog.jsonb_to_agtype(jsonb)
+    RETURNS agtype
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT $1::text::agtype';
+
+CREATE CAST (jsonb AS agtype)
+    WITH FUNCTION ag_catalog.jsonb_to_agtype(jsonb);
+
+--
+-- S4: VLE SRF signature change
+--
+-- The age_vle SRF now emits start_id and end_id as scalar graphid columns
+-- alongside the existing `edges` column. This allows the cypher transformer
+-- to rewrite terminal-edge match quals as plain integer equalities,
+-- removing the per-row age_match_vle_terminal_edge and age_match_two_vle_edges
+-- function calls from VLE query plans.  Both qual functions are dropped.
+--
+-- BREAKING CHANGE for any external SQL that called age_vle(...) directly
+-- and relied on `RETURNS SETOF agtype`, or called age_match_vle_terminal_edge
+-- / age_match_two_vle_edges directly.  Internal AGE callers (the cypher
+-- transformer) are not affected.
+--
+DROP FUNCTION IF EXISTS ag_catalog.age_match_vle_terminal_edge(variadic "any");
+DROP FUNCTION IF EXISTS ag_catalog.age_match_two_vle_edges(agtype, agtype);
+
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype);
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype, agtype);
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Composite types for vertex and edge
+--
+CREATE TYPE ag_catalog.vertex AS (
+    id graphid,
+    label agtype,
+    properties agtype
+);
+
+CREATE TYPE ag_catalog.edge AS (
+    id graphid,
+    label agtype,
+    end_id graphid,
+    start_id graphid,
+    properties agtype
+);
+
+--
+-- vertex/edge to agtype cast functions
+--
+CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Implicit casts from vertex/edge to agtype
+--
+CREATE CAST (vertex AS agtype)
+    WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
+AS IMPLICIT;
+
+CREATE CAST (edge AS agtype)
+    WITH FUNCTION ag_catalog.edge_to_agtype(edge)
+AS IMPLICIT;
+
+CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_json(edge)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS json)
+    WITH FUNCTION ag_catalog.vertex_to_json(vertex);
+
+CREATE CAST (edge AS json)
+    WITH FUNCTION ag_catalog.edge_to_json(edge);
+
+CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS jsonb)
+    WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
+
+CREATE CAST (edge AS jsonb)
+    WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
+
+--
+-- Equality operators for vertex and edge (compare by id)
+--
+CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.vertex_eq,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.vertex_ne(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.vertex_ne,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_eq(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.edge_eq,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_ne(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.edge_ne,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+--
+-- Drop and recreate _label_name with new return type (cstring -> agtype)
+--
+DROP FUNCTION IF EXISTS ag_catalog._label_name(oid, graphid);
+
+CREATE FUNCTION ag_catalog._label_name(graph_oid oid, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_vertex with new signature (cstring -> 
agtype for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_vertex(graphid, cstring, 
agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_vertex(graphid, agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_edge with new signature (cstring -> agtype 
for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_edge(graphid, graphid, 
graphid, cstring, agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid,
+                                              agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Helper function for optimized startNode/endNode
+--
+CREATE FUNCTION ag_catalog._get_vertex_by_graphid(text, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+-- Internal selftest for the agehash open-addressing hashtable. Returns "OK"
+-- on success or "FAIL: ..." with a diagnostic message. Intended for the
+-- agehash regression test only.
+CREATE FUNCTION ag_catalog._agehash_self_test()
+    RETURNS text
+    LANGUAGE c
+    VOLATILE
+    PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';::text::agtype';
+
+CREATE CAST (jsonb AS agtype)
+    WITH FUNCTION ag_catalog.jsonb_to_agtype(jsonb);
+
+--
+-- S4: VLE SRF signature change
+--
+-- The age_vle SRF now emits start_id and end_id as scalar graphid columns
+-- alongside the existing `edges` column. This allows the cypher transformer
+-- to rewrite terminal-edge match quals as plain integer equalities,
+-- removing the per-row age_match_vle_terminal_edge and age_match_two_vle_edges
+-- function calls from VLE query plans.  Both qual functions are dropped.
+--
+-- BREAKING CHANGE for any external SQL that called age_vle(...) directly
+-- and relied on `RETURNS SETOF agtype`, or called age_match_vle_terminal_edge
+-- / age_match_two_vle_edges directly.  Internal AGE callers (the cypher
+-- transformer) are not affected.
+--
+DROP FUNCTION IF EXISTS ag_catalog.age_match_vle_terminal_edge(variadic "any");
+DROP FUNCTION IF EXISTS ag_catalog.age_match_two_vle_edges(agtype, agtype);
+
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype);
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype, agtype);
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Composite types for vertex and edge
+--
+CREATE TYPE ag_catalog.vertex AS (
+    id graphid,
+    label agtype,
+    properties agtype
+);
+
+CREATE TYPE ag_catalog.edge AS (
+    id graphid,
+    label agtype,
+    end_id graphid,
+    start_id graphid,
+    properties agtype
+);
+
+--
+-- vertex/edge to agtype cast functions
+--
+CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Implicit casts from vertex/edge to agtype
+--
+CREATE CAST (vertex AS agtype)
+    WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
+AS IMPLICIT;
+
+CREATE CAST (edge AS agtype)
+    WITH FUNCTION ag_catalog.edge_to_agtype(edge)
+AS IMPLICIT;
+
+CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_json(edge)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS json)
+    WITH FUNCTION ag_catalog.vertex_to_json(vertex);
+
+CREATE CAST (edge AS json)
+    WITH FUNCTION ag_catalog.edge_to_json(edge);
+
+CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS jsonb)
+    WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
+
+CREATE CAST (edge AS jsonb)
+    WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
+
+--
+-- Equality operators for vertex and edge (compare by id)
+--
+CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $ SELECT --
+-- agtype <-> jsonb bidirectional casts
+--
+
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json($1)::jsonb';
+
+CREATE CAST (agtype AS jsonb)
+    WITH FUNCTION ag_catalog.agtype_to_jsonb(agtype);
+
+-- jsonb -> agtype (explicit)
+CREATE FUNCTION ag_catalog.jsonb_to_agtype(jsonb)
+    RETURNS agtype
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT $1::text::agtype';
+
+CREATE CAST (jsonb AS agtype)
+    WITH FUNCTION ag_catalog.jsonb_to_agtype(jsonb);
+
+--
+-- S4: VLE SRF signature change
+--
+-- The age_vle SRF now emits start_id and end_id as scalar graphid columns
+-- alongside the existing `edges` column. This allows the cypher transformer
+-- to rewrite terminal-edge match quals as plain integer equalities,
+-- removing the per-row age_match_vle_terminal_edge and age_match_two_vle_edges
+-- function calls from VLE query plans.  Both qual functions are dropped.
+--
+-- BREAKING CHANGE for any external SQL that called age_vle(...) directly
+-- and relied on `RETURNS SETOF agtype`, or called age_match_vle_terminal_edge
+-- / age_match_two_vle_edges directly.  Internal AGE callers (the cypher
+-- transformer) are not affected.
+--
+DROP FUNCTION IF EXISTS ag_catalog.age_match_vle_terminal_edge(variadic "any");
+DROP FUNCTION IF EXISTS ag_catalog.age_match_two_vle_edges(agtype, agtype);
+
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype);
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype, agtype);
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Composite types for vertex and edge
+--
+CREATE TYPE ag_catalog.vertex AS (
+    id graphid,
+    label agtype,
+    properties agtype
+);
+
+CREATE TYPE ag_catalog.edge AS (
+    id graphid,
+    label agtype,
+    end_id graphid,
+    start_id graphid,
+    properties agtype
+);
+
+--
+-- vertex/edge to agtype cast functions
+--
+CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Implicit casts from vertex/edge to agtype
+--
+CREATE CAST (vertex AS agtype)
+    WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
+AS IMPLICIT;
+
+CREATE CAST (edge AS agtype)
+    WITH FUNCTION ag_catalog.edge_to_agtype(edge)
+AS IMPLICIT;
+
+CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_json(edge)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS json)
+    WITH FUNCTION ag_catalog.vertex_to_json(vertex);
+
+CREATE CAST (edge AS json)
+    WITH FUNCTION ag_catalog.edge_to_json(edge);
+
+CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS jsonb)
+    WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
+
+CREATE CAST (edge AS jsonb)
+    WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
+
+--
+-- Equality operators for vertex and edge (compare by id)
+--
+CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.vertex_eq,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.vertex_ne(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.vertex_ne,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_eq(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.edge_eq,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_ne(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.edge_ne,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+--
+-- Drop and recreate _label_name with new return type (cstring -> agtype)
+--
+DROP FUNCTION IF EXISTS ag_catalog._label_name(oid, graphid);
+
+CREATE FUNCTION ag_catalog._label_name(graph_oid oid, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_vertex with new signature (cstring -> 
agtype for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_vertex(graphid, cstring, 
agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_vertex(graphid, agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_edge with new signature (cstring -> agtype 
for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_edge(graphid, graphid, 
graphid, cstring, agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid,
+                                              agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Helper function for optimized startNode/endNode
+--
+CREATE FUNCTION ag_catalog._get_vertex_by_graphid(text, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+-- Internal selftest for the agehash open-addressing hashtable. Returns "OK"
+-- on success or "FAIL: ..." with a diagnostic message. Intended for the
+-- agehash regression test only.
+CREATE FUNCTION ag_catalog._agehash_self_test()
+    RETURNS text
+    LANGUAGE c
+    VOLATILE
+    PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';.id = 

Review Comment:
   There is stray text appended after the function terminator (`AS 
'MODULE_PATHNAME';.id =`). This is invalid SQL and will abort the upgrade 
script. It also suggests the file has been inadvertently concatenated/garbled 
(similar stray fragments appear elsewhere).



##########
age--1.7.0--y.y.y.sql:
##########
@@ -464,6 +464,1841 @@ $$;
 -- agtype <-> jsonb bidirectional casts
 --
 
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json(--
+-- agtype <-> jsonb bidirectional casts
+--
+
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json($1)::jsonb';
+
+CREATE CAST (agtype AS jsonb)
+    WITH FUNCTION ag_catalog.agtype_to_jsonb(agtype);
+
+-- jsonb -> agtype (explicit)
+CREATE FUNCTION ag_catalog.jsonb_to_agtype(jsonb)
+    RETURNS agtype
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT $1::text::agtype';
+
+CREATE CAST (jsonb AS agtype)
+    WITH FUNCTION ag_catalog.jsonb_to_agtype(jsonb);
+
+--
+-- S4: VLE SRF signature change
+--
+-- The age_vle SRF now emits start_id and end_id as scalar graphid columns
+-- alongside the existing `edges` column. This allows the cypher transformer
+-- to rewrite terminal-edge match quals as plain integer equalities,
+-- removing the per-row age_match_vle_terminal_edge and age_match_two_vle_edges
+-- function calls from VLE query plans.  Both qual functions are dropped.
+--
+-- BREAKING CHANGE for any external SQL that called age_vle(...) directly
+-- and relied on `RETURNS SETOF agtype`, or called age_match_vle_terminal_edge
+-- / age_match_two_vle_edges directly.  Internal AGE callers (the cypher
+-- transformer) are not affected.
+--
+DROP FUNCTION IF EXISTS ag_catalog.age_match_vle_terminal_edge(variadic "any");
+DROP FUNCTION IF EXISTS ag_catalog.age_match_two_vle_edges(agtype, agtype);
+
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype);
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype, agtype);
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Composite types for vertex and edge
+--
+CREATE TYPE ag_catalog.vertex AS (
+    id graphid,
+    label agtype,
+    properties agtype
+);
+
+CREATE TYPE ag_catalog.edge AS (
+    id graphid,
+    label agtype,
+    end_id graphid,
+    start_id graphid,
+    properties agtype
+);
+
+--
+-- vertex/edge to agtype cast functions
+--
+CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Implicit casts from vertex/edge to agtype
+--
+CREATE CAST (vertex AS agtype)
+    WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
+AS IMPLICIT;
+
+CREATE CAST (edge AS agtype)
+    WITH FUNCTION ag_catalog.edge_to_agtype(edge)
+AS IMPLICIT;
+
+CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_json(edge)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS json)
+    WITH FUNCTION ag_catalog.vertex_to_json(vertex);
+
+CREATE CAST (edge AS json)
+    WITH FUNCTION ag_catalog.edge_to_json(edge);
+
+CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS jsonb)
+    WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
+
+CREATE CAST (edge AS jsonb)
+    WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
+
+--
+-- Equality operators for vertex and edge (compare by id)
+--
+CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.vertex_eq,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.vertex_ne(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.vertex_ne,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_eq(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.edge_eq,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_ne(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.edge_ne,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+--
+-- Drop and recreate _label_name with new return type (cstring -> agtype)
+--
+DROP FUNCTION IF EXISTS ag_catalog._label_name(oid, graphid);
+
+CREATE FUNCTION ag_catalog._label_name(graph_oid oid, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_vertex with new signature (cstring -> 
agtype for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_vertex(graphid, cstring, 
agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_vertex(graphid, agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_edge with new signature (cstring -> agtype 
for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_edge(graphid, graphid, 
graphid, cstring, agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid,
+                                              agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Helper function for optimized startNode/endNode
+--
+CREATE FUNCTION ag_catalog._get_vertex_by_graphid(text, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+-- Internal selftest for the agehash open-addressing hashtable. Returns "OK"
+-- on success or "FAIL: ..." with a diagnostic message. Intended for the
+-- agehash regression test only.
+CREATE FUNCTION ag_catalog._agehash_self_test()
+    RETURNS text
+    LANGUAGE c
+    VOLATILE
+    PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';)::jsonb';
+
+CREATE CAST (agtype AS jsonb)
+    WITH FUNCTION ag_catalog.agtype_to_jsonb(agtype);
+
+-- jsonb -> agtype (explicit)
+CREATE FUNCTION ag_catalog.jsonb_to_agtype(jsonb)
+    RETURNS agtype
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT --
+-- agtype <-> jsonb bidirectional casts
+--
+
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json($1)::jsonb';
+
+CREATE CAST (agtype AS jsonb)
+    WITH FUNCTION ag_catalog.agtype_to_jsonb(agtype);
+
+-- jsonb -> agtype (explicit)
+CREATE FUNCTION ag_catalog.jsonb_to_agtype(jsonb)
+    RETURNS agtype
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT $1::text::agtype';
+
+CREATE CAST (jsonb AS agtype)
+    WITH FUNCTION ag_catalog.jsonb_to_agtype(jsonb);
+
+--
+-- S4: VLE SRF signature change
+--
+-- The age_vle SRF now emits start_id and end_id as scalar graphid columns
+-- alongside the existing `edges` column. This allows the cypher transformer
+-- to rewrite terminal-edge match quals as plain integer equalities,
+-- removing the per-row age_match_vle_terminal_edge and age_match_two_vle_edges
+-- function calls from VLE query plans.  Both qual functions are dropped.
+--
+-- BREAKING CHANGE for any external SQL that called age_vle(...) directly
+-- and relied on `RETURNS SETOF agtype`, or called age_match_vle_terminal_edge
+-- / age_match_two_vle_edges directly.  Internal AGE callers (the cypher
+-- transformer) are not affected.
+--
+DROP FUNCTION IF EXISTS ag_catalog.age_match_vle_terminal_edge(variadic "any");
+DROP FUNCTION IF EXISTS ag_catalog.age_match_two_vle_edges(agtype, agtype);
+
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype);
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype, agtype);
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Composite types for vertex and edge
+--
+CREATE TYPE ag_catalog.vertex AS (
+    id graphid,
+    label agtype,
+    properties agtype
+);
+
+CREATE TYPE ag_catalog.edge AS (
+    id graphid,
+    label agtype,
+    end_id graphid,
+    start_id graphid,
+    properties agtype
+);
+
+--
+-- vertex/edge to agtype cast functions
+--
+CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Implicit casts from vertex/edge to agtype
+--
+CREATE CAST (vertex AS agtype)
+    WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
+AS IMPLICIT;
+
+CREATE CAST (edge AS agtype)
+    WITH FUNCTION ag_catalog.edge_to_agtype(edge)
+AS IMPLICIT;
+
+CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_json(edge)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS json)
+    WITH FUNCTION ag_catalog.vertex_to_json(vertex);
+
+CREATE CAST (edge AS json)
+    WITH FUNCTION ag_catalog.edge_to_json(edge);
+
+CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS jsonb)
+    WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
+
+CREATE CAST (edge AS jsonb)
+    WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
+
+--
+-- Equality operators for vertex and edge (compare by id)
+--
+CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.vertex_eq,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.vertex_ne(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.vertex_ne,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_eq(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.edge_eq,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_ne(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.edge_ne,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+--
+-- Drop and recreate _label_name with new return type (cstring -> agtype)
+--
+DROP FUNCTION IF EXISTS ag_catalog._label_name(oid, graphid);
+
+CREATE FUNCTION ag_catalog._label_name(graph_oid oid, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_vertex with new signature (cstring -> 
agtype for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_vertex(graphid, cstring, 
agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_vertex(graphid, agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_edge with new signature (cstring -> agtype 
for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_edge(graphid, graphid, 
graphid, cstring, agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid,
+                                              agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Helper function for optimized startNode/endNode
+--
+CREATE FUNCTION ag_catalog._get_vertex_by_graphid(text, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+-- Internal selftest for the agehash open-addressing hashtable. Returns "OK"
+-- on success or "FAIL: ..." with a diagnostic message. Intended for the
+-- agehash regression test only.
+CREATE FUNCTION ag_catalog._agehash_self_test()
+    RETURNS text
+    LANGUAGE c
+    VOLATILE
+    PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';::text::agtype';
+
+CREATE CAST (jsonb AS agtype)
+    WITH FUNCTION ag_catalog.jsonb_to_agtype(jsonb);
+
+--
+-- S4: VLE SRF signature change
+--
+-- The age_vle SRF now emits start_id and end_id as scalar graphid columns
+-- alongside the existing `edges` column. This allows the cypher transformer
+-- to rewrite terminal-edge match quals as plain integer equalities,
+-- removing the per-row age_match_vle_terminal_edge and age_match_two_vle_edges
+-- function calls from VLE query plans.  Both qual functions are dropped.
+--
+-- BREAKING CHANGE for any external SQL that called age_vle(...) directly
+-- and relied on `RETURNS SETOF agtype`, or called age_match_vle_terminal_edge
+-- / age_match_two_vle_edges directly.  Internal AGE callers (the cypher
+-- transformer) are not affected.
+--
+DROP FUNCTION IF EXISTS ag_catalog.age_match_vle_terminal_edge(variadic "any");
+DROP FUNCTION IF EXISTS ag_catalog.age_match_two_vle_edges(agtype, agtype);
+
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype);
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype, agtype);
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Composite types for vertex and edge
+--
+CREATE TYPE ag_catalog.vertex AS (
+    id graphid,
+    label agtype,
+    properties agtype
+);
+
+CREATE TYPE ag_catalog.edge AS (
+    id graphid,
+    label agtype,
+    end_id graphid,
+    start_id graphid,
+    properties agtype
+);
+
+--
+-- vertex/edge to agtype cast functions
+--
+CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Implicit casts from vertex/edge to agtype
+--
+CREATE CAST (vertex AS agtype)
+    WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
+AS IMPLICIT;
+
+CREATE CAST (edge AS agtype)
+    WITH FUNCTION ag_catalog.edge_to_agtype(edge)
+AS IMPLICIT;
+
+CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_json(edge)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS json)
+    WITH FUNCTION ag_catalog.vertex_to_json(vertex);
+
+CREATE CAST (edge AS json)
+    WITH FUNCTION ag_catalog.edge_to_json(edge);
+
+CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS jsonb)
+    WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
+
+CREATE CAST (edge AS jsonb)
+    WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
+
+--
+-- Equality operators for vertex and edge (compare by id)
+--
+CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $ SELECT --
+-- agtype <-> jsonb bidirectional casts
+--
+
+-- agtype -> jsonb (explicit)
+-- Uses json intermediate (agtype_to_json -> json::jsonb) because agtype
+-- extends jsonb's binary format with types (AGTV_INTEGER, AGTV_FLOAT,
+-- AGTV_VERTEX, AGTV_EDGE, AGTV_PATH) that jsonb does not recognize.
+CREATE FUNCTION ag_catalog.agtype_to_jsonb(agtype)
+    RETURNS jsonb
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT ag_catalog.agtype_to_json($1)::jsonb';
+
+CREATE CAST (agtype AS jsonb)
+    WITH FUNCTION ag_catalog.agtype_to_jsonb(agtype);
+
+-- jsonb -> agtype (explicit)
+CREATE FUNCTION ag_catalog.jsonb_to_agtype(jsonb)
+    RETURNS agtype
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'SELECT $1::text::agtype';
+
+CREATE CAST (jsonb AS agtype)
+    WITH FUNCTION ag_catalog.jsonb_to_agtype(jsonb);
+
+--
+-- S4: VLE SRF signature change
+--
+-- The age_vle SRF now emits start_id and end_id as scalar graphid columns
+-- alongside the existing `edges` column. This allows the cypher transformer
+-- to rewrite terminal-edge match quals as plain integer equalities,
+-- removing the per-row age_match_vle_terminal_edge and age_match_two_vle_edges
+-- function calls from VLE query plans.  Both qual functions are dropped.
+--
+-- BREAKING CHANGE for any external SQL that called age_vle(...) directly
+-- and relied on `RETURNS SETOF agtype`, or called age_match_vle_terminal_edge
+-- / age_match_two_vle_edges directly.  Internal AGE callers (the cypher
+-- transformer) are not affected.
+--
+DROP FUNCTION IF EXISTS ag_catalog.age_match_vle_terminal_edge(variadic "any");
+DROP FUNCTION IF EXISTS ag_catalog.age_match_two_vle_edges(agtype, agtype);
+
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype);
+DROP FUNCTION IF EXISTS ag_catalog.age_vle(agtype, agtype, agtype, agtype,
+                                           agtype, agtype, agtype, agtype);
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.age_vle(IN agtype, IN agtype, IN agtype, IN agtype,
+                                   IN agtype, IN agtype, IN agtype, IN agtype,
+                                   OUT edges    agtype,
+                                   OUT start_id graphid,
+                                   OUT end_id   graphid)
+    RETURNS SETOF record
+LANGUAGE C
+STABLE
+CALLED ON NULL INPUT
+PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Composite types for vertex and edge
+--
+CREATE TYPE ag_catalog.vertex AS (
+    id graphid,
+    label agtype,
+    properties agtype
+);
+
+CREATE TYPE ag_catalog.edge AS (
+    id graphid,
+    label agtype,
+    end_id graphid,
+    start_id graphid,
+    properties agtype
+);
+
+--
+-- vertex/edge to agtype cast functions
+--
+CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Implicit casts from vertex/edge to agtype
+--
+CREATE CAST (vertex AS agtype)
+    WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
+AS IMPLICIT;
+
+CREATE CAST (edge AS agtype)
+    WITH FUNCTION ag_catalog.edge_to_agtype(edge)
+AS IMPLICIT;
+
+CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_json(edge)
+    RETURNS json
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS json)
+    WITH FUNCTION ag_catalog.vertex_to_json(vertex);
+
+CREATE CAST (edge AS json)
+    WITH FUNCTION ag_catalog.edge_to_json(edge);
+
+CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
+    RETURNS jsonb
+    LANGUAGE c
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (vertex AS jsonb)
+    WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
+
+CREATE CAST (edge AS jsonb)
+    WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
+
+--
+-- Equality operators for vertex and edge (compare by id)
+--
+CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.vertex_eq,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.vertex_ne(vertex, vertex)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.vertex_ne,
+    LEFTARG = vertex,
+    RIGHTARG = vertex,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_eq(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id = $2.id $$;
+
+CREATE OPERATOR = (
+    FUNCTION = ag_catalog.edge_eq,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = =,
+    NEGATOR = <>,
+    RESTRICT = eqsel,
+    JOIN = eqjoinsel
+);
+
+CREATE FUNCTION ag_catalog.edge_ne(edge, edge)
+    RETURNS boolean
+    LANGUAGE sql
+    IMMUTABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS $$ SELECT $1.id <> $2.id $$;
+
+CREATE OPERATOR <> (
+    FUNCTION = ag_catalog.edge_ne,
+    LEFTARG = edge,
+    RIGHTARG = edge,
+    COMMUTATOR = <>,
+    NEGATOR = =,
+    RESTRICT = neqsel,
+    JOIN = neqjoinsel
+);
+
+--
+-- Drop and recreate _label_name with new return type (cstring -> agtype)
+--
+DROP FUNCTION IF EXISTS ag_catalog._label_name(oid, graphid);
+
+CREATE FUNCTION ag_catalog._label_name(graph_oid oid, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_vertex with new signature (cstring -> 
agtype for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_vertex(graphid, cstring, 
agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_vertex(graphid, agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Drop and recreate _agtype_build_edge with new signature (cstring -> agtype 
for label)
+--
+DROP FUNCTION IF EXISTS ag_catalog._agtype_build_edge(graphid, graphid, 
graphid, cstring, agtype);
+
+CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid,
+                                              agtype, agtype)
+    RETURNS agtype
+    LANGUAGE c
+    IMMUTABLE
+CALLED ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- Helper function for optimized startNode/endNode
+--
+CREATE FUNCTION ag_catalog._get_vertex_by_graphid(text, graphid)
+    RETURNS agtype
+    LANGUAGE c
+    STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+-- Internal selftest for the agehash open-addressing hashtable. Returns "OK"
+-- on success or "FAIL: ..." with a diagnostic message. Intended for the
+-- agehash regression test only.
+CREATE FUNCTION ag_catalog._agehash_self_test()
+    RETURNS text
+    LANGUAGE c
+    VOLATILE
+    PARALLEL UNSAFE
+AS 'MODULE_PATHNAME';.id = 
+--
+-- Issue #2356: restore lightweight selectivity functions for containment
+-- and key-existence operators.
+--
+-- The PG14+ branches of AGE bound RESTRICT=matchingsel / JOIN=matchingjoinsel
+-- on @>, <@, @>>, <<@, ?, ?|, ?&. matchingsel is built for pattern operators
+-- (LIKE / regex) and invokes the operator's underlying support function on
+-- pg_statistic MCVs during planning. For agtype that re-runs agtype_contains
+-- per MCV, which can dominate planning time on point queries (TPS regression
+-- reported on PG18). PostgreSQL core itself binds @>/<@/? on jsonb to
+-- contsel/contjoinsel for the same reason; this aligns AGE with that
+-- precedent.
+--
+ALTER OPERATOR ag_catalog.@>(agtype, agtype)
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.<@(agtype, agtype)
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.@>>(agtype, agtype)
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.<<@(agtype, agtype)
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.?(agtype, text)
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.?(agtype, agtype)
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.?|(agtype, text[])
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.?|(agtype, agtype)
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.?&(agtype, text[])
+    SET (RESTRICT = contsel, JOIN = contjoinsel);
+ALTER OPERATOR ag_catalog.?&(agtype, agtype)
+    SET (RESTRICT = contsel, JOIN = contjoinsel);.id $;

Review Comment:
   The final `ALTER OPERATOR ...` statement has extra trailing garbage (`;.id 
$;`). This makes the script syntactically invalid and will prevent extension 
upgrades. Remove the stray fragment so the statement ends at the semicolon.



-- 
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