Skip to content

Commit 5ef7d6d

Browse files
Add vertex and edge composite types with direct field access optimization (#2303)
- Introduce vertex and edge as pg composite types vertex: (id, label, properties) edge: (id, label, start_id, end_id, properties) - end_id precedes start_id intentionally, it matches the agtype edge's canonical key order (keys sorted by length, end_id=6 < start_id=8), keeping the composite positionally consistent with the agtype representation. - Property access (a.name) now directly uses a.properties for agtype_access_operator instead of rebuilding via _agtype_build_vertex/edge - Optimize accessor functions (id, properties, label, type, start_id, end_id) to use direct FieldSelect on composite types instead of agtype functions - Add casts: vertex/edge to agtype, vertex/edge to json/jsonb - Add eq and not eq ops for vertex/edge composite types. - Fix label_name specific routine to use cache instead of ag_label scan - Write/update clauses have executors strictly tied to agtype, due to which the variables after any write/update clause are carried forward as agtype. - Allows users to completely skip agtype build functions and return vertex/edge for pure read queries. - Change _label_name to return agtype since record comparisons are not allowed with cstring. Consequently, _agtype_build_vertex/edge now accept agtype as label. - Fix MERGE clause type mismatch when accessing properties from previous MATCH clauses by wrapping columns with agtype_volatile_wrapper before namespace lookup. - Update expression index in pgvector.sql, since now it uses raw properties column instead of _agtype_build_vertex/edge. - Add regression tests Assisted-by AI
1 parent f02eda0 commit 5ef7d6d

25 files changed

Lines changed: 3643 additions & 376 deletions

age--1.7.0--y.y.y.sql

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,3 +536,220 @@ STABLE
536536
CALLED ON NULL INPUT
537537
PARALLEL UNSAFE
538538
AS 'MODULE_PATHNAME';
539+
540+
--
541+
-- Composite types for vertex and edge
542+
--
543+
CREATE TYPE ag_catalog.vertex AS (
544+
id graphid,
545+
label agtype,
546+
properties agtype
547+
);
548+
549+
CREATE TYPE ag_catalog.edge AS (
550+
id graphid,
551+
label agtype,
552+
end_id graphid,
553+
start_id graphid,
554+
properties agtype
555+
);
556+
557+
--
558+
-- vertex/edge to agtype cast functions
559+
--
560+
CREATE FUNCTION ag_catalog.vertex_to_agtype(vertex)
561+
RETURNS agtype
562+
LANGUAGE c
563+
IMMUTABLE
564+
RETURNS NULL ON NULL INPUT
565+
PARALLEL SAFE
566+
AS 'MODULE_PATHNAME';
567+
568+
CREATE FUNCTION ag_catalog.edge_to_agtype(edge)
569+
RETURNS agtype
570+
LANGUAGE c
571+
IMMUTABLE
572+
RETURNS NULL ON NULL INPUT
573+
PARALLEL SAFE
574+
AS 'MODULE_PATHNAME';
575+
576+
--
577+
-- Implicit casts from vertex/edge to agtype
578+
--
579+
CREATE CAST (vertex AS agtype)
580+
WITH FUNCTION ag_catalog.vertex_to_agtype(vertex)
581+
AS IMPLICIT;
582+
583+
CREATE CAST (edge AS agtype)
584+
WITH FUNCTION ag_catalog.edge_to_agtype(edge)
585+
AS IMPLICIT;
586+
587+
CREATE FUNCTION ag_catalog.vertex_to_json(vertex)
588+
RETURNS json
589+
LANGUAGE c
590+
IMMUTABLE
591+
RETURNS NULL ON NULL INPUT
592+
PARALLEL SAFE
593+
AS 'MODULE_PATHNAME';
594+
595+
CREATE FUNCTION ag_catalog.edge_to_json(edge)
596+
RETURNS json
597+
LANGUAGE c
598+
IMMUTABLE
599+
RETURNS NULL ON NULL INPUT
600+
PARALLEL SAFE
601+
AS 'MODULE_PATHNAME';
602+
603+
CREATE CAST (vertex AS json)
604+
WITH FUNCTION ag_catalog.vertex_to_json(vertex);
605+
606+
CREATE CAST (edge AS json)
607+
WITH FUNCTION ag_catalog.edge_to_json(edge);
608+
609+
CREATE FUNCTION ag_catalog.vertex_to_jsonb(vertex)
610+
RETURNS jsonb
611+
LANGUAGE c
612+
IMMUTABLE
613+
RETURNS NULL ON NULL INPUT
614+
PARALLEL SAFE
615+
AS 'MODULE_PATHNAME';
616+
617+
CREATE FUNCTION ag_catalog.edge_to_jsonb(edge)
618+
RETURNS jsonb
619+
LANGUAGE c
620+
IMMUTABLE
621+
RETURNS NULL ON NULL INPUT
622+
PARALLEL SAFE
623+
AS 'MODULE_PATHNAME';
624+
625+
CREATE CAST (vertex AS jsonb)
626+
WITH FUNCTION ag_catalog.vertex_to_jsonb(vertex);
627+
628+
CREATE CAST (edge AS jsonb)
629+
WITH FUNCTION ag_catalog.edge_to_jsonb(edge);
630+
631+
--
632+
-- Equality operators for vertex and edge (compare by id)
633+
--
634+
CREATE FUNCTION ag_catalog.vertex_eq(vertex, vertex)
635+
RETURNS boolean
636+
LANGUAGE sql
637+
IMMUTABLE
638+
RETURNS NULL ON NULL INPUT
639+
PARALLEL SAFE
640+
AS $$ SELECT $1.id = $2.id $$;
641+
642+
CREATE OPERATOR = (
643+
FUNCTION = ag_catalog.vertex_eq,
644+
LEFTARG = vertex,
645+
RIGHTARG = vertex,
646+
COMMUTATOR = =,
647+
NEGATOR = <>,
648+
RESTRICT = eqsel,
649+
JOIN = eqjoinsel
650+
);
651+
652+
CREATE FUNCTION ag_catalog.vertex_ne(vertex, vertex)
653+
RETURNS boolean
654+
LANGUAGE sql
655+
IMMUTABLE
656+
RETURNS NULL ON NULL INPUT
657+
PARALLEL SAFE
658+
AS $$ SELECT $1.id <> $2.id $$;
659+
660+
CREATE OPERATOR <> (
661+
FUNCTION = ag_catalog.vertex_ne,
662+
LEFTARG = vertex,
663+
RIGHTARG = vertex,
664+
COMMUTATOR = <>,
665+
NEGATOR = =,
666+
RESTRICT = neqsel,
667+
JOIN = neqjoinsel
668+
);
669+
670+
CREATE FUNCTION ag_catalog.edge_eq(edge, edge)
671+
RETURNS boolean
672+
LANGUAGE sql
673+
IMMUTABLE
674+
RETURNS NULL ON NULL INPUT
675+
PARALLEL SAFE
676+
AS $$ SELECT $1.id = $2.id $$;
677+
678+
CREATE OPERATOR = (
679+
FUNCTION = ag_catalog.edge_eq,
680+
LEFTARG = edge,
681+
RIGHTARG = edge,
682+
COMMUTATOR = =,
683+
NEGATOR = <>,
684+
RESTRICT = eqsel,
685+
JOIN = eqjoinsel
686+
);
687+
688+
CREATE FUNCTION ag_catalog.edge_ne(edge, edge)
689+
RETURNS boolean
690+
LANGUAGE sql
691+
IMMUTABLE
692+
RETURNS NULL ON NULL INPUT
693+
PARALLEL SAFE
694+
AS $$ SELECT $1.id <> $2.id $$;
695+
696+
CREATE OPERATOR <> (
697+
FUNCTION = ag_catalog.edge_ne,
698+
LEFTARG = edge,
699+
RIGHTARG = edge,
700+
COMMUTATOR = <>,
701+
NEGATOR = =,
702+
RESTRICT = neqsel,
703+
JOIN = neqjoinsel
704+
);
705+
706+
--
707+
-- Drop and recreate _label_name with new return type (cstring -> agtype)
708+
--
709+
DROP FUNCTION IF EXISTS ag_catalog._label_name(oid, graphid);
710+
711+
CREATE FUNCTION ag_catalog._label_name(graph_oid oid, graphid)
712+
RETURNS agtype
713+
LANGUAGE c
714+
IMMUTABLE
715+
PARALLEL SAFE
716+
AS 'MODULE_PATHNAME';
717+
718+
--
719+
-- Drop and recreate _agtype_build_vertex with new signature (cstring -> agtype for label)
720+
--
721+
DROP FUNCTION IF EXISTS ag_catalog._agtype_build_vertex(graphid, cstring, agtype);
722+
723+
CREATE FUNCTION ag_catalog._agtype_build_vertex(graphid, agtype, agtype)
724+
RETURNS agtype
725+
LANGUAGE c
726+
IMMUTABLE
727+
CALLED ON NULL INPUT
728+
PARALLEL SAFE
729+
AS 'MODULE_PATHNAME';
730+
731+
--
732+
-- Drop and recreate _agtype_build_edge with new signature (cstring -> agtype for label)
733+
--
734+
DROP FUNCTION IF EXISTS ag_catalog._agtype_build_edge(graphid, graphid, graphid, cstring, agtype);
735+
736+
CREATE FUNCTION ag_catalog._agtype_build_edge(graphid, graphid, graphid,
737+
agtype, agtype)
738+
RETURNS agtype
739+
LANGUAGE c
740+
IMMUTABLE
741+
CALLED ON NULL INPUT
742+
PARALLEL SAFE
743+
AS 'MODULE_PATHNAME';
744+
745+
--
746+
-- Helper function for optimized startNode/endNode
747+
--
748+
CREATE FUNCTION ag_catalog._get_vertex_by_graphid(text, graphid)
749+
RETURNS agtype
750+
LANGUAGE c
751+
STABLE
752+
RETURNS NULL ON NULL INPUT
753+
PARALLEL SAFE
754+
AS 'MODULE_PATHNAME';
755+

0 commit comments

Comments
 (0)