0001-WIP-SQL-Property-Graph-Queries-SQL-PGQ-20240305.patch
text/x-patch
Filename: 0001-WIP-SQL-Property-Graph-Queries-SQL-PGQ-20240305.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0001
Subject: WIP: SQL Property Graph Queries (SQL/PGQ)
| File | + | − |
|---|---|---|
| doc/src/sgml/catalogs.sgml | 47 | 1 |
| doc/src/sgml/ddl.sgml | 228 | 1 |
| doc/src/sgml/features.sgml | 2 | 2 |
| doc/src/sgml/func.sgml | 15 | 0 |
| doc/src/sgml/information_schema.sgml | 80 | 0 |
| doc/src/sgml/keywords/sql2023-16-nonreserved.txt | 27 | 0 |
| doc/src/sgml/keywords/sql2023-16-reserved.txt | 12 | 0 |
| doc/src/sgml/queries.sgml | 156 | 0 |
| doc/src/sgml/ref/allfiles.sgml | 3 | 0 |
| doc/src/sgml/ref/alter_extension.sgml | 2 | 1 |
| doc/src/sgml/ref/alter_property_graph.sgml | 150 | 0 |
| doc/src/sgml/ref/comment.sgml | 1 | 0 |
| doc/src/sgml/ref/create_property_graph.sgml | 232 | 0 |
| doc/src/sgml/ref/drop_property_graph.sgml | 111 | 0 |
| doc/src/sgml/reference.sgml | 3 | 0 |
| doc/src/sgml/ref/grant.sgml | 6 | 1 |
| doc/src/sgml/ref/psql-ref.sgml | 7 | 6 |
| doc/src/sgml/ref/revoke.sgml | 7 | 0 |
| doc/src/sgml/ref/security_label.sgml | 1 | 0 |
| doc/src/sgml/ref/select.sgml | 43 | 0 |
| src/backend/catalog/aclchk.c | 24 | 0 |
| src/backend/catalog/dependency.c | 17 | 0 |
| src/backend/catalog/information_schema.sql | 363 | 0 |
| src/backend/catalog/Makefile | 4 | 1 |
| src/backend/catalog/objectaddress.c | 140 | 0 |
| src/backend/catalog/pg_class.c | 2 | 0 |
| src/backend/catalog/sql_features.txt | 100 | 0 |
| src/backend/commands/alter.c | 25 | 6 |
| src/backend/commands/dropcmds.c | 1 | 0 |
| src/backend/commands/event_trigger.c | 6 | 0 |
| src/backend/commands/Makefile | 1 | 0 |
| src/backend/commands/meson.build | 1 | 0 |
| src/backend/commands/propgraphcmds.c | 1001 | 0 |
| src/backend/commands/seclabel.c | 1 | 0 |
| src/backend/commands/tablecmds.c | 14 | 0 |
| src/backend/executor/execMain.c | 1 | 1 |
| src/backend/nodes/nodeFuncs.c | 72 | 0 |
| src/backend/nodes/outfuncs.c | 5 | 0 |
| src/backend/nodes/queryjumblefuncs.c | 5 | 0 |
| src/backend/nodes/readfuncs.c | 5 | 0 |
| src/backend/optimizer/path/allpaths.c | 4 | 0 |
| src/backend/optimizer/prep/prepjointree.c | 8 | 0 |
| src/backend/parser/gram.y | 684 | 9 |
| src/backend/parser/Makefile | 1 | 0 |
| src/backend/parser/meson.build | 1 | 0 |
| src/backend/parser/parse_clause.c | 85 | 0 |
| src/backend/parser/parse_collate.c | 7 | 0 |
| src/backend/parser/parse_graphtable.c | 251 | 0 |
| src/backend/parser/parse_relation.c | 94 | 0 |
| src/backend/parser/parse_target.c | 5 | 0 |
| src/backend/parser/scan.l | 48 | 0 |
| src/backend/rewrite/Makefile | 1 | 0 |
| src/backend/rewrite/meson.build | 1 | 0 |
| src/backend/rewrite/rewriteGraphTable.c | 422 | 0 |
| src/backend/rewrite/rewriteHandler.c | 12 | 0 |
| src/backend/tcop/utility.c | 34 | 0 |
| src/backend/utils/adt/ruleutils.c | 502 | 0 |
| src/bin/pg_dump/common.c | 2 | 1 |
| src/bin/pg_dump/pg_backup_archiver.c | 1 | 0 |
| src/bin/pg_dump/pg_dump.c | 60 | 7 |
| src/bin/pg_dump/t/002_pg_dump.pl | 11 | 0 |
| src/bin/psql/command.c | 2 | 1 |
| src/bin/psql/describe.c | 41 | 2 |
| src/bin/psql/help.c | 1 | 0 |
| src/bin/psql/tab-complete.c | 63 | 2 |
| src/fe_utils/psqlscan.l | 8 | 0 |
| src/include/catalog/dependency.h | 3 | 0 |
| src/include/catalog/meson.build | 3 | 0 |
| src/include/catalog/pg_class.h | 1 | 0 |
| src/include/catalog/pg_proc.dat | 3 | 0 |
| src/include/catalog/pg_propgraph_element.h | 103 | 0 |
| src/include/catalog/pg_propgraph_label.h | 59 | 0 |
| src/include/catalog/pg_propgraph_property.h | 74 | 0 |
| src/include/commands/propgraphcmds.h | 23 | 0 |
| src/include/nodes/parsenodes.h | 135 | 0 |
| src/include/nodes/primnodes.h | 22 | 0 |
| src/include/parser/kwlist.h | 10 | 0 |
| src/include/parser/parse_graphtable.h | 31 | 0 |
| src/include/parser/parse_relation.h | 8 | 0 |
| src/include/rewrite/rewriteGraphTable.h | 21 | 0 |
| src/include/tcop/cmdtaglist.h | 3 | 0 |
| src/include/utils/acl.h | 1 | 0 |
| src/pl/plpgsql/src/pl_gram.y | 1 | 0 |
| src/test/regress/expected/alter_generic.out | 49 | 2 |
| src/test/regress/expected/create_property_graph.out | 370 | 0 |
| src/test/regress/expected/graph_table.out | 122 | 0 |
| src/test/regress/expected/object_address.out | 12 | 2 |
| src/test/regress/expected/oidjoins.out | 9 | 0 |
| src/test/regress/parallel_schedule | 2 | 2 |
| src/test/regress/sql/alter_generic.sql | 34 | 0 |
| src/test/regress/sql/create_property_graph.sql | 130 | 0 |
| src/test/regress/sql/graph_table.sql | 96 | 0 |
| src/test/regress/sql/object_address.sql | 3 | 1 |
| src/tools/pgindent/typedefs.list | 19 | 0 |
From 3595146327c87b3255bb2efc777f6952937d4cb7 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 16 Feb 2024 15:50:43 +0100
Subject: [PATCH] WIP: SQL Property Graph Queries (SQL/PGQ)
Implementation of SQL property graph queries, according to SQL/PGQ
standard (ISO 9075-16:2023).
---
doc/src/sgml/catalogs.sgml | 48 +-
doc/src/sgml/ddl.sgml | 229 +++-
doc/src/sgml/features.sgml | 4 +-
doc/src/sgml/func.sgml | 15 +
doc/src/sgml/information_schema.sgml | 80 ++
.../sgml/keywords/sql2023-16-nonreserved.txt | 27 +
doc/src/sgml/keywords/sql2023-16-reserved.txt | 12 +
doc/src/sgml/queries.sgml | 156 +++
doc/src/sgml/ref/allfiles.sgml | 3 +
doc/src/sgml/ref/alter_extension.sgml | 3 +-
doc/src/sgml/ref/alter_property_graph.sgml | 150 +++
doc/src/sgml/ref/comment.sgml | 1 +
doc/src/sgml/ref/create_property_graph.sgml | 232 ++++
doc/src/sgml/ref/drop_property_graph.sgml | 111 ++
doc/src/sgml/ref/grant.sgml | 7 +-
doc/src/sgml/ref/psql-ref.sgml | 13 +-
doc/src/sgml/ref/revoke.sgml | 7 +
doc/src/sgml/ref/security_label.sgml | 1 +
doc/src/sgml/ref/select.sgml | 43 +
doc/src/sgml/reference.sgml | 3 +
src/backend/catalog/Makefile | 5 +-
src/backend/catalog/aclchk.c | 24 +
src/backend/catalog/dependency.c | 17 +
src/backend/catalog/information_schema.sql | 363 ++++++
src/backend/catalog/objectaddress.c | 140 +++
src/backend/catalog/pg_class.c | 2 +
src/backend/catalog/sql_features.txt | 100 ++
src/backend/commands/Makefile | 1 +
src/backend/commands/alter.c | 31 +-
src/backend/commands/dropcmds.c | 1 +
src/backend/commands/event_trigger.c | 6 +
src/backend/commands/meson.build | 1 +
src/backend/commands/propgraphcmds.c | 1001 +++++++++++++++++
src/backend/commands/seclabel.c | 1 +
src/backend/commands/tablecmds.c | 14 +
src/backend/executor/execMain.c | 2 +-
src/backend/nodes/nodeFuncs.c | 72 ++
src/backend/nodes/outfuncs.c | 5 +
src/backend/nodes/queryjumblefuncs.c | 5 +
src/backend/nodes/readfuncs.c | 5 +
src/backend/optimizer/path/allpaths.c | 4 +
src/backend/optimizer/prep/prepjointree.c | 8 +
src/backend/parser/Makefile | 1 +
src/backend/parser/gram.y | 693 +++++++++++-
src/backend/parser/meson.build | 1 +
src/backend/parser/parse_clause.c | 85 ++
src/backend/parser/parse_collate.c | 7 +
src/backend/parser/parse_graphtable.c | 251 +++++
src/backend/parser/parse_relation.c | 94 ++
src/backend/parser/parse_target.c | 5 +
src/backend/parser/scan.l | 48 +
src/backend/rewrite/Makefile | 1 +
src/backend/rewrite/meson.build | 1 +
src/backend/rewrite/rewriteGraphTable.c | 422 +++++++
src/backend/rewrite/rewriteHandler.c | 12 +
src/backend/tcop/utility.c | 34 +
src/backend/utils/adt/ruleutils.c | 502 +++++++++
src/bin/pg_dump/common.c | 3 +-
src/bin/pg_dump/pg_backup_archiver.c | 1 +
src/bin/pg_dump/pg_dump.c | 67 +-
src/bin/pg_dump/t/002_pg_dump.pl | 11 +
src/bin/psql/command.c | 3 +-
src/bin/psql/describe.c | 43 +-
src/bin/psql/help.c | 1 +
src/bin/psql/tab-complete.c | 65 +-
src/fe_utils/psqlscan.l | 8 +
src/include/catalog/dependency.h | 3 +
src/include/catalog/meson.build | 3 +
src/include/catalog/pg_class.h | 1 +
src/include/catalog/pg_proc.dat | 3 +
src/include/catalog/pg_propgraph_element.h | 103 ++
src/include/catalog/pg_propgraph_label.h | 59 +
src/include/catalog/pg_propgraph_property.h | 74 ++
src/include/commands/propgraphcmds.h | 23 +
src/include/nodes/parsenodes.h | 135 +++
src/include/nodes/primnodes.h | 22 +
src/include/parser/kwlist.h | 10 +
src/include/parser/parse_graphtable.h | 31 +
src/include/parser/parse_relation.h | 8 +
src/include/rewrite/rewriteGraphTable.h | 21 +
src/include/tcop/cmdtaglist.h | 3 +
src/include/utils/acl.h | 1 +
src/pl/plpgsql/src/pl_gram.y | 1 +
src/test/regress/expected/alter_generic.out | 51 +-
.../expected/create_property_graph.out | 370 ++++++
src/test/regress/expected/graph_table.out | 122 ++
src/test/regress/expected/object_address.out | 14 +-
src/test/regress/expected/oidjoins.out | 9 +
src/test/regress/parallel_schedule | 4 +-
src/test/regress/sql/alter_generic.sql | 34 +
.../regress/sql/create_property_graph.sql | 130 +++
src/test/regress/sql/graph_table.sql | 96 ++
src/test/regress/sql/object_address.sql | 4 +-
src/tools/pgindent/typedefs.list | 19 +
94 files changed, 6617 insertions(+), 49 deletions(-)
create mode 100644 doc/src/sgml/keywords/sql2023-16-nonreserved.txt
create mode 100644 doc/src/sgml/keywords/sql2023-16-reserved.txt
create mode 100644 doc/src/sgml/ref/alter_property_graph.sgml
create mode 100644 doc/src/sgml/ref/create_property_graph.sgml
create mode 100644 doc/src/sgml/ref/drop_property_graph.sgml
create mode 100644 src/backend/commands/propgraphcmds.c
create mode 100644 src/backend/parser/parse_graphtable.c
create mode 100644 src/backend/rewrite/rewriteGraphTable.c
create mode 100644 src/include/catalog/pg_propgraph_element.h
create mode 100644 src/include/catalog/pg_propgraph_label.h
create mode 100644 src/include/catalog/pg_propgraph_property.h
create mode 100644 src/include/commands/propgraphcmds.h
create mode 100644 src/include/parser/parse_graphtable.h
create mode 100644 src/include/rewrite/rewriteGraphTable.h
create mode 100644 src/test/regress/expected/create_property_graph.out
create mode 100644 src/test/regress/expected/graph_table.out
create mode 100644 src/test/regress/sql/create_property_graph.sql
create mode 100644 src/test/regress/sql/graph_table.sql
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 0ae97d1ada..8142d6fd4e 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -240,6 +240,21 @@
<entry>functions and procedures</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-propgraph-element"><structname>pg_propgraph_element</structname></link></entry>
+ <entry>property graph elements (vertices and edges)</entry>
+ </row>
+
+ <row>
+ <entry><link linkend="catalog-pg-propgraph-label"><structname>pg_propgraph_label</structname></link></entry>
+ <entry>property graph labels</entry>
+ </row>
+
+ <row>
+ <entry><link linkend="catalog-pg-propgraph-property"><structname>pg_propgraph_property</structname></link></entry>
+ <entry>property graph properties</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-publication"><structname>pg_publication</structname></link></entry>
<entry>publications for logical replication</entry>
@@ -2115,7 +2130,8 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
<literal>c</literal> = composite type,
<literal>f</literal> = foreign table,
<literal>p</literal> = partitioned table,
- <literal>I</literal> = partitioned index
+ <literal>I</literal> = partitioned index,
+ <literal>g</literal> = property graph
</para></entry>
</row>
@@ -6258,6 +6274,36 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</sect1>
+ <sect1 id="catalog-pg-propgraph-element">
+ <title><structname>pg_propgraph_element</structname></title>
+
+ <indexterm zone="catalog-pg-propgraph-element">
+ <primary>pg_propgraph_element</primary>
+ </indexterm>
+
+ <para>TODO</para>
+ </sect1>
+
+ <sect1 id="catalog-pg-propgraph-label">
+ <title><structname>pg_propgraph_label</structname></title>
+
+ <indexterm zone="catalog-pg-propgraph-label">
+ <primary>pg_propgraph_label</primary>
+ </indexterm>
+
+ <para>TODO</para>
+ </sect1>
+
+ <sect1 id="catalog-pg-propgraph-property">
+ <title><structname>pg_propgraph_property</structname></title>
+
+ <indexterm zone="catalog-pg-propgraph-property">
+ <primary>pg_propgraph_property</primary>
+ </indexterm>
+
+ <para>TODO</para>
+ </sect1>
+
<sect1 id="catalog-pg-publication">
<title><structname>pg_publication</structname></title>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 9d7e2c756b..7333cf57e3 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1951,7 +1951,7 @@ REVOKE ALL ON accounts FROM PUBLIC;
<para>
Allows <command>SELECT</command> from
any column, or specific column(s), of a table, view, materialized
- view, or other table-like object.
+ view, property graph, or other table-like object.
Also allows use of <command>COPY TO</command>.
This privilege is also needed to reference existing column values in
<command>UPDATE</command>, <command>DELETE</command>,
@@ -5269,6 +5269,233 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
</para>
</sect1>
+ <sect1 id="ddl-property-graphs">
+ <title>Property Graphs</title>
+
+ <indexterm zone="ddl-property-graphs">
+ <primary>property graph</primary>
+ </indexterm>
+
+ <para>
+ A property graph is a way to represent database contents, instead of using
+ relational structures such as tables. A property graph can then be queried
+ using graph pattern matching syntax, instead of join queries typical of
+ relational databases. PostgreSQL implements SQL/PGQ<footnote><para>Here,
+ PGQ stands for <quote>property graph query</quote>. In the jargon of graph
+ databases, <quote>property graph</quote> is normally abbreviated as PG,
+ which is clearly confusing for practioners of PostgreSQL, also usually
+ abbreviated as PG.</para></footnote>, which is part of the SQL standard,
+ where a property graph is defined as a kind of read-only view over
+ relational tables. So the actual data is still in tables or table-like
+ objects, but is exposed as a graph for graph querying operations. (This is
+ in contrast to native graph databases, where the data is stored directly in
+ a graph structure.) Underneath, both relational queries and graph queries
+ use the same query planning and execution infrastucture, and in fact
+ relational and graph queries can be combined and mixed in single queries.
+ </para>
+
+ <para>
+ A graph is a set of vertices and edges. Each edge has two distinguishable
+ associated vertices called the source and destination vertices. (So in
+ this model all edges are directed.) Vertices and edges together are called
+ the elements of the graph. A property graph extends this well-known
+ mathematical structure with a way to represent user data. In a property
+ graph, each vertex or edge has one or more associated labels, and each
+ label has zero or more properties. The labels are similar to table row
+ types in that they define the kind of the contained data and its structure.
+ The properties are similar to columns in that they contain the actual data.
+ In fact, by default, a property graph definition exposes the underlying
+ tables and columns as labels and properties, but more complicated
+ definitions are possible.
+ </para>
+
+ <para>
+ Consider the following table definitions:
+<programlisting>
+CREATE TABLE products (
+ product_no integer PRIMARY KEY,
+ name varchar,
+ price numeric
+);
+
+CREATE TABLE customers (
+ customer_id integer PRIMARY KEY,
+ name varchar,
+ address varchar
+);
+
+CREATE TABLE orders (
+ order_id integer PRIMARY KEY,
+ ordered_when date
+);
+
+CREATE TABLE order_items (
+ order_items_id integer PRIMARY KEY,
+ order_id integer REFERENCES orders (order_id),
+ product_no integer REFERENCES products (product_no),
+ quantity integer
+);
+
+CREATE TABLE customer_orders (
+ customer_orders_id integer PRIMARY KEY,
+ customer_id integer REFERENCES customers (customer_id),
+ order_id integer REFERENCES orders (order_id)
+);
+</programlisting>
+ When mapping this to a graph, the first three tables would be the vertices
+ and the last two tables would be the edges. The foreign-key definitions
+ correspond to the fact that edges link two vertices. (Graph definitions
+ work more naturally with many-to-many relationships, so this example is
+ organized like that, even though one-to-many relationships might be used
+ here in a pure relational approach.)
+ </para>
+
+ <para>
+ Here is an example how a property graph could be defined on top of these
+ tables:
+<programlisting>
+CREATE PROPERTY GRAPH myshop
+ VERTEX TABLES (
+ products,
+ customers,
+ orders
+ )
+ EDGE TABLES (
+ order_items SOURCE orders DESTINATION products,
+ customer_orders SOURCE customers DESTINATION orders
+ );
+</programlisting>
+ </para>
+
+ <para>
+ This graph could then be queried like this:
+<programlisting>
+-- get list of customers active today
+SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers)-[IS customer_orders]->(o IS orders WHERE ordered_when = current_date) COLUMNS (c.name AS customer_name));
+</programlisting>
+ corresponding approximately to this relational query:
+<programlisting>
+-- get list of customers active today
+SELECT customers.name FROM customers JOIN customer_orders USING (customer_id) JOIN orders USING (order_id) WHERE orders.ordered_when = current_date;
+</programlisting>
+ </para>
+
+ <para>
+ The above definition requires that all tables have primary keys and that
+ for each edge there is an appropriate foreign key. Otherwise, additional
+ clauses have to be specified to identify the key columns. For example,
+ this would be the fully verbose definition that does not rely on primary
+ and foreign keys:
+<programlisting>
+CREATE PROPERTY GRAPH myshop
+ VERTEX TABLES (
+ products KEY (product_no),
+ customers KEY (customer_id),
+ orders KEY (order_id)
+ )
+ EDGE TABLES (
+ order_items KEY (order_items_id)
+ SOURCE KEY (order_id) REFERENCES orders (order_id)
+ DESTINATION KEY (product_no) REFERENCES products (product_no),
+ customer_orders KEY (customer_orders_id)
+ SOURCE KEY (customer_id) REFERENCES customers (customer_id)
+ DESTINATION KEY (order_id) REFERENCES orders (order_id)
+ );
+</programlisting>
+ </para>
+
+ <para>
+ As mentioned above, by default, the names of the tables and columns are
+ exposed as labels and properties, respectively. The clauses <literal>IS
+ customer</literal>, <literal>IS order</literal>, etc. in the
+ <literal>MATCH</literal> clause in fact refer to labels, not table names.
+ </para>
+
+ <para>
+ One use of labels is to expose a table through a different name in the
+ graph. For example, in graphs, vertices typically have singular noun
+ labels and edges typically have verbs as labels, such as <quote>is</quote>,
+ <quote>has</quote>, <quote>contains</quote>, or something specific like
+ <quote>approves</quote>. We can introduce such labels into our example
+ like this:
+<programlisting>
+CREATE PROPERTY GRAPH myshop
+ VERTEX TABLES (
+ products LABEL product,
+ customers LABEL customer,
+ orders LABEL order
+ )
+ EDGE TABLES (
+ order_items SOURCE orders DESTINATION products LABEL contains,
+ customer_orders SOURCE customers DESTINATION orders LABEL has
+ );
+</programlisting>
+ </para>
+
+ <para>
+ With this definition, we can write a query like this:
+<programlisting>
+SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c:customer)-[:has]->(o:order WHERE ordered_when = current_date) COLUMNS (c.name AS customer_name));
+</programlisting>
+ With the new labels and using the colon instead of <literal>IS</literal>,
+ which are equivalent, the <literal>MATCH</literal> clause is now more
+ compact and intuitive.
+ </para>
+
+ <para>
+ Another use is to apply the same label to multiple element tables. For
+ example, consider this additional table:
+<programlisting>
+CREATE TABLE employees (
+ employee_id integer PRIMARY KEY,
+ employee_name varchar,
+ ...
+);
+</programlisting>
+and the following graph definition:
+<programlisting>
+CREATE PROPERTY GRAPH myshop
+ VERTEX TABLES (
+ products LABEL product,
+ customers LABEL customer LABEL person PROPERTIES (name),
+ orders LABEL order,
+ employees LABEL employee LABEL person PROPERTIES (employee_name AS name)
+ )
+ EDGE TABLES (
+ order_items SOURCE orders DESTINATION products LABEL contains,
+ customer_orders SOURCE customers DESTINATION orders LABEL has
+ );
+</programlisting>
+ (In practice, there ought to be an edge linking the
+ <literal>employees</literal> table to something, but it is allowed like
+ this.)
+ </para>
+
+ <para>
+ Then we can run a query like this (incomplete):
+<programlisting>
+SELECT ... FROM GRAPH_TABLE (myshop MATCH (:person WHERE name = '...')-[]->... COLUMNS (...));
+</programlisting>
+ This would automatically consider both the <literal>customers</literal> and
+ the <literal>employees</literal> tables when looking for an edge with the
+ <literal>person</literal> label.
+ </para>
+
+ <para>
+ When more than one element table has the same label, it is required that
+ the properties match in number, name, and type. In the example, we specify
+ an explicit property list and in one case override the name of the column
+ to achieve this.
+ </para>
+
+ <para>
+ For more details on the syntax for creating property graphs, see <link
+ linkend="sql-create-property-graph"><command>CREATE PROPERTY
+ GRAPH</command></link>. More details about the graph query syntax is in
+ <xref linkend="queries-graph"/>.
+ </para>
+ </sect1>
+
<sect1 id="ddl-others">
<title>Other Database Objects</title>
diff --git a/doc/src/sgml/features.sgml b/doc/src/sgml/features.sgml
index 966fd39882..1abe6ccd3d 100644
--- a/doc/src/sgml/features.sgml
+++ b/doc/src/sgml/features.sgml
@@ -70,10 +70,10 @@
<para>
The <productname>PostgreSQL</productname> core covers parts 1, 2, 9,
- 11, and 14. Part 3 is covered by the ODBC driver, and part 13 is
+ 11, 14, and 16. Part 3 is covered by the ODBC driver, and part 13 is
covered by the PL/Java plug-in, but exact conformance is currently
not being verified for these components. There are currently no
- implementations of parts 4, 10, 15, and 16
+ implementations of parts 4, 10, and 15
for <productname>PostgreSQL</productname>.
</para>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index e5fa82c161..2e720c62c8 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24999,6 +24999,21 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
</para></entry>
</row>
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>pg_get_propgraphdef</primary>
+ </indexterm>
+ <function>pg_get_propgraphdef</function> ( <parameter>propgraph</parameter> <type>oid</type> )
+ <returnvalue>text</returnvalue>
+ </para>
+ <para>
+ Reconstructs the creating command for a property graph.
+ (This is a decompiled reconstruction, not the original text
+ of the command.)
+ </para></entry>
+ </row>
+
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml
index 9e66be4e83..02f18eedbd 100644
--- a/doc/src/sgml/information_schema.sgml
+++ b/doc/src/sgml/information_schema.sgml
@@ -4171,6 +4171,86 @@ ORDER BY c.ordinal_position;
</table>
</sect1>
+ <sect1 id="infoschema-pg-edge-table-components">
+ <title><literal>pg_edge_table_components</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-pg-element-table-key-columns">
+ <title><literal>pg_element_table_key_columns</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-pg-element-table-labels">
+ <title><literal>pg_element_table_labels</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-pg-element-table-properties">
+ <title><literal>pg_element_table_properties</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-pg-element-tables">
+ <title><literal>pg_element_tables</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-pg-label-properties">
+ <title><literal>pg_element_label_properties</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-pg-labels">
+ <title><literal>pg_element_labels</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-pg-property-data-types">
+ <title><literal>pg_property_data_types</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-pg-property-graph-privileges">
+ <title><literal>pg_property_graph_privileges</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
+ <sect1 id="infoschema-property-graphs">
+ <title><literal>property_graphs</literal></title>
+
+ <para>
+ TODO
+ </para>
+ </sect1>
+
<sect1 id="infoschema-referential-constraints">
<title><literal>referential_constraints</literal></title>
diff --git a/doc/src/sgml/keywords/sql2023-16-nonreserved.txt b/doc/src/sgml/keywords/sql2023-16-nonreserved.txt
new file mode 100644
index 0000000000..39756c6067
--- /dev/null
+++ b/doc/src/sgml/keywords/sql2023-16-nonreserved.txt
@@ -0,0 +1,27 @@
+ACYCLIC
+BINDINGS
+BOUND
+DESTINATION
+DIFFERENT
+DIRECTED
+EDGE
+EDGES
+ELEMENTS
+LABEL
+LABELED
+NODE
+PATHS
+PROPERTIES
+PROPERTY
+PROPERTY_GRAPH_CATALOG
+PROPERTY_GRAPH_NAME
+PROPERTY_GRAPH_SCHEMA
+RELATIONSHIP
+RELATIONSHIPS
+SHORTEST
+SINGLETONS
+STEP
+TABLES
+TRAIL
+VERTEX
+WALK
diff --git a/doc/src/sgml/keywords/sql2023-16-reserved.txt b/doc/src/sgml/keywords/sql2023-16-reserved.txt
new file mode 100644
index 0000000000..3bdd7e2b27
--- /dev/null
+++ b/doc/src/sgml/keywords/sql2023-16-reserved.txt
@@ -0,0 +1,12 @@
+ALL_DIFFERENT
+BINDING_COUNT
+ELEMENT_ID
+ELEMENT_NUMBER
+EXPORT
+GRAPH
+GRAPH_TABLE
+MATCHNUM
+PATH_LENGTH
+PATH_NAME
+PROPERTY_EXISTS
+SAME
diff --git a/doc/src/sgml/queries.sgml b/doc/src/sgml/queries.sgml
index 648b283b06..5b088331c6 100644
--- a/doc/src/sgml/queries.sgml
+++ b/doc/src/sgml/queries.sgml
@@ -2744,4 +2744,160 @@ SELECT * FROM t;
</sect1>
+ <sect1 id="queries-graph">
+ <title>Graph Queries</title>
+
+ <para>
+ This section describes the sublanguage for querying property graphs,
+ defined as described in <xref linkend="ddl-property-graphs"/>.
+ </para>
+
+ <sect2 id="queries-graph-overview">
+ <title>Overview</title>
+
+ <para>
+ Consider this example from <xref linkend="ddl-property-graphs"/>:
+<programlisting>
+-- get list of customers active today
+SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers)-[IS customer_orders]->(o IS orders WHERE o.ordered_when = current_date) COLUMNS (c.name AS customer_name));
+</programlisting>
+ The graph query part happens inside the <literal>GRAPH_TABLE</literal>
+ construct. As far as the rest of the query is concerned, this acts like a
+ table function in that it produces a computed table as output. Like other
+ <literal>FROM</literal> clause elements, table alias and column alias
+ names can be assigned to the result, and the result can be joined with
+ other tables, subsequently filtered, and so on, for example:
+<programlisting>
+SELECT ... FROM GRAPH_TABLE (mygraph MATCH ... COLUMNS (...)) AS myresult (a, b, c) JOIN othertable USING (a) WHERE b > 0 ORDER BY c;
+</programlisting>
+ </para>
+
+ <para>
+ The <literal>GRAPH_TABLE</literal> clause consists of the graph name,
+ followed by the keyword <literal>MATCH</literal>, followed by a graph
+ pattern expression (see below), followed by the keyword
+ <literal>COLUMNS</literal> and a column list.
+ </para>
+ </sect2>
+
+ <sect2 id="queries-graph-patterns">
+ <title>Graph Patterns</title>
+
+ <para>
+ The core of the graph querying functionality is the graph pattern, which
+ appears after the keyword <literal>MATCH</literal>. Formally, a graph
+ pattern consists of one or more path patterns. A path is a sequence of
+ graph elements, starting and ending with a vertex and alternating between
+ vertices and edges. A path pattern is a syntactic expressions that
+ matches paths.
+ </para>
+
+ <para>
+ A path pattern thus matches a sequence of vertices and edges. The
+ simplest possible path pattern is
+<programlisting>
+()
+</programlisting>
+ which matches a single vertex. The next simplest pattern would be
+<programlisting>
+()-[]->()
+</programlisting>
+ which matches a vertex followed by an edge followed by a vertex. The
+ characters <literal>()</literal> are a vertex pattern and the characters
+ <literal>-[]-></literal> are an edge pattern.
+ </para>
+
+ <tip>
+ <para>
+ A way to remember these symbols is that in visual representations of
+ property graphs, vertices are usually circles (like
+ <literal>()</literal>) and edges have rectangular labels (like
+ <literal>[]</literal>).
+ </para>
+ </tip>
+
+ <para>
+ The above patterns would match any vertex, or any two vertices connected
+ by any edge, which isn't very interesting. Normally, we want to search
+ for elements (vertices and edges) that have certain characteristics.
+ These characteristics are written in between the parentheses or brackets.
+ (This is also called an element pattern filler.) Typically, we would
+ search for elements with a certain label. This is written either by
+ <literal>IS <replaceable>labelname</replaceable></literal> or equivalently
+ <literal>:<replaceable>labelname</replaceable></literal>. For example,
+ this would match all vertices with the label <literal>person</literal>:
+<programlisting>
+(IS person)
+</programlisting>
+ or
+<programlisting>
+(:person)
+</programlisting>
+ (From now on, we will just use the colon syntax, for simplicity. But it
+ helps to read it as <quote>is</quote> for understanding.) The next
+ example would match a vertex with the label <literal>person</literal>
+ connected to a vertex with the label <literal>account</literal> connected
+ by an edge with the label <literal>has</literal>.
+<programlisting>
+(:person)-[:has]->(:account)
+</programlisting>
+ Multiple labels can also be matched, using <quote>or</quote> semantics:
+<programlisting>
+(:person)-[:has]->(:account|creditcard)
+</programlisting>
+ </para>
+
+ <para>
+ Recall that edges are directed. The other direction is also possible in a
+ path pattern, for example:
+<programlisting>
+(:account)<-[:has]-(:person)
+</programlisting>
+ It is also possible to match both directions:
+<programlisting>
+(:person)-[:is_friend_of]-(:person)
+</programlisting>
+ This has a meaning of <quote>or</quote>: An edge in either direction would
+ match.
+ </para>
+
+ <para>
+ In many cases, the edge patterns don't need a filler. (All the filtering
+ then happens on the vertices.) For these cases, an abbreviated edge
+ pattern syntax is available that omits the brackets, for example:
+<programlisting>
+(:person)->(:account)
+(:acount)<-(:person)
+(:person)-(:person)
+</programlisting>
+ As is often the case, abbreviated syntax can make expressions more compact
+ but also sometimes harder to understand.
+ </para>
+
+ <para>
+ Furthermore, it is possible to define graph pattern variables in the path
+ pattern expressions. These are bound to the matched elements and can be
+ used to refer to the property values from those elements. The most
+ important use is to use them in the <literal>COLUMNS</literal> clause to
+ define the tabular result of the <literal>GRAPH_TABLE</literal> clause.
+ For example (assuming appropriate definitions of the property graph as
+ well as the underlying tables):
+<programlisting>
+GRAPH_TABLE (mygraph MATCH (p:person)-[h:has]->(a:account)
+ COLUMNS (p.name AS person_name, h.since AS has_account_since, a.num AS account_number)
+</programlisting>
+ <literal>WHERE</literal> clauses can be used inside element patterns to
+ filter matches:
+<programlisting>
+(:person)-[:has]->(a:account WHERE a.type = 'savings')
+</programlisting>
+ </para>
+
+ <!-- TODO: multiple path patterns in a graph pattern (comma-separated) -->
+
+ <!-- TODO: quantifiers -->
+
+ </sect2>
+ </sect1>
+
</chapter>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 4a42999b18..990d2b1af3 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -27,6 +27,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
<!ENTITY alterPolicy SYSTEM "alter_policy.sgml">
<!ENTITY alterProcedure SYSTEM "alter_procedure.sgml">
+<!ENTITY alterPropertyGraph SYSTEM "alter_property_graph.sgml">
<!ENTITY alterPublication SYSTEM "alter_publication.sgml">
<!ENTITY alterRole SYSTEM "alter_role.sgml">
<!ENTITY alterRoutine SYSTEM "alter_routine.sgml">
@@ -79,6 +80,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY createOperatorFamily SYSTEM "create_opfamily.sgml">
<!ENTITY createPolicy SYSTEM "create_policy.sgml">
<!ENTITY createProcedure SYSTEM "create_procedure.sgml">
+<!ENTITY createPropertyGraph SYSTEM "create_property_graph.sgml">
<!ENTITY createPublication SYSTEM "create_publication.sgml">
<!ENTITY createRole SYSTEM "create_role.sgml">
<!ENTITY createRule SYSTEM "create_rule.sgml">
@@ -127,6 +129,7 @@ Complete list of usable sgml source files in this directory.
<!ENTITY dropOwned SYSTEM "drop_owned.sgml">
<!ENTITY dropPolicy SYSTEM "drop_policy.sgml">
<!ENTITY dropProcedure SYSTEM "drop_procedure.sgml">
+<!ENTITY dropPropertyGraph SYSTEM "drop_property_graph.sgml">
<!ENTITY dropPublication SYSTEM "drop_publication.sgml">
<!ENTITY dropRole SYSTEM "drop_role.sgml">
<!ENTITY dropRoutine SYSTEM "drop_routine.sgml">
diff --git a/doc/src/sgml/ref/alter_extension.sgml b/doc/src/sgml/ref/alter_extension.sgml
index c819c7bb4e..60218fcd01 100644
--- a/doc/src/sgml/ref/alter_extension.sgml
+++ b/doc/src/sgml/ref/alter_extension.sgml
@@ -46,6 +46,7 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
OPERATOR FAMILY <replaceable class="parameter">object_name</replaceable> USING <replaceable class="parameter">index_method</replaceable> |
[ PROCEDURAL ] LANGUAGE <replaceable class="parameter">object_name</replaceable> |
PROCEDURE <replaceable class="parameter">procedure_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
+ PROPERTY GRAPH <replaceable class="parameter">object_name</replaceable> |
ROUTINE <replaceable class="parameter">routine_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
SCHEMA <replaceable class="parameter">object_name</replaceable> |
SEQUENCE <replaceable class="parameter">object_name</replaceable> |
@@ -179,7 +180,7 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
The name of an object to be added to or removed from the extension.
Names of tables,
aggregates, domains, foreign tables, functions, operators,
- operator classes, operator families, procedures, routines, sequences, text search objects,
+ operator classes, operator families, procedures, property graphs, routines, sequences, text search objects,
types, and views can be schema-qualified.
</para>
</listitem>
diff --git a/doc/src/sgml/ref/alter_property_graph.sgml b/doc/src/sgml/ref/alter_property_graph.sgml
new file mode 100644
index 0000000000..e4e56ad652
--- /dev/null
+++ b/doc/src/sgml/ref/alter_property_graph.sgml
@@ -0,0 +1,150 @@
+<!--
+doc/src/sgml/ref/alter_property_graph.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-alter-property-graph">
+ <indexterm zone="sql-alter-property-graph">
+ <primary>ALTER PROPERTY GRAPH</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>ALTER PROPERTY GRAPH</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>ALTER PROPERTY GRAPH</refname>
+ <refpurpose>change the definition of an SQL-property graph</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> ADD
+ [ {VERTEX|NODE} TABLES ( <replaceable class="parameter">vertex_table_name</replaceable> [, ...] ) ]
+ [ {EDGE|RELATIONSHIP} TABLES ( { <replaceable class="parameter">edge_table_name</replaceable> SOURCE <replaceable class="parameter">source_table_name</replaceable> DESTINATION <replaceable class="parameter">dest_table_name</replaceable> } [, ...] ) ]
+
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> DROP
+ {VERTEX|NODE} TABLES ( <replaceable class="parameter">vertex_table_alias</replaceable> [, ...] ) [ CASCADE | RESTRICT ]
+
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> DROP
+ {EDGE|RELATIONSHIP} TABLES ( <replaceable class="parameter">edge_table_alias</replaceable> [, ...] ) [ CASCADE | RESTRICT ]
+
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> ALTER
+ {VERTEX|NODE|EDGE|RELATIONSHIP} TABLE <replaceable class="parameter">element_table_alias</replaceable>
+ { ADD LABEL <replaceable class="parameter">label_name</replaceable> [ NO PROPERTIES | PROPERTIES ALL COLUMNS | PROPERTIES ( { <replaceable class="parameter">expression</replaceable> [ AS <replaceable class="parameter">property_name</replaceable> ] } [, ...] ) ] } [ ... ]
+
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> ALTER
+ {VERTEX|NODE|EDGE|RELATIONSHIP} TABLE <replaceable class="parameter">element_table_alias</replaceable>
+ DROP LABEL <replaceable class="parameter">label_name</replaceable> [ CASCADE | RESTRICT ]
+
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> ALTER
+ {VERTEX|NODE|EDGE|RELATIONSHIP} TABLE <replaceable class="parameter">element_table_alias</replaceable>
+ ALTER LABEL <replaceable class="parameter">label_name</replaceable> ADD PROPERTIES ( { <replaceable class="parameter">expression</replaceable> [ AS <replaceable class="parameter">property_name</replaceable> ] } [, ...] )
+
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> ALTER
+ {VERTEX|NODE|EDGE|RELATIONSHIP} TABLE <replaceable class="parameter">element_table_alias</replaceable>
+ ALTER LABEL <replaceable class="parameter">label_name</replaceable> DROP PROPERTIES ( <replaceable class="parameter">property_name</replaceable> [, ...] ) [ CASCADE | RESTRICT ]
+
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> OWNER TO { <replaceable class="parameter">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER PROPERTY GRAPH <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
+ALTER PROPERTY GRAPH [ IF EXISTS ] <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ TODO
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of a property graph to be altered.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the property graph does not exist. A notice is
+ issued in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>TODO</term>
+ <listitem>
+ <para>
+ TODO
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_owner</replaceable></term>
+ <listitem>
+ <para>
+ The user name of the new owner of the property graph.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_name</replaceable></term>
+ <listitem>
+ <para>
+ The new name for the property graph.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">new_schema</replaceable></term>
+ <listitem>
+ <para>
+ The new schema for the property graph.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+<programlisting>
+ALTER PROPERTY GRAPH g1 RENAME TO g2;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ TODO
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-create-property-graph"/></member>
+ <member><xref linkend="sql-drop-property-graph"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index 5b43c56b13..7b251476e2 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -47,6 +47,7 @@ COMMENT ON
POLICY <replaceable class="parameter">policy_name</replaceable> ON <replaceable class="parameter">table_name</replaceable> |
[ PROCEDURAL ] LANGUAGE <replaceable class="parameter">object_name</replaceable> |
PROCEDURE <replaceable class="parameter">procedure_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
+ PROPERTY GRAPH <replaceable class="parameter">object_name</replaceable>
PUBLICATION <replaceable class="parameter">object_name</replaceable> |
ROLE <replaceable class="parameter">object_name</replaceable> |
ROUTINE <replaceable class="parameter">routine_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
diff --git a/doc/src/sgml/ref/create_property_graph.sgml b/doc/src/sgml/ref/create_property_graph.sgml
new file mode 100644
index 0000000000..257186a6a9
--- /dev/null
+++ b/doc/src/sgml/ref/create_property_graph.sgml
@@ -0,0 +1,232 @@
+<!--
+doc/src/sgml/ref/create_property_graph.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-create-property-graph">
+ <indexterm zone="sql-create-property-graph">
+ <primary>CREATE PROPERTY GRAPH</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>CREATE PROPERTY GRAPH</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>CREATE PROPERTY GRAPH</refname>
+ <refpurpose>define an SQL-property graph</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE [ TEMP | TEMPORARY ] PROPERTY GRAPH <replaceable class="parameter">name</replaceable>
+ [ {VERTEX|NODE} TABLES ( <replaceable class="parameter">vertex_table_definition</replaceable> [, ...] ) ]
+ [ {EDGE|RELATIONSHIP} TABLES ( <replaceable class="parameter">edge_table_definition</replaceable> [, ...] ) ]
+
+<phrase>where <replaceable class="parameter">vertex_table_definition</replaceable> is:</phrase>
+
+ <replaceable class="parameter">vertex_table_name</replaceable> [ AS <replaceable class="parameter">alias</replaceable> ] [ KEY ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ] [ <replaceable class="parameter">element_table_label_and_properties</replaceable> ]
+
+<phrase>and <replaceable class="parameter">edge_table_definition</replaceable> is:</phrase>
+
+ <replaceable class="parameter">edge_table_name</replaceable> [ AS <replaceable class="parameter">alias</replaceable> ] [ KEY ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
+ SOURCE [ KEY ( <replaceable class="parameter">column_name</replaceable> [, ...] ) REFERENCES ] <replaceable class="parameter">source_table</replaceable> [ ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
+ DESTINATION [ KEY ( <replaceable class="parameter">column_name</replaceable> [, ...] ) REFERENCES ] <replaceable class="parameter">dest_table</replaceable> [ ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ]
+ [ <replaceable class="parameter">element_table_label_and_properties</replaceable> ]
+
+<phrase>and <replaceable class="parameter">element_table_label_and_properties</replaceable> is either:</phrase>
+
+ NO PROPERTIES | PROPERTIES ALL COLUMNS | PROPERTIES ( { <replaceable class="parameter">expression</replaceable> [ AS <replaceable class="parameter">property_name</replaceable> ] } [, ...] )
+
+<phrase>or:</phrase>
+
+ { { LABEL <replaceable class="parameter">label</replaceable> | DEFAULT LABEL } [ NO PROPERTIES | PROPERTIES ALL COLUMNS | PROPERTIES ( { <replaceable class="parameter">expression</replaceable> [ AS <replaceable class="parameter">property_name</replaceable> ] } [, ...] ) ] } [...]
+</synopsis>
+</refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>CREATE PROPERTY GRAPH</command> defines a property graph. A
+ property graph consists of vertices and edges, together called elements,
+ each with associated labels and properties, and can be queried using the
+ <literal>GRAPH_TABLE</literal> clause of <xref linkend="sql-select"/> with
+ a special path matching syntax. The data in the graph is stored in regular
+ tables (or views, foreign tables, etc.). Each vertex or edge corresponds
+ to a table. The property graph definition links these tables together into
+ a graph structure that can be queried using graph query techniques.
+ </para>
+
+ <para>
+ <command>CREATE PROPERTY GRAPH</command> does not physically materialize a
+ graph. It is thus similar to <command>CREATE VIEW</command> in that it
+ records a structure that is used only when the defined object is queried.
+ </para>
+
+ <para>
+ If a schema name is given (for example, <literal>CREATE PROPERTY GRAPH
+ myschema.mygraph ...</literal>) then the property graph is created in the
+ specified schema. Otherwise it is created in the current schema.
+ Temporary property graphs exist in a special schema, so a schema name
+ cannot be given when creating a temporary property graph. Property graphs
+ share a namespace with tables and other relation types, so the name of the
+ property graph must be distinct from the name of any other relation (table,
+ sequence, index, view, materialized view, or foreign table) in the same
+ schema.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the new property graph.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>VERTEX</literal>/<literal>NODE</literal></term>
+ <term><literal>EDGE</literal>/<literal>RELATIONSHIP</literal></term>
+ <listitem>
+ <para>
+ These keywords are synonyms, respectively.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">vertex_table_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of a table that will contain vertices in the new property
+ graph.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">edge_table_name</replaceable></term>
+ <listitem>
+ <para>
+ The name of a table that will contain edges in the new property graph.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">alias</replaceable></term>
+ <listitem>
+ <para>
+ A unique identifier for the vertex or edge table. This defaults to the
+ name of the table. Aliases must be unique in a property graph
+ definition (across all vertex table and edge table definitions).
+ (Therefore, if a table is used more than once as a vertex or edge table,
+ then an explicit alias must be specified for at least one of them to
+ distinguish them.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>KEY ( <replaceable class="parameter">column_name</replaceable> [, ...] )</literal></term>
+ <listitem>
+ <para>
+ A set of columns that uniquely identifies a row in the vertex or edge
+ table. Defaults to the primary key.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">source_table</replaceable></term>
+ <term><replaceable class="parameter">dest_table</replaceable></term>
+ <listitem>
+ <para>
+ The vertex tables that the edge table is linked to. These refer to the
+ aliases of a the vertex table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>KEY ( <replaceable class="parameter">column_name</replaceable> [, ...] ) REFERENCES ... ( <replaceable class="parameter">column_name</replaceable> [, ...] )</literal></term>
+ <listitem>
+ <para>
+ Two sets of columns that connect the edge table and the source or
+ destination vertex table, like in a foreign-key relationship. If a
+ foreign-key constraint between the two tables exists, it is used by
+ default.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">element_table_label_and_properties</replaceable></term>
+ <listitem>
+ <para>
+ Defines the labels and properties for the element (vertex or edge)
+ table. Each element has at least one label. By default, the label is
+ the same as the element table alias. This can be specified explicitly
+ as <literal>DEFAULT LABEL</literal>. Alternatively, one or more freely
+ chosen label names can be specified. (Label names do not have to be
+ unique across a property graph. It is can be useful to assign the same
+ label to different elements.) Each label has a list (possibly empty) of
+ properties. By default, all columns of a table are automatically
+ exposed as properties. This can be specified explicitly as
+ <literal>PROPERTIES ALL COLUMNS</literal>. Alternatively, a list of
+ expressions, which can refer to the columns of the underlying table, can
+ be specified as properties. If the expressions are not a plain column
+ reference, then an explicit property name must also be specified.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Notes</title>
+
+ <para>
+ Property graphs are queried using the <literal>GRAPH_TABLE</literal> clause
+ of <xref linkend="sql-select"/>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+<programlisting>
+CREATE PROPERTY GRAPH g1
+ VERTEX TABLES (v1, v2, v3)
+ EDGE TABLES (e1 SOURCE v1 DESTINATION v2,
+ e2 SOURCE v1 DESTINATION v3);
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>CREATE PROPERTY GRAPH</command> conforms to ISO/IEC 9075-16
+ (SQL/PGQ).
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-alter-property-graph"/></member>
+ <member><xref linkend="sql-drop-property-graph"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/drop_property_graph.sgml b/doc/src/sgml/ref/drop_property_graph.sgml
new file mode 100644
index 0000000000..31cb77a2af
--- /dev/null
+++ b/doc/src/sgml/ref/drop_property_graph.sgml
@@ -0,0 +1,111 @@
+<!--
+doc/src/sgml/ref/drop_property_graph.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-drop-property-graph">
+ <indexterm zone="sql-drop-property-graph">
+ <primary>DROP PROPERTY GRAPH</primary>
+ </indexterm>
+
+ <refmeta>
+ <refentrytitle>DROP PROPERTY GRAPH</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>DROP PROPERTY GRAPH</refname>
+ <refpurpose>remove an SQL-property graph</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP PROPERTY GRAPH [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>DROP PROPERTY GRAPH</command> drops an existing property graph.
+ To execute this command you must be the owner of the property graph.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>IF EXISTS</literal></term>
+ <listitem>
+ <para>
+ Do not throw an error if the property graph does not exist. A notice is
+ issued in this case.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">name</replaceable></term>
+ <listitem>
+ <para>
+ The name (optionally schema-qualified) of the property graph to remove.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>CASCADE</literal></term>
+ <listitem>
+ <para>
+ Automatically drop objects that depend on the property graph, and in
+ turn all objects that depend on those objects (see <xref
+ linkend="ddl-depend"/>).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Refuse to drop the property graph if any objects depend on it. This is
+ the default.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <para>
+<programlisting>
+DROP PROPERTY GRAPH g1;
+</programlisting></para>
+ </refsect1>
+
+ <refsect1>
+ <title>Compatibility</title>
+
+ <para>
+ <command>DROP PROPERTY GRAPH</command> conforms to ISO/IEC 9075-16
+ (SQL/PGQ), except that the standard only allows one property graph to be
+ dropped per command, and apart from the <literal>IF EXISTS</literal>
+ option, which is a <productname>PostgreSQL</productname> extension..
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <simplelist type="inline">
+ <member><xref linkend="sql-create-property-graph"/></member>
+ <member><xref linkend="sql-alter-property-graph"/></member>
+ </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index 9d27b7fcde..35a3a0234e 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -82,6 +82,11 @@ GRANT { { SET | ALTER SYSTEM } [, ... ] | ALL [ PRIVILEGES ] }
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
+GRANT { SELECT | ALL [ PRIVILEGES ] }
+ ON PROPERTY GRAPH <replaceable>graph_name</replaceable> [, ...]
+ TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
+ [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
+
GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
ON SCHEMA <replaceable>schema_name</replaceable> [, ...]
TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
@@ -119,7 +124,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
that grants privileges on a database object (table, column, view,
foreign table, sequence, database, foreign-data wrapper, foreign server,
function, procedure, procedural language, large object, configuration
- parameter, schema, tablespace, or type), and one that grants
+ parameter, property graph, schema, tablespace, or type), and one that grants
membership in a role. These variants are similar in many ways, but
they are different enough to be described separately.
</para>
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index cc7d797159..c613f16b43 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1218,7 +1218,7 @@ INSERT INTO tbl1 VALUES ($1, $2) \bind 'first value' 'second value' \g
<listitem>
<para>
- For each relation (table, view, materialized view, index, sequence,
+ For each relation (table, view, materialized view, index, property graph, sequence,
or foreign table)
or composite type matching the
<replaceable class="parameter">pattern</replaceable>, show all
@@ -1258,9 +1258,9 @@ INSERT INTO tbl1 VALUES ($1, $2) \bind 'first value' 'second value' \g
<para>
If <command>\d</command> is used without a
<replaceable class="parameter">pattern</replaceable> argument, it is
- equivalent to <command>\dtvmsE</command> which will show a list of
- all visible tables, views, materialized views, sequences and
- foreign tables.
+ equivalent to <command>\dtvmsEG</command> which will show a list of
+ all visible tables, views, materialized views, sequences,
+ foreign tables, and property graphs.
This is purely a convenience measure.
</para>
</note>
@@ -1528,6 +1528,7 @@ INSERT INTO tbl1 VALUES ($1, $2) \bind 'first value' 'second value' \g
<varlistentry id="app-psql-meta-command-de">
<term><literal>\dE[S+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
+ <term><literal>\dG[S+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<term><literal>\di[S+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<term><literal>\dm[S+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<term><literal>\ds[S+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
@@ -1536,10 +1537,10 @@ INSERT INTO tbl1 VALUES ($1, $2) \bind 'first value' 'second value' \g
<listitem>
<para>
- In this group of commands, the letters <literal>E</literal>,
+ In this group of commands, the letters <literal>E</literal>, <literal>G</literal>,
<literal>i</literal>, <literal>m</literal>, <literal>s</literal>,
<literal>t</literal>, and <literal>v</literal>
- stand for foreign table, index, materialized view,
+ stand for foreign table, index, property graph, materialized view,
sequence, table, and view,
respectively.
You can specify any or all of
diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml
index 2db66bbf37..8f43a1bbd7 100644
--- a/doc/src/sgml/ref/revoke.sgml
+++ b/doc/src/sgml/ref/revoke.sgml
@@ -104,6 +104,13 @@ REVOKE [ GRANT OPTION FOR ]
[ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
[ CASCADE | RESTRICT ]
+REVOKE [ GRANT OPTION FOR ]
+ { SELECT | ALL [ PRIVILEGES ] }
+ ON PROPERTY GRAPH <replaceable>graph_name</replaceable> [, ...]
+ FROM <replaceable class="parameter">role_specification</replaceable> [, ...]
+ [ GRANTED BY <replaceable class="parameter">role_specification</replaceable> ]
+ [ CASCADE | RESTRICT ]
+
REVOKE [ GRANT OPTION FOR ]
{ { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
ON SCHEMA <replaceable>schema_name</replaceable> [, ...]
diff --git a/doc/src/sgml/ref/security_label.sgml b/doc/src/sgml/ref/security_label.sgml
index e5e5fb483e..9b97085a3f 100644
--- a/doc/src/sgml/ref/security_label.sgml
+++ b/doc/src/sgml/ref/security_label.sgml
@@ -35,6 +35,7 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
MATERIALIZED VIEW <replaceable class="parameter">object_name</replaceable> |
[ PROCEDURAL ] LANGUAGE <replaceable class="parameter">object_name</replaceable> |
PROCEDURE <replaceable class="parameter">procedure_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
+ PROPERTY GRAPH <replaceable class="parameter">object_name</replaceable>
PUBLICATION <replaceable class="parameter">object_name</replaceable> |
ROLE <replaceable class="parameter">object_name</replaceable> |
ROUTINE <replaceable class="parameter">routine_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index 9917df7839..2ab3eecb80 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -59,6 +59,7 @@ SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replac
[ LATERAL ] <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) AS ( <replaceable class="parameter">column_definition</replaceable> [, ...] )
[ LATERAL ] ROWS FROM( <replaceable class="parameter">function_name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> [, ...] ] ) [ AS ( <replaceable class="parameter">column_definition</replaceable> [, ...] ) ] [, ...] )
[ WITH ORDINALITY ] [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ]
+ GRAPH_TABLE ( <replaceable class="parameter">graph_name</replaceable> MATCH <replaceable class="parameter">graph_pattern</replaceable> COLUMNS ( { <replaceable class="parameter">expression</replaceable> [ AS <replaceable class="parameter">name</replaceable> ] } [, ...] ) ) [ [ AS ] <replaceable class="parameter">alias</replaceable> [ ( <replaceable class="parameter">column_alias</replaceable> [, ...] ) ] ]
<replaceable class="parameter">from_item</replaceable> <replaceable class="parameter">join_type</replaceable> <replaceable class="parameter">from_item</replaceable> { ON <replaceable class="parameter">join_condition</replaceable> | USING ( <replaceable class="parameter">join_column</replaceable> [, ...] ) [ AS <replaceable class="parameter">join_using_alias</replaceable> ] }
<replaceable class="parameter">from_item</replaceable> NATURAL <replaceable class="parameter">join_type</replaceable> <replaceable class="parameter">from_item</replaceable>
<replaceable class="parameter">from_item</replaceable> CROSS JOIN <replaceable class="parameter">from_item</replaceable>
@@ -587,6 +588,48 @@ TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>GRAPH_TABLE ( <replaceable class="parameter">graph_name</replaceable> MATCH <replaceable class="parameter">graph_pattern</replaceable> COLUMNS ( { <replaceable class="parameter">expression</replaceable> [ AS <replaceable class="parameter">name</replaceable> ] } [, ...] ) )</literal></term>
+ <listitem>
+ <para>
+ This clause produces output from matching the specifying graph pattern
+ against a property graph. See <xref linkend="ddl-property-graphs"/>
+ and <xref linkend="queries-graph"/> for more information.
+ </para>
+
+ <para>
+ <replaceable class="parameter">graph_name</replaceable> is the name
+ (optionally schema-qualified) of an existing property graph (defined
+ with <xref linkend="sql-create-property-graph"/>).
+ </para>
+
+ <para>
+ <replaceable class="parameter">graph_pattern</replaceable> is a graph
+ pattern in a special graph pattern sublanguage. See <xref
+ linkend="queries-graph-patterns"/>.
+ </para>
+
+ <para>
+ The <literal>COLUMNS</literal> clause defines the output columns of
+ the <literal>GRAPH_TABLE</literal> clause. <replaceable
+ class="parameter">expression</replaceable> is a scalar expression
+ using the graph pattern variables defined in the <replaceable
+ class="parameter">graph_pattern</replaceable>. The name of the output
+ columns are specified using the <literal>AS</literal> clauses. If the
+ expressions are simple property references, the property names are
+ used as the output names, otherwise an explicit name must be
+ specified.
+ </para>
+
+ <para>
+ Like for other <literal>FROM</literal> clause items, a table alias
+ name and column alias names may follow the <literal>GRAPH_TABLE
+ (...)</literal> clause. (A column alias list would be redundant with
+ the <literal>COLUMNS</literal> clause.)
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><replaceable class="parameter">join_type</replaceable></term>
<listitem>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index aa94f6adf6..d3fb783608 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -55,6 +55,7 @@
&alterOperatorFamily;
&alterPolicy;
&alterProcedure;
+ &alterPropertyGraph;
&alterPublication;
&alterRole;
&alterRoutine;
@@ -107,6 +108,7 @@
&createOperatorFamily;
&createPolicy;
&createProcedure;
+ &createPropertyGraph;
&createPublication;
&createRole;
&createRule;
@@ -155,6 +157,7 @@
&dropOwned;
&dropPolicy;
&dropProcedure;
+ &dropPropertyGraph;
&dropPublication;
&dropRole;
&dropRoutine;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 196ecafc90..e7fc979fc2 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -118,7 +118,10 @@ CATALOG_HEADERS := \
pg_publication_namespace.h \
pg_publication_rel.h \
pg_subscription.h \
- pg_subscription_rel.h
+ pg_subscription_rel.h \
+ pg_propgraph_element.h \
+ pg_propgraph_label.h \
+ pg_propgraph_property.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h syscache_ids.h syscache_info.h system_fk_info.h
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 023938682d..7c3360c5a8 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -290,6 +290,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case OBJECT_PARAMETER_ACL:
whole_mask = ACL_ALL_RIGHTS_PARAMETER_ACL;
break;
+ case OBJECT_PROPGRAPH:
+ whole_mask = ACL_ALL_RIGHTS_PROPGRAPH;
+ break;
default:
elog(ERROR, "unrecognized object type: %d", objtype);
/* not reached, but keep compiler quiet */
@@ -534,6 +537,10 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_PARAMETER_ACL;
errormsg = gettext_noop("invalid privilege type %s for parameter");
break;
+ case OBJECT_PROPGRAPH:
+ all_privileges = ACL_ALL_RIGHTS_PROPGRAPH;
+ errormsg = gettext_noop("invalid privilege type %s for property graph");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -604,6 +611,7 @@ ExecGrantStmt_oids(InternalGrant *istmt)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_PROPGRAPH:
ExecGrant_Relation(istmt);
break;
case OBJECT_DATABASE:
@@ -676,6 +684,7 @@ objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
{
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_PROPGRAPH:
foreach(cell, objnames)
{
RangeVar *relvar = (RangeVar *) lfirst(cell);
@@ -876,6 +885,10 @@ objectsInSchemaToOids(ObjectType objtype, List *nspnames)
objs = getRelationsInNamespace(namespaceId, RELKIND_SEQUENCE);
objects = list_concat(objects, objs);
break;
+ case OBJECT_PROPGRAPH:
+ objs = getRelationsInNamespace(namespaceId, RELKIND_PROPGRAPH);
+ objects = list_concat(objects, objs);
+ break;
case OBJECT_FUNCTION:
case OBJECT_PROCEDURE:
case OBJECT_ROUTINE:
@@ -1077,6 +1090,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_SCHEMA;
errormsg = gettext_noop("invalid privilege type %s for schema");
break;
+ case OBJECT_PROPGRAPH:
+ all_privileges = ACL_ALL_RIGHTS_PROPGRAPH;
+ errormsg = gettext_noop("invalid privilege type %s for property graph");
+ break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) action->objtype);
@@ -2761,6 +2778,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_PROCEDURE:
msg = gettext_noop("permission denied for procedure %s");
break;
+ case OBJECT_PROPGRAPH:
+ msg = gettext_noop("permission denied for property graph %s");
+ break;
case OBJECT_PUBLICATION:
msg = gettext_noop("permission denied for publication %s");
break;
@@ -2887,6 +2907,9 @@ aclcheck_error(AclResult aclerr, ObjectType objtype,
case OBJECT_PROCEDURE:
msg = gettext_noop("must be owner of procedure %s");
break;
+ case OBJECT_PROPGRAPH:
+ msg = gettext_noop("must be owner of property graph %s");
+ break;
case OBJECT_PUBLICATION:
msg = gettext_noop("must be owner of publication %s");
break;
@@ -3023,6 +3046,7 @@ pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum, Oid roleid,
pg_attribute_aclmask(object_oid, attnum, roleid, mask, how);
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
+ case OBJECT_PROPGRAPH:
return pg_class_aclmask(object_oid, roleid, mask, how);
case OBJECT_DATABASE:
return object_aclmask(DatabaseRelationId, object_oid, roleid, mask, how);
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index eadcf6af0d..30c6f77044 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -50,6 +50,9 @@
#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_propgraph_element.h"
+#include "catalog/pg_propgraph_label.h"
+#include "catalog/pg_propgraph_property.h"
#include "catalog/pg_publication.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_publication_rel.h"
@@ -70,6 +73,7 @@
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/policy.h"
+#include "commands/propgraphcmds.h"
#include "commands/publicationcmds.h"
#include "commands/seclabel.h"
#include "commands/sequence.h"
@@ -1451,6 +1455,9 @@ doDeletion(const ObjectAddress *object, int flags)
case OCLASS_AM:
case OCLASS_AMOP:
case OCLASS_AMPROC:
+ case OCLASS_PROPGRAPH_ELEMENT:
+ case OCLASS_PROPGRAPH_LABEL:
+ case OCLASS_PROPGRAPH_PROPERTY:
case OCLASS_SCHEMA:
case OCLASS_TSPARSER:
case OCLASS_TSDICT:
@@ -2163,6 +2170,7 @@ find_expr_references_walker(Node *node,
switch (rte->rtekind)
{
case RTE_RELATION:
+ case RTE_GRAPH_TABLE:
add_object_address(RelationRelationId, rte->relid, 0,
context->addrs);
break;
@@ -2900,6 +2908,15 @@ getObjectClass(const ObjectAddress *object)
case PolicyRelationId:
return OCLASS_POLICY;
+ case PropgraphElementRelationId:
+ return OCLASS_PROPGRAPH_ELEMENT;
+
+ case PropgraphLabelRelationId:
+ return OCLASS_PROPGRAPH_LABEL;
+
+ case PropgraphPropertyRelationId:
+ return OCLASS_PROPGRAPH_PROPERTY;
+
case PublicationNamespaceRelationId:
return OCLASS_PUBLICATION_NAMESPACE;
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index c402ae7274..aab28fd708 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -3009,3 +3009,366 @@ CREATE VIEW user_mappings AS
FROM _pg_user_mappings;
GRANT SELECT ON user_mappings TO PUBLIC;
+
+
+-- SQL/PGQ views; these use section numbers from part 16 of the standard.
+
+/*
+ * 15.2
+ * PG_DEFINED_LABEL_SETS view
+ */
+
+-- TODO
+
+
+/*
+ * 15.3
+ * PG_DEFINED_LABEL_SET_LABELS view
+ */
+
+-- TODO
+
+
+/*
+ * 15.4
+ * PG_EDGE_DEFINED_LABEL_SETS view
+ */
+
+-- TODO
+
+
+/*
+ * 15.5
+ * PG_EDGE_TABLE_COMPONENTS view
+ */
+
+CREATE VIEW pg_edge_table_components AS
+ SELECT CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(npg.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(pg.relname AS sql_identifier) AS property_graph_name,
+ CAST(eg.pgealias AS sql_identifier) AS edge_table_alias,
+ CAST(v.pgealias AS sql_identifier) AS vertex_table_alias,
+ CAST(CASE eg.end WHEN 'src' THEN 'SOURCE' WHEN 'dest' THEN 'DESTINATION' END AS character_data) AS edge_end,
+ CAST(ae.attname AS sql_identifier) AS edge_table_column_name,
+ CAST(av.attname AS sql_identifier) AS vertex_table_column_name,
+ CAST((eg.egkey).n AS cardinal_number) AS ordinal_position
+ FROM pg_namespace npg
+ JOIN
+ (SELECT * FROM pg_class WHERE relkind = 'g') AS pg
+ ON npg.oid = pg.relnamespace
+ JOIN
+ (SELECT pgepgid, pgealias, pgerelid, 'src' AS end, pgesrcvertexid AS vertexid, _pg_expandarray(pgesrckey) AS egkey, _pg_expandarray(pgesrcref) AS egref FROM pg_propgraph_element WHERE pgekind = 'e'
+ UNION ALL
+ SELECT pgepgid, pgealias, pgerelid, 'dest' AS end, pgedestvertexid AS vertexid, _pg_expandarray(pgedestkey) AS egkey, _pg_expandarray(pgedestref) AS egref FROM pg_propgraph_element WHERE pgekind = 'e'
+ ) AS eg
+ ON pg.oid = eg.pgepgid
+ JOIN
+ (SELECT * FROM pg_propgraph_element WHERE pgekind = 'v') AS v
+ ON eg.vertexid = v.oid
+ JOIN
+ (SELECT * FROM pg_attribute WHERE NOT attisdropped) AS ae
+ ON eg.pgerelid = ae.attrelid AND (eg.egkey).x = ae.attnum
+ JOIN
+ (SELECT * FROM pg_attribute WHERE NOT attisdropped) AS av
+ ON v.pgerelid = av.attrelid AND (eg.egref).x = av.attnum
+ WHERE NOT pg_is_other_temp_schema(npg.oid)
+ AND (pg_has_role(pg.relowner, 'USAGE')
+ OR has_table_privilege(pg.oid, 'SELECT'));
+
+GRANT SELECT ON pg_edge_table_components TO PUBLIC;
+
+
+/*
+ * 15.6
+ * PG_EDGE_TRIPLETS view
+ */
+
+-- TODO
+
+
+/*
+ * 15.7
+ * PG_ELEMENT_TABLE_KEY_COLUMNS view
+ */
+
+CREATE VIEW pg_element_table_key_columns AS
+ SELECT CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(npg.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(pg.relname AS sql_identifier) AS property_graph_name,
+ CAST(pgealias AS sql_identifier) AS element_table_alias,
+ CAST(a.attname AS sql_identifier) AS column_name,
+ CAST((el.ekey).n AS cardinal_number) AS ordinal_position
+ FROM pg_namespace npg
+ JOIN
+ (SELECT * FROM pg_class WHERE relkind = 'g') AS pg
+ ON npg.oid = pg.relnamespace
+ JOIN
+ (SELECT pgepgid, pgealias, pgerelid, _pg_expandarray(pgekey) AS ekey FROM pg_propgraph_element) AS el
+ ON pg.oid = el.pgepgid
+ JOIN
+ (SELECT * FROM pg_attribute WHERE NOT attisdropped) AS a
+ ON el.pgerelid = a.attrelid AND (el.ekey).x = a.attnum
+ WHERE NOT pg_is_other_temp_schema(npg.oid)
+ AND (pg_has_role(pg.relowner, 'USAGE')
+ OR has_table_privilege(pg.oid, 'SELECT'));
+
+GRANT SELECT ON pg_element_table_key_columns TO PUBLIC;
+
+
+/*
+ * 15.8
+ * PG_ELEMENT_TABLE_LABELS view
+ */
+
+CREATE VIEW pg_element_table_labels AS
+ SELECT CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(npg.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(pg.relname AS sql_identifier) AS property_graph_name,
+ CAST(e.pgealias AS sql_identifier) AS element_table_alias,
+ CAST(l.pgllabel AS sql_identifier) AS label_name
+ FROM pg_namespace npg, pg_class pg, pg_propgraph_element e, pg_propgraph_label l
+ WHERE pg.relnamespace = npg.oid
+ AND e.pgepgid = pg.oid
+ AND l.pglelid = e.oid
+ AND pg.relkind = 'g'
+ AND (NOT pg_is_other_temp_schema(npg.oid))
+ AND (pg_has_role(pg.relowner, 'USAGE')
+ OR has_table_privilege(pg.oid, 'SELECT'));
+
+GRANT SELECT ON pg_element_table_labels TO PUBLIC;
+
+
+/*
+ * 15.9
+ * PG_ELEMENT_TABLE_PROPERTIES view
+ */
+
+CREATE VIEW pg_element_table_properties AS
+ SELECT DISTINCT
+ CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(npg.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(pg.relname AS sql_identifier) AS property_graph_name,
+ CAST(e.pgealias AS sql_identifier) AS element_table_alias,
+ CAST(pr.pgpname AS sql_identifier) AS property_name,
+ CAST(pg_get_expr(pr.pgpexpr, e.pgerelid) AS character_data) AS property_expression
+ FROM pg_namespace npg, pg_class pg, pg_propgraph_element e, pg_propgraph_label l, pg_propgraph_property pr
+ WHERE pg.relnamespace = npg.oid
+ AND e.pgepgid = pg.oid
+ AND l.pglelid = e.oid
+ AND pr.pgplabelid = l.oid
+ AND pg.relkind = 'g'
+ AND (NOT pg_is_other_temp_schema(npg.oid))
+ AND (pg_has_role(pg.relowner, 'USAGE')
+ OR has_table_privilege(pg.oid, 'SELECT'));
+
+GRANT SELECT ON pg_element_table_properties TO PUBLIC;
+
+
+/*
+ * 15.10
+ * PG_ELEMENT_TABLES view
+ */
+
+CREATE VIEW pg_element_tables AS
+ SELECT CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(npg.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(pg.relname AS sql_identifier) AS property_graph_name,
+ CAST(e.pgealias AS sql_identifier) AS element_table_alias,
+ CAST(CASE e.pgekind WHEN 'e' THEN 'EDGE' WHEN 'v' THEN 'VERTEX' END AS character_data) AS element_table_kind,
+ CAST(current_database() AS sql_identifier) AS table_catalog,
+ CAST(nt.nspname AS sql_identifier) AS table_schema,
+ CAST(t.relname AS sql_identifier) AS table_name,
+ CAST(NULL AS character_data) AS element_table_definition
+ FROM pg_namespace npg, pg_class pg, pg_propgraph_element e, pg_class t, pg_namespace nt
+ WHERE pg.relnamespace = npg.oid
+ AND e.pgepgid = pg.oid
+ AND e.pgerelid = t.oid
+ AND t.relnamespace = nt.oid
+ AND pg.relkind = 'g'
+ AND (NOT pg_is_other_temp_schema(npg.oid))
+ AND (pg_has_role(pg.relowner, 'USAGE')
+ OR has_table_privilege(pg.oid, 'SELECT'));
+
+GRANT SELECT ON pg_element_tables TO PUBLIC;
+
+
+/*
+ * 15.11
+ * PG_LABEL_PROPERTIES view
+ */
+
+CREATE VIEW pg_label_properties AS
+ SELECT DISTINCT
+ CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(npg.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(pg.relname AS sql_identifier) AS property_graph_name,
+ CAST(l.pgllabel AS sql_identifier) AS label_name,
+ CAST(pr.pgpname AS sql_identifier) AS property_name
+ FROM pg_namespace npg, pg_class pg, pg_propgraph_element e, pg_propgraph_label l, pg_propgraph_property pr
+ WHERE pg.relnamespace = npg.oid
+ AND e.pgepgid = pg.oid
+ AND l.pglelid = e.oid
+ AND pr.pgplabelid = l.oid
+ AND pg.relkind = 'g'
+ AND (NOT pg_is_other_temp_schema(npg.oid))
+ AND (pg_has_role(pg.relowner, 'USAGE')
+ OR has_table_privilege(pg.oid, 'SELECT'));
+
+GRANT SELECT ON pg_label_properties TO PUBLIC;
+
+
+/*
+ * 15.12
+ * PG_LABELS view
+ */
+
+CREATE VIEW pg_labels AS
+ SELECT DISTINCT
+ CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(npg.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(pg.relname AS sql_identifier) AS property_graph_name,
+ CAST(l.pgllabel AS sql_identifier) AS label_name
+ FROM pg_namespace npg, pg_class pg, pg_propgraph_element e, pg_propgraph_label l
+ WHERE pg.relnamespace = npg.oid
+ AND e.pgepgid = pg.oid
+ AND l.pglelid = e.oid
+ AND pg.relkind = 'g'
+ AND (NOT pg_is_other_temp_schema(npg.oid))
+ AND (pg_has_role(pg.relowner, 'USAGE')
+ OR has_table_privilege(pg.oid, 'SELECT'));
+
+GRANT SELECT ON pg_labels TO PUBLIC;
+
+
+/*
+ * 15.13
+ * PG_PROPERTY_DATA_TYPES view
+ */
+
+CREATE VIEW pg_property_data_types AS
+ SELECT DISTINCT
+ CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(npg.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(pg.relname AS sql_identifier) AS property_graph_name,
+ CAST(pgp.pgpname AS sql_identifier) AS property_name,
+
+ CAST(
+ CASE WHEN t.typtype = 'd' THEN
+ CASE WHEN bt.typelem <> 0 AND bt.typlen = -1 THEN 'ARRAY'
+ WHEN nbt.nspname = 'pg_catalog' THEN format_type(t.typbasetype, null)
+ ELSE 'USER-DEFINED' END
+ ELSE
+ CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ARRAY'
+ WHEN nt.nspname = 'pg_catalog' THEN format_type(pgp.pgptypid, null)
+ ELSE 'USER-DEFINED' END
+ END
+ AS character_data)
+ AS data_type,
+
+ CAST(null AS cardinal_number) AS character_maximum_length,
+ CAST(null AS cardinal_number) AS character_octet_length,
+ CAST(null AS sql_identifier) AS character_set_catalog,
+ CAST(null AS sql_identifier) AS character_set_schema,
+ CAST(null AS sql_identifier) AS character_set_name,
+ CAST(null AS sql_identifier) AS collation_catalog, -- FIXME
+ CAST(null AS sql_identifier) AS collation_schema, -- FIXME
+ CAST(null AS sql_identifier) AS collation_name, -- FIXME
+ CAST(null AS cardinal_number) AS numeric_precision,
+ CAST(null AS cardinal_number) AS numeric_precision_radix,
+ CAST(null AS cardinal_number) AS numeric_scale,
+ CAST(null AS cardinal_number) AS datetime_precision,
+ CAST(null AS character_data) AS interval_type,
+ CAST(null AS cardinal_number) AS interval_precision,
+
+ CAST(current_database() AS sql_identifier) AS user_defined_type_catalog,
+ CAST(coalesce(nbt.nspname, nt.nspname) AS sql_identifier) AS user_defined_type_schema,
+ CAST(coalesce(bt.typname, t.typname) AS sql_identifier) AS user_defined_type_name,
+
+ CAST(null AS sql_identifier) AS scope_catalog,
+ CAST(null AS sql_identifier) AS scope_schema,
+ CAST(null AS sql_identifier) AS scope_name,
+
+ CAST(null AS cardinal_number) AS maximum_cardinality,
+ CAST(pgp.pgpname AS sql_identifier) AS dtd_identifier
+
+ FROM pg_propgraph_property pgp
+ JOIN (pg_class pg JOIN pg_namespace npg ON (pg.relnamespace = npg.oid)) ON pgp.pgppgid = pg.oid
+ JOIN (pg_type t JOIN pg_namespace nt ON (t.typnamespace = nt.oid)) ON pgp.pgptypid = t.oid
+ LEFT JOIN (pg_type bt JOIN pg_namespace nbt ON (bt.typnamespace = nbt.oid))
+ ON (t.typtype = 'd' AND t.typbasetype = bt.oid)
+
+ WHERE pg.relkind = 'g'
+ AND (NOT pg_is_other_temp_schema(npg.oid))
+ AND (pg_has_role(pg.relowner, 'USAGE')
+ OR has_table_privilege(pg.oid, 'SELECT'));
+
+GRANT SELECT ON pg_property_data_types TO PUBLIC;
+
+
+/*
+ * 15.14
+ * PG_PROPERTY_GRAPH_PRIVILEGES view
+ */
+
+CREATE VIEW pg_property_graph_privileges AS
+ SELECT CAST(u_grantor.rolname AS sql_identifier) AS grantor,
+ CAST(grantee.rolname AS sql_identifier) AS grantee,
+ CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(nc.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(c.relname AS sql_identifier) AS property_graph_name,
+ CAST(c.prtype AS character_data) AS privilege_type,
+ CAST(
+ CASE WHEN
+ -- object owner always has grant options
+ pg_has_role(grantee.oid, c.relowner, 'USAGE')
+ OR c.grantable
+ THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_grantable
+
+ FROM (
+ SELECT oid, relname, relnamespace, relkind, relowner, (aclexplode(coalesce(relacl, acldefault('r', relowner)))).* FROM pg_class
+ ) AS c (oid, relname, relnamespace, relkind, relowner, grantor, grantee, prtype, grantable),
+ pg_namespace nc,
+ pg_authid u_grantor,
+ (
+ SELECT oid, rolname FROM pg_authid
+ UNION ALL
+ SELECT 0::oid, 'PUBLIC'
+ ) AS grantee (oid, rolname)
+
+ WHERE c.relnamespace = nc.oid
+ AND c.relkind IN ('g')
+ AND c.grantee = grantee.oid
+ AND c.grantor = u_grantor.oid
+ AND c.prtype IN ('SELECT')
+ AND (pg_has_role(u_grantor.oid, 'USAGE')
+ OR pg_has_role(grantee.oid, 'USAGE')
+ OR grantee.rolname = 'PUBLIC');
+
+GRANT SELECT ON pg_property_graph_privileges TO PUBLIC;
+
+
+/*
+ * 15.15
+ * PG_VERTEX_DEFINED_LABEL_SETS view
+ */
+
+-- TODO
+
+
+/*
+ * 15.16
+ * PROPERTY_GRAPHS view
+ */
+
+CREATE VIEW property_graphs AS
+ SELECT CAST(current_database() AS sql_identifier) AS property_graph_catalog,
+ CAST(nc.nspname AS sql_identifier) AS property_graph_schema,
+ CAST(c.relname AS sql_identifier) AS property_graph_name
+ FROM pg_namespace nc, pg_class c
+ WHERE c.relnamespace = nc.oid
+ AND c.relkind = 'g'
+ AND (NOT pg_is_other_temp_schema(nc.oid))
+ AND (pg_has_role(c.relowner, 'USAGE')
+ OR has_table_privilege(c.oid, 'SELECT'));
+
+GRANT SELECT ON property_graphs TO PUBLIC;
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 360c6b2ba6..bf0ae45678 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -47,6 +47,9 @@
#include "catalog/pg_parameter_acl.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_propgraph_element.h"
+#include "catalog/pg_propgraph_label.h"
+#include "catalog/pg_propgraph_property.h"
#include "catalog/pg_publication.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_publication_rel.h"
@@ -370,6 +373,48 @@ static const ObjectPropertyType ObjectProperty[] =
OBJECT_OPFAMILY,
true
},
+ {
+ "property graph element",
+ PropgraphElementRelationId,
+ PropgraphElementObjectIndexId,
+ PROPGRAPHELOID,
+ PROPGRAPHELALIAS,
+ Anum_pg_propgraph_element_oid,
+ Anum_pg_propgraph_element_pgealias,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
+ {
+ "property graph label",
+ PropgraphLabelRelationId,
+ PropgraphLabelObjectIndexId,
+ -1,
+ PROPGRAPHLABELNAME,
+ Anum_pg_propgraph_label_oid,
+ Anum_pg_propgraph_label_pgllabel,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
+ {
+ "property graph property",
+ PropgraphPropertyRelationId,
+ PropgraphPropertyObjectIndexId,
+ -1,
+ PROPGRAPHPROPNAME,
+ Anum_pg_propgraph_property_oid,
+ Anum_pg_propgraph_property_pgpname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
{
"role",
AuthIdRelationId,
@@ -680,6 +725,9 @@ static const struct object_type_map
{
"foreign table", OBJECT_FOREIGN_TABLE
},
+ {
+ "property graph", OBJECT_PROPGRAPH
+ },
{
"table column", OBJECT_COLUMN
},
@@ -849,6 +897,18 @@ static const struct object_type_map
{
"policy", OBJECT_POLICY
},
+ /* OCLASS_PROPGRAPH_ELEMENT */
+ {
+ "property graph element", -1
+ },
+ /* OCLASS_PROPGRAPH_LABEL */
+ {
+ "property graph label", -1
+ },
+ /* OCLASS_PROPGRAPH_PROPERTY */
+ {
+ "property graph property", -1
+ },
/* OCLASS_PUBLICATION */
{
"publication", OBJECT_PUBLICATION
@@ -989,6 +1049,7 @@ get_object_address(ObjectType objtype, Node *object,
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
+ case OBJECT_PROPGRAPH:
address =
get_relation_by_qualified_name(objtype, castNode(List, object),
&relation, lockmode,
@@ -1397,6 +1458,13 @@ get_relation_by_qualified_name(ObjectType objtype, List *object,
errmsg("\"%s\" is not an index",
RelationGetRelationName(relation))));
break;
+ case OBJECT_PROPGRAPH:
+ if (relation->rd_rel->relkind != RELKIND_PROPGRAPH)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a property graph",
+ RelationGetRelationName(relation))));
+ break;
case OBJECT_SEQUENCE:
if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
ereport(ERROR,
@@ -2312,6 +2380,7 @@ pg_get_object_address(PG_FUNCTION_ARGS)
case OBJECT_MATVIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
+ case OBJECT_PROPGRAPH:
case OBJECT_COLUMN:
case OBJECT_ATTRIBUTE:
case OBJECT_COLLATION:
@@ -2431,6 +2500,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
+ case OBJECT_PROPGRAPH:
case OBJECT_COLUMN:
case OBJECT_RULE:
case OBJECT_TRIGGER:
@@ -3961,6 +4031,43 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
+ case OCLASS_PROPGRAPH_ELEMENT:
+ {
+ HeapTuple tup;
+ Form_pg_propgraph_element pgeform;
+ StringInfoData rel;
+
+ tup = SearchSysCache1(PROPGRAPHELOID,
+ ObjectIdGetDatum(object->objectId));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for property graph element %u",
+ object->objectId);
+
+ pgeform = (Form_pg_propgraph_element) GETSTRUCT(tup);
+
+ initStringInfo(&rel);
+ getRelationDescription(&rel, pgeform->pgepgid, false);
+
+ /* translator: second %s is, e.g., "property graph %s" */
+ appendStringInfo(&buffer, _("element %s in %s"),
+ NameStr(pgeform->pgealias), rel.data);
+ pfree(rel.data);
+ ReleaseSysCache(tup);
+ break;
+ }
+
+ case OCLASS_PROPGRAPH_LABEL:
+ {
+ appendStringInfo(&buffer, "label %u TODO", object->objectId);
+ break;
+ }
+
+ case OCLASS_PROPGRAPH_PROPERTY:
+ {
+ appendStringInfo(&buffer, "property %u TODO", object->objectId);
+ break;
+ }
+
case OCLASS_PUBLICATION:
{
char *pubname = get_publication_name(object->objectId,
@@ -4148,6 +4255,10 @@ getRelationDescription(StringInfo buffer, Oid relid, bool missing_ok)
appendStringInfo(buffer, _("foreign table %s"),
relname);
break;
+ case RELKIND_PROPGRAPH:
+ appendStringInfo(buffer, _("property graph %s"),
+ relname);
+ break;
default:
/* shouldn't get here */
appendStringInfo(buffer, _("relation %s"),
@@ -4568,6 +4679,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
+ case OCLASS_PROPGRAPH_ELEMENT:
+ appendStringInfoString(&buffer, "property graph element");
+ break;
+
+ case OCLASS_PROPGRAPH_LABEL:
+ appendStringInfoString(&buffer, "property graph label");
+ break;
+
+ case OCLASS_PROPGRAPH_PROPERTY:
+ appendStringInfoString(&buffer, "property graph property");
+ break;
+
case OCLASS_PUBLICATION:
appendStringInfoString(&buffer, "publication");
break;
@@ -4651,6 +4774,9 @@ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId,
case RELKIND_FOREIGN_TABLE:
appendStringInfoString(buffer, "foreign table");
break;
+ case RELKIND_PROPGRAPH:
+ appendStringInfoString(buffer, "property graph");
+ break;
default:
/* shouldn't get here */
appendStringInfoString(buffer, "relation");
@@ -5810,6 +5936,18 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
+ case OCLASS_PROPGRAPH_ELEMENT:
+ appendStringInfo(&buffer, "%u TODO", object->objectId);
+ break;
+
+ case OCLASS_PROPGRAPH_LABEL:
+ appendStringInfo(&buffer, "%u TODO", object->objectId);
+ break;
+
+ case OCLASS_PROPGRAPH_PROPERTY:
+ appendStringInfo(&buffer, "%u TODO", object->objectId);
+ break;
+
case OCLASS_PUBLICATION:
{
char *pubname;
@@ -6118,6 +6256,8 @@ get_relkind_objtype(char relkind)
return OBJECT_MATVIEW;
case RELKIND_FOREIGN_TABLE:
return OBJECT_FOREIGN_TABLE;
+ case RELKIND_PROPGRAPH:
+ return OBJECT_PROPGRAPH;
case RELKIND_TOASTVALUE:
return OBJECT_TABLE;
default:
diff --git a/src/backend/catalog/pg_class.c b/src/backend/catalog/pg_class.c
index e05b0bbb2e..748f06b3ff 100644
--- a/src/backend/catalog/pg_class.c
+++ b/src/backend/catalog/pg_class.c
@@ -45,6 +45,8 @@ errdetail_relkind_not_supported(char relkind)
return errdetail("This operation is not supported for partitioned tables.");
case RELKIND_PARTITIONED_INDEX:
return errdetail("This operation is not supported for partitioned indexes.");
+ case RELKIND_PROPGRAPH:
+ return errdetail("This operation is not supported for property graphs.");
default:
elog(ERROR, "unrecognized relkind: '%c'", relkind);
return 0;
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 925d15a2c3..1346c42ec9 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -348,6 +348,106 @@ F866 FETCH FIRST clause: PERCENT option NO
F867 FETCH FIRST clause: WITH TIES option YES
F868 ORDER BY in grouped table YES
F869 SQL implementation info population YES
+G000 Graph pattern YES SQL/PGQ required
+G001 Repeatable-elements match mode YES SQL/PGQ required XXX
+G002 Different-edges match mode NO
+G003 Explicit REPEATABLE ELEMENTS keyword NO
+G004 Path variables NO
+G005 Path search prefix in a path pattern NO
+G006 Graph pattern KEEP clause: path mode prefix NO
+G007 Graph pattern KEEP clause: path search prefix NO
+G008 Graph pattern WHERE clause YES SQL/PGQ required
+G010 Explicit WALK keyword NO
+G011 Advanced path modes: TRAIL NO
+G012 Advanced path modes: SIMPLE NO
+G013 Advanced path modes: ACYCLIC NO
+G014 Explicit PATH/PATHS keywords NO
+G015 All path search: explicit ALL keyword NO
+G016 Any path search NO
+G017 All shortest path search NO
+G018 Any shortest path search NO
+G019 Counted shortest path search NO
+G020 Counted shortest group search NO
+G030 Path multiset alternation NO
+G031 Path multiset alternation: variable length path operands NO
+G032 Path pattern union NO
+G033 Path pattern union: variable length path operands NO
+G034 Path concatenation YES SQL/PGQ required
+G035 Quantified paths NO
+G036 Quantified edges NO
+G037 Questioned paths NO
+G038 Parenthesized path pattern expression NO
+G039 Simplified path pattern expression: full defaulting NO
+G040 Vertex pattern YES SQL/PGQ required
+G041 Non-local element pattern predicates NO
+G042 Basic full edge patterns YES SQL/PGQ required
+G043 Complete full edge patterns NO
+G044 Basic abbreviated edge patterns YES
+G045 Complete abbreviated edge patterns NO
+G046 Relaxed topological consistency: adjacent vertex patterns NO
+G047 Relaxed topological consistency: concise edge patterns NO
+G048 Parenthesized path pattern: subpath variable declaration NO
+G049 Parenthesized path pattern: path mode prefix NO
+G050 Parenthesized path pattern: WHERE clause NO
+G051 Parenthesized path pattern: non-local predicates NO
+G060 Bounded graph pattern quantifiers NO
+G061 Unbounded graph pattern quantifiers NO
+G070 Label expression: label disjunction NO SQL/PGQ required
+G071 Label expression: label conjunction NO
+G072 Label expression: label negation NO
+G073 Label expression: individual label name YES SQL/PGQ required
+G074 Label expression: wildcard label NO
+G075 Parenthesized label expression NO
+G080 Simplified path pattern expression: basic defaulting NO
+G081 Simplified path pattern expression: full overrides NO
+G082 Simplified path pattern expression: basic overrides NO
+G090 Property reference YES SQL/PGQ required
+G100 ELEMENT_ID function NO
+G110 IS DIRECTED predicate NO
+G111 IS LABELED predicate NO
+G112 IS SOURCE and IS DESTINATION predicate NO
+G113 ALL_DIFFERENT predicate NO
+G114 SAME predicate NO
+G115 PROPERTY_EXISTS predicate NO
+G120 Within-match aggregates NO
+G800 PATH_NAME function NO
+G801 ELEMENT_NUMBER function NO
+G802 PATH_LENGTH function NO
+G803 MATCHNUM function NO
+G810 IS BOUND predicate NO
+G811 IS BOUND predicate: AS option NO
+G820 BINDING_COUNT NO
+G830 Colon in 'is label' expression YES
+G840 Path-ordered aggregates NO
+G850 SQL/PGQ Information Schema views YES
+G860 GET DIAGNOSTICS enhancements for SQL-property graphs NO
+G900 GRAPH_TABLE YES SQL/PGQ required
+G901 GRAPH_TABLE: ONE ROW PER VERTEX NO
+G902 GRAPH_TABLE: ONE ROW PER STEP NO
+G903 GRAPH_TABLE: explicit ONE ROW PER MATCH keywords NO
+G904 All properties reference NO
+G905 GRAPH_TABLE: optional COLUMNS clause NO
+G906 GRAPH_TABLE: explicit EXPORT ALL NO
+G907 GRAPH_TABLE: EXPORT ALL EXCEPT NO
+G908 GRAPH_TABLE: EXPORT SINGLETONS list NO
+G909 GRAPH_TABLE: explicit EXPORT NO SINGLETONS NO
+G910 GRAPH_TABLE: 'in paths clause' NO
+G920 DDL-based SQL-property graphs YES SQL/PGQ required
+G921 Empty SQL-property graph YES
+G922 Views as element tables YES
+G923 In-line views as element tables NO
+G924 Explicit key clause for element tables YES SQL/PGQ required
+G925 Explicit label and properties clause for element tables YES SQL/PGQ required
+G926 More than one label for vertex tables YES
+G927 More than one label for edge tables YES
+G928 Value expressions as properties and renaming of properties YES
+G929 Labels and properties: EXCEPT list NO
+G940 Multi-sourced/destined edges NO XXX
+G941 Implicit removal of incomplete edges NO XXX
+G950 Alter property graph statement: ADD/DROP element table YES
+G960 Alter element table definition: ADD/DROP LABEL YES
+G970 Alter element table definition: ALTER LABEL YES
+G980 DROP PROPERTY GRAPH: CASCADE drop behavior YES
R010 Row pattern recognition: FROM clause NO
R020 Row pattern recognition: WINDOW clause NO
R030 Row pattern recognition: full aggregate support NO
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 48f7348f91..6260d6b79e 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -46,6 +46,7 @@ OBJS = \
portalcmds.o \
prepare.o \
proclang.o \
+ propgraphcmds.o \
publicationcmds.o \
schemacmds.o \
seclabel.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index cd740140fd..af31144a17 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -378,6 +378,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_MATVIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
+ case OBJECT_PROPGRAPH:
return RenameRelation(stmt);
case OBJECT_COLUMN:
@@ -534,6 +535,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
+ case OBJECT_PROPGRAPH:
address = AlterTableNamespace(stmt,
oldSchemaAddr ? &oldNspOid : NULL);
break;
@@ -685,6 +687,9 @@ AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid,
case OCLASS_EVENT_TRIGGER:
case OCLASS_PARAMETER_ACL:
case OCLASS_POLICY:
+ case OCLASS_PROPGRAPH_ELEMENT:
+ case OCLASS_PROPGRAPH_LABEL:
+ case OCLASS_PROPGRAPH_PROPERTY:
case OCLASS_PUBLICATION:
case OCLASS_PUBLICATION_NAMESPACE:
case OCLASS_PUBLICATION_REL:
@@ -907,6 +912,7 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
case OBJECT_OPCLASS:
case OBJECT_OPFAMILY:
case OBJECT_PROCEDURE:
+ case OBJECT_PROPGRAPH:
case OBJECT_ROUTINE:
case OBJECT_STATISTIC_EXT:
case OBJECT_TABLESPACE:
@@ -916,16 +922,29 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
Relation relation;
ObjectAddress address;
- address = get_object_address(stmt->objectType,
- stmt->object,
- &relation,
- AccessExclusiveLock,
- false);
- Assert(relation == NULL);
+ if (stmt->relation)
+ address = get_object_address_rv(stmt->objectType,
+ stmt->relation,
+ NIL,
+ &relation,
+ AccessExclusiveLock,
+ false);
+ else
+ {
+ address = get_object_address(stmt->objectType,
+ stmt->object,
+ &relation,
+ AccessExclusiveLock,
+ false);
+ Assert(relation == NULL);
+ }
AlterObjectOwner_internal(address.classId, address.objectId,
newowner);
+ if (relation)
+ relation_close(relation, NoLock);
+
return address;
}
break;
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 85eec7e394..8d6faf1edd 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -482,6 +482,7 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
case OBJECT_FOREIGN_TABLE:
case OBJECT_INDEX:
case OBJECT_MATVIEW:
+ case OBJECT_PROPGRAPH:
case OBJECT_ROLE:
case OBJECT_SEQUENCE:
case OBJECT_SUBSCRIPTION:
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index ab11ab500b..4dbc48ea13 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1167,6 +1167,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_OPFAMILY:
case OBJECT_POLICY:
case OBJECT_PROCEDURE:
+ case OBJECT_PROPGRAPH:
case OBJECT_PUBLICATION:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
@@ -1247,6 +1248,9 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DEFACL:
case OCLASS_EXTENSION:
case OCLASS_POLICY:
+ case OCLASS_PROPGRAPH_ELEMENT:
+ case OCLASS_PROPGRAPH_LABEL:
+ case OCLASS_PROPGRAPH_PROPERTY:
case OCLASS_PUBLICATION:
case OCLASS_PUBLICATION_NAMESPACE:
case OCLASS_PUBLICATION_REL:
@@ -2265,6 +2269,7 @@ stringify_grant_objtype(ObjectType objtype)
case OBJECT_OPERATOR:
case OBJECT_OPFAMILY:
case OBJECT_POLICY:
+ case OBJECT_PROPGRAPH:
case OBJECT_PUBLICATION:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
@@ -2349,6 +2354,7 @@ stringify_adefprivs_objtype(ObjectType objtype)
case OBJECT_OPFAMILY:
case OBJECT_PARAMETER_ACL:
case OBJECT_POLICY:
+ case OBJECT_PROPGRAPH:
case OBJECT_PUBLICATION:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
diff --git a/src/backend/commands/meson.build b/src/backend/commands/meson.build
index 6dd00a4abd..34dd12d7d5 100644
--- a/src/backend/commands/meson.build
+++ b/src/backend/commands/meson.build
@@ -34,6 +34,7 @@ backend_sources += files(
'portalcmds.c',
'prepare.c',
'proclang.c',
+ 'propgraphcmds.c',
'publicationcmds.c',
'schemacmds.c',
'seclabel.c',
diff --git a/src/backend/commands/propgraphcmds.c b/src/backend/commands/propgraphcmds.c
new file mode 100644
index 0000000000..2594886134
--- /dev/null
+++ b/src/backend/commands/propgraphcmds.c
@@ -0,0 +1,1001 @@
+/*-------------------------------------------------------------------------
+ *
+ * propgraphcmds.c
+ * property graph manipulation
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/commands/propgraphcmds.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/table.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_propgraph_element.h"
+#include "catalog/pg_propgraph_label.h"
+#include "catalog/pg_propgraph_property.h"
+#include "commands/propgraphcmds.h"
+#include "commands/tablecmds.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_relation.h"
+#include "parser/parse_target.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+struct element_info
+{
+ Oid elementid;
+ char kind;
+ Oid relid;
+ char *aliasname;
+ ArrayType *key;
+
+ char *srcvertex;
+ Oid srcvertexid;
+ ArrayType *srckey;
+ ArrayType *srcref;
+
+ char *destvertex;
+ Oid destvertexid;
+ ArrayType *destkey;
+ ArrayType *destref;
+
+ List *labels;
+};
+
+
+static ArrayType *propgraph_element_get_key(ParseState *pstate, const List *key_clause, int location, Relation element_rel);
+static ArrayType *array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel);
+static void insert_element_record(ObjectAddress pgaddress, struct element_info *einfo);
+static Oid insert_label_record(Oid graphid, Oid peoid, const char *label);
+static void insert_property_records(Oid graphid, Oid labeloid, Oid pgerelid, const PropGraphProperties *properties);
+static void insert_property_record(Oid graphid, Oid labeloid, const char *propname, const Expr *expr);
+static Oid get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location);
+static Oid get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location);
+static Oid get_element_relid(Oid peid);
+
+
+/*
+ * CREATE PROPERTY GRAPH
+ */
+ObjectAddress
+CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt)
+{
+ CreateStmt *cstmt = makeNode(CreateStmt);
+ char components_persistence;
+ ListCell *lc;
+ ObjectAddress pgaddress;
+ List *vertex_infos = NIL;
+ List *edge_infos = NIL;
+ List *element_aliases = NIL;
+
+ if (stmt->pgname->relpersistence == RELPERSISTENCE_UNLOGGED)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("property graphs cannot be unlogged because they do not have storage")));
+
+ components_persistence = RELPERSISTENCE_PERMANENT;
+
+ foreach(lc, stmt->vertex_tables)
+ {
+ PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
+ struct element_info *vinfo;
+ Relation rel;
+
+ vinfo = palloc0_object(struct element_info);
+ vinfo->kind = PGEKIND_VERTEX;
+
+ vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
+
+ rel = table_open(vinfo->relid, NoLock);
+
+ if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+ components_persistence = RELPERSISTENCE_TEMP;
+
+ if (vertex->vtable->alias)
+ vinfo->aliasname = vertex->vtable->alias->aliasname;
+ else
+ vinfo->aliasname = vertex->vtable->relname;
+
+ if (list_member(element_aliases, makeString(vinfo->aliasname)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("alias \"%s\" used more than once as element table", vinfo->aliasname),
+ parser_errposition(pstate, vertex->location)));
+
+ vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, vertex->location, rel);
+
+ vinfo->labels = vertex->labels;
+
+ table_close(rel, NoLock);
+
+ vertex_infos = lappend(vertex_infos, vinfo);
+
+ element_aliases = lappend(element_aliases, makeString(vinfo->aliasname));
+ }
+
+ foreach(lc, stmt->edge_tables)
+ {
+ PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc);
+ struct element_info *einfo;
+ Relation rel;
+ ListCell *lc2;
+ Oid srcrelid;
+ Oid destrelid;
+ Relation srcrel;
+ Relation destrel;
+
+ einfo = palloc0_object(struct element_info);
+ einfo->kind = PGEKIND_EDGE;
+
+ einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
+
+ rel = table_open(einfo->relid, NoLock);
+
+ if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+ components_persistence = RELPERSISTENCE_TEMP;
+
+ if (edge->etable->alias)
+ einfo->aliasname = edge->etable->alias->aliasname;
+ else
+ einfo->aliasname = edge->etable->relname;
+
+ if (list_member(element_aliases, makeString(einfo->aliasname)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("alias \"%s\" used more than once as element table", einfo->aliasname),
+ parser_errposition(pstate, edge->location)));
+
+ einfo->key = propgraph_element_get_key(pstate, edge->ekey, edge->location, rel);
+
+ einfo->srcvertex = edge->esrcvertex;
+ einfo->destvertex = edge->edestvertex;
+
+ srcrelid = 0;
+ destrelid = 0;
+ foreach(lc2, vertex_infos)
+ {
+ struct element_info *vinfo = lfirst(lc2);
+
+ if (strcmp(vinfo->aliasname, edge->esrcvertex) == 0)
+ srcrelid = vinfo->relid;
+
+ if (strcmp(vinfo->aliasname, edge->edestvertex) == 0)
+ destrelid = vinfo->relid;
+
+ if (srcrelid && destrelid)
+ break;
+ }
+ if (!srcrelid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("source vertex \"%s\" of edge \"%s\" does not exist",
+ edge->esrcvertex, einfo->aliasname),
+ parser_errposition(pstate, edge->location)));
+ if (!destrelid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("destination vertex \"%s\" of edge \"%s\" does not exist",
+ edge->edestvertex, einfo->aliasname),
+ parser_errposition(pstate, edge->location)));
+
+ if (!edge->esrckey || !edge->esrcvertexcols || !edge->edestkey || !edge->edestvertexcols)
+ elog(ERROR, "TODO foreign key support");
+
+ srcrel = table_open(srcrelid, NoLock);
+ destrel = table_open(destrelid, NoLock);
+
+ if (list_length(edge->esrckey) != list_length(edge->esrcvertexcols))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("mismatching number of columns in source vertex definition of edge \"%s\"",
+ einfo->aliasname),
+ parser_errposition(pstate, edge->location)));
+
+ if (list_length(edge->edestkey) != list_length(edge->edestvertexcols))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("mismatching number of columns in destination vertex definition of edge \"%s\"",
+ einfo->aliasname),
+ parser_errposition(pstate, edge->location)));
+
+ einfo->srckey = array_from_column_list(pstate, edge->esrckey, edge->location, rel);
+ einfo->srcref = array_from_column_list(pstate, edge->esrcvertexcols, edge->location, srcrel);
+ einfo->destkey = array_from_column_list(pstate, edge->edestkey, edge->location, rel);
+ einfo->destref = array_from_column_list(pstate, edge->edestvertexcols, edge->location, destrel);
+
+ /* TODO: various consistency checks */
+
+ einfo->labels = edge->labels;
+
+ table_close(destrel, NoLock);
+ table_close(srcrel, NoLock);
+
+ table_close(rel, NoLock);
+
+ edge_infos = lappend(edge_infos, einfo);
+
+ element_aliases = lappend(element_aliases, makeString(einfo->aliasname));
+ }
+
+ cstmt->relation = stmt->pgname;
+ cstmt->oncommit = ONCOMMIT_NOOP;
+
+ /*
+ * Automatically make it temporary if any component tables are temporary
+ * (see also DefineView()).
+ */
+ if (stmt->pgname->relpersistence == RELPERSISTENCE_PERMANENT
+ && components_persistence == RELPERSISTENCE_TEMP)
+ {
+ cstmt->relation = copyObject(cstmt->relation);
+ cstmt->relation->relpersistence = RELPERSISTENCE_TEMP;
+ ereport(NOTICE,
+ (errmsg("property graph \"%s\" will be temporary",
+ stmt->pgname->relname)));
+ }
+
+ pgaddress = DefineRelation(cstmt, RELKIND_PROPGRAPH, InvalidOid, NULL, NULL);
+
+ foreach(lc, vertex_infos)
+ {
+ struct element_info *vinfo = lfirst(lc);
+
+ insert_element_record(pgaddress, vinfo);
+ }
+
+ foreach(lc, edge_infos)
+ {
+ struct element_info *einfo = lfirst(lc);
+ ListCell *lc2;
+
+ /*
+ * Look up the vertices again. Now the vertices have OIDs assigned,
+ * which we need.
+ */
+ foreach(lc2, vertex_infos)
+ {
+ struct element_info *vinfo = lfirst(lc2);
+
+ if (strcmp(vinfo->aliasname, einfo->srcvertex) == 0)
+ einfo->srcvertexid = vinfo->elementid;
+ if (strcmp(vinfo->aliasname, einfo->destvertex) == 0)
+ einfo->destvertexid = vinfo->elementid;
+ if (einfo->srcvertexid && einfo->destvertexid)
+ break;
+ }
+ Assert(einfo->srcvertexid);
+ Assert(einfo->destvertexid);
+ insert_element_record(pgaddress, einfo);
+ }
+
+ return pgaddress;
+}
+
+/*
+ * Process the key clause specified for an element. If key_clause is non-NIL,
+ * then it is a list of column names. Otherwise, the primary key of the
+ * relation is used. The return value is an array of column numbers.
+ */
+static ArrayType *
+propgraph_element_get_key(ParseState *pstate, const List *key_clause, int location, Relation element_rel)
+{
+ ArrayType *a;
+
+ if (key_clause == NIL)
+ {
+ Oid pkidx = RelationGetPrimaryKeyIndex(element_rel);
+
+ if (!pkidx)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("element table key must be specified for table without primary key"),
+ parser_errposition(pstate, location)));
+ else
+ {
+ Relation indexDesc;
+ int numattrs;
+ Datum *attnumsd;
+
+ indexDesc = index_open(pkidx, AccessShareLock);
+ numattrs = indexDesc->rd_index->indkey.dim1;
+ attnumsd = palloc_array(Datum, numattrs);
+ for (int i = 0; i < numattrs; i++)
+ attnumsd[i] = Int16GetDatum(indexDesc->rd_index->indkey.values[i]);
+ a = construct_array_builtin(attnumsd, numattrs, INT2OID);
+ index_close(indexDesc, NoLock);
+ }
+ }
+ else
+ {
+ a = array_from_column_list(pstate, key_clause, location, element_rel);
+ }
+
+ return a;
+}
+
+/*
+ * Convert list of column names in the specified relation into an array of
+ * column numbers.
+ */
+static ArrayType *
+array_from_column_list(ParseState *pstate, const List *colnames, int location, Relation element_rel)
+{
+ int numattrs;
+ Datum *attnumsd;
+ int i;
+ ListCell *lc;
+
+ numattrs = list_length(colnames);
+ attnumsd = palloc_array(Datum, numattrs);
+
+ i = 0;
+ foreach(lc, colnames)
+ {
+ char *colname = strVal(lfirst(lc));
+ Oid relid = RelationGetRelid(element_rel);
+ AttrNumber attnum;
+
+ attnum = get_attnum(relid, colname);
+ if (!attnum)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
+ colname, get_rel_name(relid)),
+ parser_errposition(pstate, location)));
+ attnumsd[i++] = Int16GetDatum(attnum);
+ }
+
+ for (int j = 0; j < numattrs; j++)
+ {
+ for (int k = j + 1; k < numattrs; k++)
+ {
+ if (DatumGetInt16(attnumsd[j]) == DatumGetInt16(attnumsd[k]))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("graph key columns list must not contain duplicates"),
+ parser_errposition(pstate, location)));
+ }
+ }
+
+ return construct_array_builtin(attnumsd, numattrs, INT2OID);
+}
+
+/*
+ * Insert a record for an element into the pg_propgraph_element catalog. Also
+ * inserts labels and properties into their respective catalogs.
+ */
+static void
+insert_element_record(ObjectAddress pgaddress, struct element_info *einfo)
+{
+ Oid graphid = pgaddress.objectId;
+ Relation rel;
+ NameData aliasname;
+ Oid peoid;
+ Datum values[Natts_pg_propgraph_element] = {0};
+ bool nulls[Natts_pg_propgraph_element] = {0};
+ HeapTuple tup;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+
+ rel = table_open(PropgraphElementRelationId, RowExclusiveLock);
+
+ peoid = GetNewOidWithIndex(rel, PropgraphElementObjectIndexId, Anum_pg_propgraph_element_oid);
+ einfo->elementid = peoid;
+ values[Anum_pg_propgraph_element_oid - 1] = ObjectIdGetDatum(peoid);
+ values[Anum_pg_propgraph_element_pgepgid - 1] = ObjectIdGetDatum(graphid);
+ values[Anum_pg_propgraph_element_pgerelid - 1] = ObjectIdGetDatum(einfo->relid);
+ namestrcpy(&aliasname, einfo->aliasname);
+ values[Anum_pg_propgraph_element_pgealias - 1] = NameGetDatum(&aliasname);
+ values[Anum_pg_propgraph_element_pgekind - 1] = CharGetDatum(einfo->kind);
+ values[Anum_pg_propgraph_element_pgesrcvertexid - 1] = ObjectIdGetDatum(einfo->srcvertexid);
+ values[Anum_pg_propgraph_element_pgedestvertexid - 1] = ObjectIdGetDatum(einfo->destvertexid);
+ values[Anum_pg_propgraph_element_pgekey - 1] = PointerGetDatum(einfo->key);
+
+ if (einfo->srckey)
+ values[Anum_pg_propgraph_element_pgesrckey - 1] = PointerGetDatum(einfo->srckey);
+ else
+ nulls[Anum_pg_propgraph_element_pgesrckey - 1] = true;
+ if (einfo->srcref)
+ values[Anum_pg_propgraph_element_pgesrcref - 1] = PointerGetDatum(einfo->srcref);
+ else
+ nulls[Anum_pg_propgraph_element_pgesrcref - 1] = true;
+ if (einfo->destkey)
+ values[Anum_pg_propgraph_element_pgedestkey - 1] = PointerGetDatum(einfo->destkey);
+ else
+ nulls[Anum_pg_propgraph_element_pgedestkey - 1] = true;
+ if (einfo->destref)
+ values[Anum_pg_propgraph_element_pgedestref - 1] = PointerGetDatum(einfo->destref);
+ else
+ nulls[Anum_pg_propgraph_element_pgedestref - 1] = true;
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, PropgraphElementRelationId, peoid);
+
+ /* Add dependency on the property graph */
+ recordDependencyOn(&myself, &pgaddress, DEPENDENCY_AUTO);
+
+ /* Add dependency on the relation */
+ ObjectAddressSet(referenced, RelationRelationId, einfo->relid);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ /* Add dependencies on vertices */
+ /* TODO: columns */
+ if (einfo->srcvertexid)
+ {
+ ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->srcvertexid);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+ if (einfo->destvertexid)
+ {
+ ObjectAddressSet(referenced, PropgraphElementRelationId, einfo->destvertexid);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ table_close(rel, NoLock);
+
+ if (einfo->labels)
+ {
+ ListCell *lc;
+
+ foreach(lc, einfo->labels)
+ {
+ PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
+ Oid labeloid;
+
+ if (lp->label)
+ labeloid = insert_label_record(graphid, peoid, lp->label);
+ else
+ labeloid = insert_label_record(graphid, peoid, einfo->aliasname);
+ insert_property_records(graphid, labeloid, einfo->relid, lp->properties);
+ }
+ }
+ else
+ {
+ Oid labeloid;
+ PropGraphProperties *pr = makeNode(PropGraphProperties);
+
+ pr->all = true;
+ pr->location = -1;
+
+ labeloid = insert_label_record(graphid, peoid, einfo->aliasname);
+ insert_property_records(graphid, labeloid, einfo->relid, pr);
+ }
+}
+
+/*
+ * Insert a record for a label into the pg_propgraph_label catalog.
+ */
+static Oid
+insert_label_record(Oid graphid, Oid peoid, const char *label)
+{
+ Relation rel;
+ NameData labelname;
+ Oid labeloid;
+ Datum values[Natts_pg_propgraph_label] = {0};
+ bool nulls[Natts_pg_propgraph_label] = {0};
+ HeapTuple tup;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+
+ rel = table_open(PropgraphLabelRelationId, RowExclusiveLock);
+
+ labeloid = GetNewOidWithIndex(rel, PropgraphLabelObjectIndexId, Anum_pg_propgraph_label_oid);
+ values[Anum_pg_propgraph_label_oid - 1] = ObjectIdGetDatum(labeloid);
+ values[Anum_pg_propgraph_label_pglpgid - 1] = ObjectIdGetDatum(graphid);
+ namestrcpy(&labelname, label);
+ values[Anum_pg_propgraph_label_pgllabel - 1] = NameGetDatum(&labelname);
+ values[Anum_pg_propgraph_label_pglelid - 1] = ObjectIdGetDatum(peoid);
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, PropgraphLabelRelationId, labeloid);
+
+ /* Add dependency on the property graph element */
+ ObjectAddressSet(referenced, PropgraphElementRelationId, peoid);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+
+ table_close(rel, NoLock);
+
+ return labeloid;
+}
+
+/*
+ * Insert records for properties into the pg_propgraph_property catalog.
+ */
+static void
+insert_property_records(Oid graphid, Oid labeloid, Oid pgerelid, const PropGraphProperties *properties)
+{
+ List *proplist = NIL;
+ ParseState *pstate;
+ ParseNamespaceItem *nsitem;
+ List *tp;
+ Relation rel;
+ ListCell *lc;
+
+ if (properties->all)
+ {
+ Relation attRelation;
+ SysScanDesc scan;
+ ScanKeyData key[1];
+ HeapTuple attributeTuple;
+
+ attRelation = table_open(AttributeRelationId, RowShareLock);
+ ScanKeyInit(&key[0],
+ Anum_pg_attribute_attrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(pgerelid));
+ scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
+ true, NULL, 1, key);
+ while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
+ {
+ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
+ ColumnRef *cr;
+ ResTarget *rt;
+
+ if (att->attnum <= 0 || att->attisdropped)
+ continue;
+
+ cr = makeNode(ColumnRef);
+ rt = makeNode(ResTarget);
+
+ cr->fields = list_make1(makeString(NameStr(att->attname)));
+ cr->location = -1;
+
+ rt->name = pstrdup(NameStr(att->attname));
+ rt->val = (Node *) cr;
+ rt->location = -1;
+
+ proplist = lappend(proplist, rt);
+ }
+ systable_endscan(scan);
+ table_close(attRelation, RowShareLock);
+ }
+ else
+ {
+ proplist = properties->properties;
+
+ foreach(lc, proplist)
+ {
+ ResTarget *rt = lfirst_node(ResTarget, lc);
+
+ if (!rt->name && !IsA(rt->val, ColumnRef))
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("property name required"),
+ parser_errposition(NULL, rt->location));
+ }
+ }
+
+ rel = table_open(pgerelid, AccessShareLock);
+
+ pstate = make_parsestate(NULL);
+ nsitem = addRangeTableEntryForRelation(pstate,
+ rel,
+ AccessShareLock,
+ NULL,
+ false,
+ true);
+ addNSItemToQuery(pstate, nsitem, true, true, true);
+
+ table_close(rel, NoLock);
+
+ tp = transformTargetList(pstate, proplist, EXPR_KIND_OTHER);
+
+ foreach(lc, tp)
+ {
+ TargetEntry *te = lfirst_node(TargetEntry, lc);
+
+ insert_property_record(graphid, labeloid, te->resname, te->expr);
+ }
+}
+
+/*
+ * Insert one record for a property into the pg_propgraph_property catalog.
+ */
+static void
+insert_property_record(Oid graphid, Oid labeloid, const char *propname, const Expr *expr)
+{
+ Relation rel;
+ NameData propnamedata;
+ Oid propoid;
+ Datum values[Natts_pg_propgraph_property] = {0};
+ bool nulls[Natts_pg_propgraph_property] = {0};
+ HeapTuple tup;
+ ObjectAddress myself;
+ ObjectAddress referenced;
+
+ rel = table_open(PropgraphPropertyRelationId, RowExclusiveLock);
+
+ propoid = GetNewOidWithIndex(rel, PropgraphPropertyObjectIndexId, Anum_pg_propgraph_property_oid);
+ values[Anum_pg_propgraph_property_oid - 1] = ObjectIdGetDatum(propoid);
+ values[Anum_pg_propgraph_property_pgppgid - 1] = ObjectIdGetDatum(graphid);
+ namestrcpy(&propnamedata, propname);
+ values[Anum_pg_propgraph_property_pgpname - 1] = NameGetDatum(&propnamedata);
+ values[Anum_pg_propgraph_property_pgptypid - 1] = ObjectIdGetDatum(exprType((const Node *) expr));
+ values[Anum_pg_propgraph_property_pgplabelid - 1] = ObjectIdGetDatum(labeloid);
+ values[Anum_pg_propgraph_property_pgpexpr - 1] = CStringGetTextDatum(nodeToString(expr));
+
+ tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ CatalogTupleInsert(rel, tup);
+ heap_freetuple(tup);
+
+ ObjectAddressSet(myself, PropgraphPropertyRelationId, propoid);
+
+ /* Add dependency on the property graph label */
+ ObjectAddressSet(referenced, PropgraphLabelRelationId, labeloid);
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+
+ table_close(rel, NoLock);
+}
+
+/*
+ * ALTER PROPERTY GRAPH
+ */
+ObjectAddress
+AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt)
+{
+ Oid pgrelid;
+ ListCell *lc;
+ ObjectAddress pgaddress;
+
+ pgrelid = RangeVarGetRelidExtended(stmt->pgname,
+ ShareRowExclusiveLock,
+ stmt->missing_ok ? RVR_MISSING_OK : 0,
+ RangeVarCallbackOwnsRelation,
+ NULL);
+ if (pgrelid == InvalidOid)
+ {
+ ereport(NOTICE,
+ (errmsg("relation \"%s\" does not exist, skipping",
+ stmt->pgname->relname)));
+ return InvalidObjectAddress;
+ }
+
+ ObjectAddressSet(pgaddress, RelationRelationId, pgrelid);
+
+ foreach(lc, stmt->add_vertex_tables)
+ {
+ PropGraphVertex *vertex = lfirst_node(PropGraphVertex, lc);
+ struct element_info *vinfo;
+ Relation rel;
+
+ vinfo = palloc0_object(struct element_info);
+ vinfo->kind = PGEKIND_VERTEX;
+
+ vinfo->relid = RangeVarGetRelidExtended(vertex->vtable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
+
+ rel = table_open(vinfo->relid, NoLock);
+
+ if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+ elog(ERROR, "TODO");
+
+ if (vertex->vtable->alias)
+ vinfo->aliasname = vertex->vtable->alias->aliasname;
+ else
+ vinfo->aliasname = vertex->vtable->relname;
+
+ vinfo->key = propgraph_element_get_key(pstate, vertex->vkey, vertex->location, rel);
+
+ vinfo->labels = vertex->labels;
+
+ table_close(rel, NoLock);
+
+ insert_element_record(pgaddress, vinfo);
+ }
+
+ CommandCounterIncrement();
+
+ foreach(lc, stmt->add_edge_tables)
+ {
+ PropGraphEdge *edge = lfirst_node(PropGraphEdge, lc);
+ struct element_info *einfo;
+ Relation rel;
+ Oid srcrelid;
+ Oid destrelid;
+ Relation srcrel;
+ Relation destrel;
+
+ einfo = palloc0_object(struct element_info);
+ einfo->kind = PGEKIND_EDGE;
+
+ einfo->relid = RangeVarGetRelidExtended(edge->etable, AccessShareLock, 0, RangeVarCallbackOwnsRelation, NULL);
+
+ rel = table_open(einfo->relid, NoLock);
+
+ if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+ elog(ERROR, "TODO");
+
+ if (edge->etable->alias)
+ einfo->aliasname = edge->etable->alias->aliasname;
+ else
+ einfo->aliasname = edge->etable->relname;
+
+ einfo->key = propgraph_element_get_key(pstate, edge->ekey, edge->location, rel);
+
+ einfo->srcvertexid = get_vertex_oid(pstate, pgrelid, edge->esrcvertex, edge->location);
+ einfo->destvertexid = get_vertex_oid(pstate, pgrelid, edge->edestvertex, edge->location);
+
+ if (!edge->esrckey || !edge->esrcvertexcols || !edge->edestkey || !edge->edestvertexcols)
+ elog(ERROR, "TODO foreign key support");
+
+ srcrelid = get_element_relid(einfo->srcvertexid);
+ destrelid = get_element_relid(einfo->destvertexid);
+
+ srcrel = table_open(srcrelid, AccessShareLock);
+ destrel = table_open(destrelid, AccessShareLock);
+
+ if (list_length(edge->esrckey) != list_length(edge->esrcvertexcols))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("mismatching number of columns in source vertex definition of edge \"%s\"",
+ einfo->aliasname),
+ parser_errposition(pstate, edge->location)));
+
+ if (list_length(edge->edestkey) != list_length(edge->edestvertexcols))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("mismatching number of columns in destination vertex definition of edge \"%s\"",
+ einfo->aliasname),
+ parser_errposition(pstate, edge->location)));
+
+ einfo->srckey = array_from_column_list(pstate, edge->esrckey, edge->location, rel);
+ einfo->srcref = array_from_column_list(pstate, edge->esrcvertexcols, edge->location, srcrel);
+ einfo->destkey = array_from_column_list(pstate, edge->edestkey, edge->location, rel);
+ einfo->destref = array_from_column_list(pstate, edge->edestvertexcols, edge->location, destrel);
+
+ /* TODO: various consistency checks */
+
+ einfo->labels = edge->labels;
+
+ table_close(destrel, NoLock);
+ table_close(srcrel, NoLock);
+
+ table_close(rel, NoLock);
+
+ insert_element_record(pgaddress, einfo);
+ }
+
+ foreach(lc, stmt->drop_vertex_tables)
+ {
+ char *alias = strVal(lfirst(lc));
+ Oid peoid;
+ ObjectAddress obj;
+
+ peoid = get_vertex_oid(pstate, pgrelid, alias, -1);
+ ObjectAddressSet(obj, PropgraphElementRelationId, peoid);
+ performDeletion(&obj, stmt->drop_behavior, 0);
+ }
+
+ foreach(lc, stmt->drop_edge_tables)
+ {
+ char *alias = strVal(lfirst(lc));
+ Oid peoid;
+ ObjectAddress obj;
+
+ peoid = get_edge_oid(pstate, pgrelid, alias, -1);
+ ObjectAddressSet(obj, PropgraphElementRelationId, peoid);
+ performDeletion(&obj, stmt->drop_behavior, 0);
+ }
+
+ foreach(lc, stmt->add_labels)
+ {
+ PropGraphLabelAndProperties *lp = lfirst_node(PropGraphLabelAndProperties, lc);
+ Oid peoid;
+ Oid pgerelid;
+ Oid labeloid;
+
+ Assert(lp->label);
+
+ if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
+ peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
+ else
+ peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
+
+ pgerelid = get_element_relid(peoid);
+
+ labeloid = insert_label_record(pgrelid, peoid, lp->label);
+ insert_property_records(pgrelid, labeloid, pgerelid, lp->properties);
+ }
+
+ if (stmt->drop_label)
+ {
+ Oid peoid;
+ Oid labeloid;
+ ObjectAddress obj;
+
+ if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
+ peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
+ else
+ peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
+
+ labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
+ Anum_pg_propgraph_label_oid,
+ ObjectIdGetDatum(peoid),
+ CStringGetDatum(stmt->drop_label));
+ if (!labeloid)
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
+ get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
+ parser_errposition(pstate, -1));
+
+ ObjectAddressSet(obj, PropgraphLabelRelationId, labeloid);
+ performDeletion(&obj, stmt->drop_behavior, 0);
+ }
+
+ if (stmt->add_properties)
+ {
+ Oid peoid;
+ Oid pgerelid;
+ Oid labeloid;
+
+ if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
+ peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
+ else
+ peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
+
+ pgerelid = get_element_relid(peoid);
+
+ labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
+ Anum_pg_propgraph_label_oid,
+ ObjectIdGetDatum(peoid),
+ CStringGetDatum(stmt->alter_label));
+ if (!labeloid)
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
+ get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label),
+ parser_errposition(pstate, -1));
+
+ insert_property_records(pgrelid, labeloid, pgerelid, stmt->add_properties);
+ }
+
+ if (stmt->drop_properties)
+ {
+ Oid peoid;
+ Oid labeloid;
+ ObjectAddress obj;
+
+ if (stmt->element_kind == PROPGRAPH_ELEMENT_KIND_VERTEX)
+ peoid = get_vertex_oid(pstate, pgrelid, stmt->element_alias, -1);
+ else
+ peoid = get_edge_oid(pstate, pgrelid, stmt->element_alias, -1);
+
+ labeloid = GetSysCacheOid2(PROPGRAPHLABELNAME,
+ Anum_pg_propgraph_label_oid,
+ ObjectIdGetDatum(peoid),
+ CStringGetDatum(stmt->alter_label));
+ if (!labeloid)
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("property graph \"%s\" element \"%s\" has no label \"%s\"",
+ get_rel_name(pgrelid), stmt->element_alias, stmt->drop_label),
+ parser_errposition(pstate, -1));
+
+ foreach(lc, stmt->drop_properties)
+ {
+ char *propname = strVal(lfirst(lc));
+ Oid propoid;
+
+ propoid = GetSysCacheOid2(PROPGRAPHPROPNAME,
+ Anum_pg_propgraph_property_oid,
+ ObjectIdGetDatum(labeloid),
+ CStringGetDatum(propname));
+ if (!propoid)
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("property graph \"%s\" element \"%s\" label \"%s\" has no property \"%s\"",
+ get_rel_name(pgrelid), stmt->element_alias, stmt->alter_label, propname),
+ parser_errposition(pstate, -1));
+
+ ObjectAddressSet(obj, PropgraphPropertyRelationId, propoid);
+ performDeletion(&obj, stmt->drop_behavior, 0);
+ }
+ }
+
+ return pgaddress;
+}
+
+/*
+ * Get OID of vertex from graph OID and element alias. Element must be a
+ * vertex, otherwise error.
+ */
+static Oid
+get_vertex_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location)
+{
+ HeapTuple tuple;
+ Oid peoid;
+
+ tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias));
+ if (!tuple)
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("property graph \"%s\" has no element with alias \"%s\"",
+ get_rel_name(pgrelid), alias),
+ parser_errposition(pstate, location));
+
+ if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_VERTEX)
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("element \"%s\" of property graph \"%s\" is not a vertex",
+ alias, get_rel_name(pgrelid)),
+ parser_errposition(pstate, location));
+
+ peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid;
+
+ ReleaseSysCache(tuple);
+
+ return peoid;
+}
+
+/*
+ * Get OID of edge from graph OID and element alias. Element must be an edge,
+ * otherwise error.
+ */
+static Oid
+get_edge_oid(ParseState *pstate, Oid pgrelid, const char *alias, int location)
+{
+ HeapTuple tuple;
+ Oid peoid;
+
+ tuple = SearchSysCache2(PROPGRAPHELALIAS, ObjectIdGetDatum(pgrelid), CStringGetDatum(alias));
+ if (!tuple)
+ ereport(ERROR,
+ errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("property graph \"%s\" has no element with alias \"%s\"",
+ get_rel_name(pgrelid), alias),
+ parser_errposition(pstate, location));
+
+ if (((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgekind != PGEKIND_EDGE)
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("element \"%s\" of property graph \"%s\" is not an edge",
+ alias, get_rel_name(pgrelid)),
+ parser_errposition(pstate, location));
+
+ peoid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->oid;
+
+ ReleaseSysCache(tuple);
+
+ return peoid;
+}
+
+/*
+ * Get the element table relation OID from the OID of the element.
+ */
+static Oid
+get_element_relid(Oid peid)
+{
+ HeapTuple tuple;
+ Oid pgerelid;
+
+ tuple = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(peid));
+ if (!tuple)
+ elog(ERROR, "cache lookup failed for property graph element %u", peid);
+
+ pgerelid = ((Form_pg_propgraph_element) GETSTRUCT(tuple))->pgerelid;
+
+ ReleaseSysCache(tuple);
+
+ return pgerelid;
+}
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 5607273bf9..0a5e34abe9 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -49,6 +49,7 @@ SecLabelSupportsObjectType(ObjectType objtype)
case OBJECT_LARGEOBJECT:
case OBJECT_MATVIEW:
case OBJECT_PROCEDURE:
+ case OBJECT_PROPGRAPH:
case OBJECT_PUBLICATION:
case OBJECT_ROLE:
case OBJECT_ROUTINE:
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c61f9305c2..f8888764d9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -298,6 +298,12 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
gettext_noop("index \"%s\" does not exist, skipping"),
gettext_noop("\"%s\" is not an index"),
gettext_noop("Use DROP INDEX to remove an index.")},
+ {RELKIND_PROPGRAPH,
+ ERRCODE_UNDEFINED_OBJECT,
+ gettext_noop("property graph \"%s\" does not exist"),
+ gettext_noop("property graph \"%s\" does not exist, skipping"),
+ gettext_noop("\"%s\" is not a property graph"),
+ gettext_noop("Use DROP PROPERTY GRAPH to remove a property graph.")},
{'\0', 0, NULL, NULL, NULL, NULL}
};
@@ -1528,6 +1534,10 @@ RemoveRelations(DropStmt *drop)
relkind = RELKIND_FOREIGN_TABLE;
break;
+ case OBJECT_PROPGRAPH:
+ relkind = RELKIND_PROPGRAPH;
+ break;
+
default:
elog(ERROR, "unrecognized drop object type: %d",
(int) drop->removeType);
@@ -13989,6 +13999,9 @@ RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
case OCLASS_EXTENSION:
case OCLASS_EVENT_TRIGGER:
case OCLASS_PARAMETER_ACL:
+ case OCLASS_PROPGRAPH_ELEMENT:
+ case OCLASS_PROPGRAPH_LABEL:
+ case OCLASS_PROPGRAPH_PROPERTY:
case OCLASS_PUBLICATION:
case OCLASS_PUBLICATION_NAMESPACE:
case OCLASS_PUBLICATION_REL:
@@ -14814,6 +14827,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
case RELKIND_MATVIEW:
case RELKIND_FOREIGN_TABLE:
case RELKIND_PARTITIONED_TABLE:
+ case RELKIND_PROPGRAPH:
/* ok to change owner */
break;
case RELKIND_INDEX:
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 940499cc61..3641902661 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -594,7 +594,7 @@ ExecCheckPermissions(List *rangeTable, List *rteperminfos,
*/
Assert(rte->rtekind == RTE_RELATION ||
(rte->rtekind == RTE_SUBQUERY &&
- rte->relkind == RELKIND_VIEW));
+ (rte->relkind == RELKIND_VIEW || rte->relkind == RELKIND_PROPGRAPH)));
(void) getRTEPermissionInfo(rteperminfos, rte);
/* Many-to-one mapping not allowed */
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 6ba8e73256..fc78c9e315 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -264,6 +264,9 @@ exprType(const Node *expr)
case T_PlaceHolderVar:
type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
break;
+ case T_GraphPropertyRef:
+ type = ((const GraphPropertyRef *) expr)->typeId;
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
type = InvalidOid; /* keep compiler quiet */
@@ -500,6 +503,9 @@ exprTypmod(const Node *expr)
return ((const SetToDefault *) expr)->typeMod;
case T_PlaceHolderVar:
return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+ case T_GraphPropertyRef:
+ /* TODO */
+ return -1;
default:
break;
}
@@ -999,6 +1005,9 @@ exprCollation(const Node *expr)
case T_PlaceHolderVar:
coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
break;
+ case T_GraphPropertyRef:
+ coll = DEFAULT_COLLATION_OID; /* FIXME */
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
coll = InvalidOid; /* keep compiler quiet */
@@ -2034,6 +2043,7 @@ expression_tree_walker_impl(Node *node,
case T_RangeTblRef:
case T_SortGroupClause:
case T_CTESearchClause:
+ case T_GraphPropertyRef:
/* primitive node types with no expression subnodes */
break;
case T_WithCheckOption:
@@ -2534,6 +2544,26 @@ expression_tree_walker_impl(Node *node,
return true;
}
break;
+ case T_GraphElementPattern:
+ {
+ GraphElementPattern *gep = (GraphElementPattern *) node;
+
+ if (WALK(gep->subexpr))
+ return true;
+ if (WALK(gep->whereClause))
+ return true;
+ }
+ break;
+ case T_GraphPattern:
+ {
+ GraphPattern *gp = (GraphPattern *) node;
+
+ if (LIST_WALK(gp->path_pattern_list))
+ return true;
+ if (WALK(gp->whereClause))
+ return true;
+ }
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
@@ -2727,6 +2757,12 @@ range_table_entry_walker_impl(RangeTblEntry *rte,
if (WALK(rte->values_lists))
return true;
break;
+ case RTE_GRAPH_TABLE:
+ if (WALK(rte->graph_pattern))
+ return true;
+ if (WALK(rte->graph_table_columns))
+ return true;
+ break;
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
case RTE_RESULT:
@@ -3727,6 +3763,10 @@ range_table_mutator_impl(List *rtable,
case RTE_VALUES:
MUTATE(newrte->values_lists, rte->values_lists, List *);
break;
+ case RTE_GRAPH_TABLE:
+ MUTATE(newrte->graph_pattern, rte->graph_pattern, GraphPattern *);
+ MUTATE(newrte->graph_table_columns, rte->graph_table_columns, List *);
+ break;
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
case RTE_RESULT:
@@ -4278,6 +4318,18 @@ raw_expression_tree_walker_impl(Node *node,
return true;
}
break;
+ case T_RangeGraphTable:
+ {
+ RangeGraphTable *rgt = (RangeGraphTable *) node;
+
+ if (WALK(rgt->graph_pattern))
+ return true;
+ if (WALK(rgt->columns))
+ return true;
+ if (WALK(rgt->alias))
+ return true;
+ }
+ break;
case T_TypeName:
{
TypeName *tn = (TypeName *) node;
@@ -4436,6 +4488,26 @@ raw_expression_tree_walker_impl(Node *node,
return true;
}
break;
+ case T_GraphElementPattern:
+ {
+ GraphElementPattern *gep = (GraphElementPattern *) node;
+
+ if (WALK(gep->subexpr))
+ return true;
+ if (WALK(gep->whereClause))
+ return true;
+ }
+ break;
+ case T_GraphPattern:
+ {
+ GraphPattern *gp = (GraphPattern *) node;
+
+ if (WALK(gp->path_pattern_list))
+ return true;
+ if (WALK(gp->whereClause))
+ return true;
+ }
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 2c30bba212..7ec49cc76d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -555,6 +555,11 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
/* we re-use these RELATION fields, too: */
WRITE_OID_FIELD(relid);
break;
+ case RTE_GRAPH_TABLE:
+ WRITE_OID_FIELD(relid);
+ WRITE_NODE_FIELD(graph_pattern);
+ WRITE_NODE_FIELD(graph_table_columns);
+ break;
case RTE_RESULT:
/* no extra fields */
break;
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 426112fa37..9fe9b56092 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -395,6 +395,11 @@ _jumbleRangeTblEntry(JumbleState *jstate, Node *node)
case RTE_NAMEDTUPLESTORE:
JUMBLE_STRING(enrname);
break;
+ case RTE_GRAPH_TABLE:
+ JUMBLE_FIELD(relid);
+ JUMBLE_NODE(graph_pattern);
+ JUMBLE_NODE(graph_table_columns);
+ break;
case RTE_RESULT:
break;
default:
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index b1e2f2b440..609166bc0c 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -418,6 +418,11 @@ _readRangeTblEntry(void)
/* we re-use these RELATION fields, too: */
READ_OID_FIELD(relid);
break;
+ case RTE_GRAPH_TABLE:
+ READ_OID_FIELD(relid);
+ READ_NODE_FIELD(graph_pattern);
+ READ_NODE_FIELD(graph_table_columns);
+ break;
case RTE_RESULT:
/* no extra fields */
break;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0b98f0856e..f422d49440 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -728,6 +728,10 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
*/
return;
+ case RTE_GRAPH_TABLE:
+ /* shouldn't happen here */
+ break;
+
case RTE_RESULT:
/* RESULT RTEs, in themselves, are no problem. */
break;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 300691cc4d..adb69159d2 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1206,6 +1206,10 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
case RTE_RESULT:
/* these can't contain any lateral references */
break;
+ case RTE_GRAPH_TABLE:
+ /* shouldn't happen here */
+ Assert(false);
+ break;
}
}
}
@@ -2267,6 +2271,10 @@ replace_vars_in_jointree(Node *jtnode,
/* these shouldn't be marked LATERAL */
Assert(false);
break;
+ case RTE_GRAPH_TABLE:
+ /* shouldn't happen here */
+ Assert(false);
+ break;
}
}
}
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index 401c16686c..b03dc4cdb0 100644
--- a/src/backend/parser/Makefile
+++ b/src/backend/parser/Makefile
@@ -23,6 +23,7 @@ OBJS = \
parse_enr.o \
parse_expr.o \
parse_func.o \
+ parse_graphtable.o \
parse_merge.o \
parse_node.o \
parse_oper.o \
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 130f7fc7c3..bcc51cc5ff 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -297,6 +297,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt
CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt
CreateAssertionStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt
+ CreatePropGraphStmt AlterPropGraphStmt
CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt
CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt
DropOpClassStmt DropOpFamilyStmt DropStmt
@@ -660,6 +661,36 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
json_object_constructor_null_clause_opt
json_array_constructor_null_clause_opt
+%type <list> vertex_tables_clause edge_tables_clause
+ opt_vertex_tables_clause opt_edge_tables_clause
+ vertex_table_list
+ opt_graph_table_key_clause
+ edge_table_list
+ source_vertex_table destination_vertex_table
+ opt_element_table_label_and_properties
+ label_and_properties_list
+ add_label_list
+%type <node> vertex_table_definition edge_table_definition
+%type <alias> opt_propgraph_table_alias
+%type <str> element_table_label_clause
+%type <node> label_and_properties element_table_properties
+ add_label
+%type <ival> vertex_or_edge
+
+%type <list> opt_graph_pattern_quantifier
+ path_pattern_list
+ path_pattern
+ path_pattern_expression
+ path_term
+%type <node> graph_pattern
+ path_factor
+ path_primary
+ opt_is_label_expression
+ label_expression
+ label_disjunction
+ label_term
+%type <str> opt_colid
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -677,6 +708,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%token <ival> ICONST PARAM
%token TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER
%token LESS_EQUALS GREATER_EQUALS NOT_EQUALS
+%token BRACKET_RIGHT_ARROW LEFT_ARROW_BRACKET MINUS_LEFT_BRACKET RIGHT_BRACKET_MINUS
/*
* If you want to make any keyword changes, update the keyword table in
@@ -703,18 +735,18 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
- DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DEPTH DESC
+ DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DEPTH DESC DESTINATION
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
+ EACH EDGE ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FINALIZE FIRST_P FLOAT_P FOLLOWING FOR
FORCE FOREIGN FORMAT FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
- GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPS
+ GENERATED GLOBAL GRANT GRANTED GRAPH GRAPH_TABLE GREATEST GROUP_P GROUPING GROUPS
HANDLER HAVING HEADER_P HOLD HOUR_P
@@ -735,7 +767,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE METHOD
MINUTE_P MINVALUE MODE MONTH_P MOVE
- NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NFC NFD NFKC NFKD NO NONE
+ NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NFC NFD NFKC NFKD NO NODE NONE
NORMALIZE NORMALIZED
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
NULLS_P NUMERIC
@@ -747,19 +779,19 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD
PLACING PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
- PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
+ PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PROPERTIES PROPERTY PUBLICATION
QUOTE
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF_P REFERENCES REFERENCING
- REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
+ REFRESH REINDEX RELATIONSHIP RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROUTINE ROUTINES ROW ROWS RULE
SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT
SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
- SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
+ SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SOURCE SQL_P STABLE STANDALONE_P
START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRIP_P
SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER
@@ -772,7 +804,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNLISTEN UNLOGGED UNTIL UPDATE USER USING
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VERBOSE VERSION_P VERTEX VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -809,6 +841,11 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%token MODE_PLPGSQL_ASSIGN2
%token MODE_PLPGSQL_ASSIGN3
+ /*
+ * FIXME: The brace characters are assigned token symbols because if we
+ * mention literal braces in the rules then the ecpg parser assembly breaks.
+ */
+%token LEFT_BRACE RIGHT_BRACE
/* Precedence: lowest to highest */
%left UNION EXCEPT
@@ -867,7 +904,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%nonassoc UNBOUNDED /* ideally would have same precedence as IDENT */
%nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
SET KEYS OBJECT_P SCALAR VALUE_P WITH WITHOUT
-%left Op OPERATOR /* multi-character ops and user-defined operators */
+%left Op OPERATOR LEFT_ARROW RIGHT_ARROW /* multi-character ops and user-defined operators */
%left '+' '-'
%left '*' '/' '%'
%left '^'
@@ -998,6 +1035,7 @@ stmt:
| AlterOperatorStmt
| AlterTypeStmt
| AlterPolicyStmt
+ | AlterPropGraphStmt
| AlterSeqStmt
| AlterSystemStmt
| AlterTableStmt
@@ -1038,6 +1076,7 @@ stmt:
| AlterOpFamilyStmt
| CreatePolicyStmt
| CreatePLangStmt
+ | CreatePropGraphStmt
| CreateSchemaStmt
| CreateSeqStmt
| CreateStmt
@@ -6892,6 +6931,7 @@ object_type_any_name:
| MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; }
| INDEX { $$ = OBJECT_INDEX; }
| FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; }
+ | PROPERTY GRAPH { $$ = OBJECT_PROPGRAPH; }
| COLLATION { $$ = OBJECT_COLLATION; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| STATISTICS { $$ = OBJECT_STATISTIC_EXT; }
@@ -7748,6 +7788,15 @@ privilege_target:
n->objs = $2;
$$ = n;
}
+ | PROPERTY GRAPH qualified_name_list
+ {
+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+
+ n->targtype = ACL_TARGET_OBJECT;
+ n->objtype = OBJECT_PROPGRAPH;
+ n->objs = $3;
+ $$ = n;
+ }
| SCHEMA name_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -9065,6 +9114,366 @@ opt_if_exists: IF_P EXISTS { $$ = true; }
;
+/*****************************************************************************
+ *
+ * CREATE PROPERTY GRAPH
+ * ALTER PROPERTY GRAPH
+ *
+ *****************************************************************************/
+
+CreatePropGraphStmt: CREATE OptTemp PROPERTY GRAPH qualified_name opt_vertex_tables_clause opt_edge_tables_clause
+ {
+ CreatePropGraphStmt *n = makeNode(CreatePropGraphStmt);
+
+ n->pgname = $5;
+ n->pgname->relpersistence = $2;
+ n->vertex_tables = $6;
+ n->edge_tables = $7;
+
+ $$ = (Node *)n;
+ }
+ ;
+
+opt_vertex_tables_clause:
+ vertex_tables_clause { $$ = $1; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+vertex_tables_clause:
+ vertex_synonym TABLES '(' vertex_table_list ')' { $$ = $4; }
+ ;
+
+vertex_synonym: NODE | VERTEX
+ ;
+
+vertex_table_list: vertex_table_definition { $$ = list_make1($1); }
+ | vertex_table_list ',' vertex_table_definition { $$ = lappend($1, $3); }
+ ;
+
+vertex_table_definition: qualified_name opt_propgraph_table_alias opt_graph_table_key_clause
+ opt_element_table_label_and_properties
+ {
+ PropGraphVertex *n = makeNode(PropGraphVertex);
+
+ $1->alias = $2;
+ n->vtable = $1;
+ n->vkey = $3;
+ n->labels = $4;
+ n->location = @1;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+opt_propgraph_table_alias:
+ AS name
+ {
+ $$ = makeNode(Alias);
+ $$->aliasname = $2;
+ }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+opt_graph_table_key_clause:
+ KEY '(' columnList ')' { $$ = $3; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+opt_edge_tables_clause:
+ edge_tables_clause { $$ = $1; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+edge_tables_clause:
+ edge_synonym TABLES '(' edge_table_list ')' { $$ = $4; }
+ ;
+
+edge_synonym: EDGE | RELATIONSHIP
+ ;
+
+edge_table_list: edge_table_definition { $$ = list_make1($1); }
+ | edge_table_list ',' edge_table_definition { $$ = lappend($1, $3); }
+ ;
+
+edge_table_definition: qualified_name opt_propgraph_table_alias opt_graph_table_key_clause
+ source_vertex_table destination_vertex_table opt_element_table_label_and_properties
+ {
+ PropGraphEdge *n = makeNode(PropGraphEdge);
+
+ $1->alias = $2;
+ n->etable = $1;
+ n->ekey = $3;
+ n->esrckey = linitial($4);
+ n->esrcvertex = lsecond($4);
+ n->esrcvertexcols = lthird($4);
+ n->edestkey = linitial($5);
+ n->edestvertex = lsecond($5);
+ n->edestvertexcols = lthird($5);
+ n->labels = $6;
+ n->location = @1;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+source_vertex_table: SOURCE name
+ {
+ $$ = list_make3(NULL, $2, NULL);
+ }
+ | SOURCE KEY '(' columnList ')' REFERENCES name '(' columnList ')'
+ {
+ $$ = list_make3($4, $7, $9);
+ }
+ ;
+
+destination_vertex_table: DESTINATION name
+ {
+ $$ = list_make3(NULL, $2, NULL);
+ }
+ | DESTINATION KEY '(' columnList ')' REFERENCES name '(' columnList ')'
+ {
+ $$ = list_make3($4, $7, $9);
+ }
+ ;
+
+opt_element_table_label_and_properties:
+ element_table_properties
+ {
+ PropGraphLabelAndProperties *lp = makeNode(PropGraphLabelAndProperties);
+
+ lp->properties = (PropGraphProperties *) $1;
+ lp->location = @1;
+
+ $$ = list_make1(lp);
+ }
+ | label_and_properties_list
+ {
+ $$ = $1;
+ }
+ | /*EMPTY*/
+ {
+ PropGraphLabelAndProperties *lp = makeNode(PropGraphLabelAndProperties);
+ PropGraphProperties *pr = makeNode(PropGraphProperties);
+
+ pr->all = true;
+ pr->location = -1;
+ lp->properties = pr;
+ lp->location = -1;
+
+ $$ = list_make1(lp);
+ }
+ ;
+
+element_table_properties:
+ NO PROPERTIES
+ {
+ PropGraphProperties *pr = makeNode(PropGraphProperties);
+
+ pr->properties = NIL;
+ pr->location = @1;
+
+ $$ = (Node *) pr;
+ }
+ | PROPERTIES ALL COLUMNS
+ /*| PROPERTIES ARE ALL COLUMNS */
+ {
+ PropGraphProperties *pr = makeNode(PropGraphProperties);
+
+ pr->all = true;
+ pr->location = @1;
+
+ $$ = (Node *) pr;
+ }
+ | PROPERTIES '(' xml_attribute_list ')'
+ {
+ PropGraphProperties *pr = makeNode(PropGraphProperties);
+
+ pr->properties = $3;
+ pr->location = @1;
+
+ $$ = (Node *) pr;
+ }
+ ;
+
+label_and_properties_list:
+ label_and_properties
+ {
+ $$ = list_make1($1);
+ }
+ | label_and_properties_list label_and_properties
+ {
+ $$ = lappend($1, $2);
+ }
+ ;
+
+label_and_properties:
+ element_table_label_clause
+ {
+ PropGraphLabelAndProperties *lp = makeNode(PropGraphLabelAndProperties);
+ PropGraphProperties *pr = makeNode(PropGraphProperties);
+
+ pr->all = true;
+ pr->location = -1;
+
+ lp->label = $1;
+ lp->properties = pr;
+ lp->location = @1;
+
+ $$ = (Node *) lp;
+ }
+ | element_table_label_clause element_table_properties
+ {
+ PropGraphLabelAndProperties *lp = makeNode(PropGraphLabelAndProperties);
+
+ lp->label = $1;
+ lp->properties = (PropGraphProperties *) $2;
+ lp->location = @1;
+
+ $$ = (Node *) lp;
+ }
+ ;
+
+element_table_label_clause:
+ LABEL name
+ {
+ $$ = $2;
+ }
+ | DEFAULT LABEL
+ {
+ $$ = NULL;
+ }
+ ;
+
+AlterPropGraphStmt:
+ ALTER PROPERTY GRAPH qualified_name ADD_P vertex_tables_clause
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+
+ n->pgname = $4;
+ n->add_vertex_tables = $6;
+
+ $$ = (Node *) n;
+ }
+ | ALTER PROPERTY GRAPH qualified_name ADD_P vertex_tables_clause ADD_P edge_tables_clause
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+
+ n->pgname = $4;
+ n->add_vertex_tables = $6;
+ n->add_edge_tables = $8;
+
+ $$ = (Node *) n;
+ }
+ | ALTER PROPERTY GRAPH qualified_name ADD_P edge_tables_clause
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+
+ n->pgname = $4;
+ n->add_edge_tables = $6;
+
+ $$ = (Node *) n;
+ }
+ | ALTER PROPERTY GRAPH qualified_name DROP vertex_synonym TABLES '(' name_list ')' opt_drop_behavior
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+
+ n->pgname = $4;
+ n->drop_vertex_tables = $9;
+ n->drop_behavior = $11;
+
+ $$ = (Node *) n;
+ }
+ | ALTER PROPERTY GRAPH qualified_name DROP edge_synonym TABLES '(' name_list ')' opt_drop_behavior
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+
+ n->pgname = $4;
+ n->drop_edge_tables = $9;
+ n->drop_behavior = $11;
+
+ $$ = (Node *) n;
+ }
+ | ALTER PROPERTY GRAPH qualified_name ALTER vertex_or_edge TABLE name
+ add_label_list
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+
+ n->pgname = $4;
+ n->element_kind = $6;
+ n->element_alias = $8;
+ n->add_labels = $9;
+
+ $$ = (Node *) n;
+ }
+ | ALTER PROPERTY GRAPH qualified_name ALTER vertex_or_edge TABLE name
+ DROP LABEL name opt_drop_behavior
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+
+ n->pgname = $4;
+ n->element_kind = $6;
+ n->element_alias = $8;
+ n->drop_label = $11;
+ n->drop_behavior = $12;
+
+ $$ = (Node *) n;
+ }
+ | ALTER PROPERTY GRAPH qualified_name ALTER vertex_or_edge TABLE name
+ ALTER LABEL name ADD_P PROPERTIES '(' xml_attribute_list ')'
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+ PropGraphProperties *pr = makeNode(PropGraphProperties);
+
+ n->pgname = $4;
+ n->element_kind = $6;
+ n->element_alias = $8;
+ n->alter_label = $11;
+
+ pr->properties = $15;
+ pr->location = @13;
+ n->add_properties = pr;
+
+ $$ = (Node *) n;
+ }
+ | ALTER PROPERTY GRAPH qualified_name ALTER vertex_or_edge TABLE name
+ ALTER LABEL name DROP PROPERTIES '(' name_list ')' opt_drop_behavior
+ {
+ AlterPropGraphStmt *n = makeNode(AlterPropGraphStmt);
+
+ n->pgname = $4;
+ n->element_kind = $6;
+ n->element_alias = $8;
+ n->alter_label = $11;
+ n->drop_properties = $15;
+ n->drop_behavior = $17;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+vertex_or_edge:
+ vertex_synonym { $$ = PROPGRAPH_ELEMENT_KIND_VERTEX; }
+ | edge_synonym { $$ = PROPGRAPH_ELEMENT_KIND_EDGE; }
+ ;
+
+add_label_list:
+ add_label { $$ = list_make1($1); }
+ | add_label_list add_label { $$ = lappend($1, $2); }
+ ;
+
+add_label: ADD_P LABEL name element_table_properties
+ {
+ PropGraphLabelAndProperties *lp = makeNode(PropGraphLabelAndProperties);
+
+ lp->label = $3;
+ lp->properties = (PropGraphProperties *) $4;
+ lp->location = @1;
+
+ $$ = (Node *) lp;
+ }
+ ;
+
+
/*****************************************************************************
*
* CREATE TRANSFORM / DROP TRANSFORM
@@ -9365,6 +9774,16 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
n->missing_ok = false;
$$ = (Node *) n;
}
+ | ALTER PROPERTY GRAPH qualified_name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+
+ n->renameType = OBJECT_PROPGRAPH;
+ n->relation = $4;
+ n->newname = $7;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
| ALTER PUBLICATION name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
@@ -9990,6 +10409,26 @@ AlterObjectSchemaStmt:
n->missing_ok = false;
$$ = (Node *) n;
}
+ | ALTER PROPERTY GRAPH qualified_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+
+ n->objectType = OBJECT_PROPGRAPH;
+ n->relation = $4;
+ n->newschema = $7;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ | ALTER PROPERTY GRAPH IF_P EXISTS qualified_name SET SCHEMA name
+ {
+ AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+
+ n->objectType = OBJECT_PROPGRAPH;
+ n->relation = $6;
+ n->newschema = $9;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
| ALTER ROUTINE function_with_argtypes SET SCHEMA name
{
AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
@@ -10333,6 +10772,15 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
n->newowner = $6;
$$ = (Node *) n;
}
+ | ALTER PROPERTY GRAPH qualified_name OWNER TO RoleSpec
+ {
+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+
+ n->objectType = OBJECT_PROPGRAPH;
+ n->relation = $4;
+ n->newowner = $7;
+ $$ = (Node *) n;
+ }
| ALTER ROUTINE function_with_argtypes OWNER TO RoleSpec
{
AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
@@ -13409,6 +13857,17 @@ table_ref: relation_expr opt_alias_clause
n->alias = $3;
$$ = (Node *) n;
}
+ | GRAPH_TABLE '(' qualified_name MATCH graph_pattern COLUMNS '(' xml_attribute_list ')' ')' opt_alias_clause
+ {
+ RangeGraphTable *n = makeNode(RangeGraphTable);
+
+ n->graph_name = $3;
+ n->graph_pattern = castNode(GraphPattern, $5);
+ n->columns = $8;
+ n->alias = $11;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
| select_with_parens opt_alias_clause
{
RangeSubselect *n = makeNode(RangeSubselect);
@@ -14590,6 +15049,10 @@ a_expr: c_expr { $$ = $1; }
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $3, @2); }
| a_expr NOT_EQUALS a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<>", $1, $3, @2); }
+ | a_expr LEFT_ARROW a_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<-", $1, $3, @2); }
+ | a_expr RIGHT_ARROW a_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "->", $1, $3, @2); }
| a_expr qual_Op a_expr %prec Op
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
@@ -15069,6 +15532,10 @@ b_expr: c_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $3, @2); }
| b_expr NOT_EQUALS b_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<>", $1, $3, @2); }
+ | b_expr LEFT_ARROW b_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<-", $1, $3, @2); }
+ | b_expr RIGHT_ARROW b_expr
+ { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "->", $1, $3, @2); }
| b_expr qual_Op b_expr %prec Op
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
| qual_Op b_expr %prec Op
@@ -16173,6 +16640,8 @@ MathOp: '+' { $$ = "+"; }
| LESS_EQUALS { $$ = "<="; }
| GREATER_EQUALS { $$ = ">="; }
| NOT_EQUALS { $$ = "<>"; }
+ | LEFT_ARROW { $$ = "<-"; }
+ | RIGHT_ARROW { $$ = "->"; }
;
qual_Op: Op
@@ -16684,6 +17153,192 @@ json_array_aggregate_order_by_clause_opt:
| /* EMPTY */ { $$ = NIL; }
;
+
+/*****************************************************************************
+ *
+ * graph patterns
+ *
+ *****************************************************************************/
+
+graph_pattern:
+ path_pattern_list where_clause
+ {
+ GraphPattern *gp = makeNode(GraphPattern);
+
+ gp->path_pattern_list = $1;
+ gp->whereClause = $2;
+ $$ = (Node *) gp;
+ }
+ ;
+
+path_pattern_list:
+ path_pattern { $$ = list_make1($1); }
+ | path_pattern_list ',' path_pattern { $$ = lappend($1, $3); }
+ ;
+
+path_pattern:
+ path_pattern_expression { $$ = $1; }
+ ;
+
+/*
+ * path pattern expression
+ */
+
+path_pattern_expression:
+ path_term { $$ = $1; }
+ /* | path_multiset_alternation */
+ /* | path_pattern_union */
+ ;
+
+path_term:
+ path_factor { $$ = list_make1($1); }
+ | path_term path_factor { $$ = lappend($1, $2); }
+ ;
+
+path_factor:
+ path_primary opt_graph_pattern_quantifier
+ {
+ GraphElementPattern *gep = (GraphElementPattern *) $1;
+
+ gep->quantifier = $2;
+ }
+ ;
+
+path_primary:
+ '(' opt_colid opt_is_label_expression where_clause ')'
+ {
+ GraphElementPattern *gep = makeNode(GraphElementPattern);
+
+ gep->kind = VERTEX_PATTERN;
+ gep->variable = $2;
+ gep->labelexpr = $3;
+ gep->whereClause = $4;
+ gep->location = @1;
+
+ $$ = (Node *) gep;
+ }
+ /* full edge pointing left: <-[ xxx ]- */
+ | LEFT_ARROW_BRACKET opt_colid opt_is_label_expression where_clause RIGHT_BRACKET_MINUS
+ {
+ GraphElementPattern *gep = makeNode(GraphElementPattern);
+
+ gep->kind = EDGE_PATTERN_LEFT;
+ gep->variable = $2;
+ gep->labelexpr = $3;
+ gep->whereClause = $4;
+ gep->location = @1;
+
+ $$ = (Node *) gep;
+ }
+ /* full edge pointing right: -[ xxx ]-> */
+ | MINUS_LEFT_BRACKET opt_colid opt_is_label_expression where_clause BRACKET_RIGHT_ARROW
+ {
+ GraphElementPattern *gep = makeNode(GraphElementPattern);
+
+ gep->kind = EDGE_PATTERN_RIGHT;
+ gep->variable = $2;
+ gep->labelexpr = $3;
+ gep->whereClause = $4;
+ gep->location = @1;
+
+ $$ = (Node *) gep;
+ }
+ /* full edge any direction: -[ xxx ]- */
+ | MINUS_LEFT_BRACKET opt_colid opt_is_label_expression where_clause RIGHT_BRACKET_MINUS
+ {
+ GraphElementPattern *gep = makeNode(GraphElementPattern);
+
+ gep->kind = EDGE_PATTERN_ANY;
+ gep->variable = $2;
+ gep->labelexpr = $3;
+ gep->whereClause = $4;
+ gep->location = @1;
+
+ $$ = (Node *) gep;
+ }
+ /* abbreviated edge patterns */
+ | LEFT_ARROW
+ {
+ GraphElementPattern *gep = makeNode(GraphElementPattern);
+
+ gep->kind = EDGE_PATTERN_LEFT;
+ gep->location = @1;
+
+ $$ = (Node *) gep;
+ }
+ | RIGHT_ARROW
+ {
+ GraphElementPattern *gep = makeNode(GraphElementPattern);
+
+ gep->kind = EDGE_PATTERN_RIGHT;
+ gep->location = @1;
+
+ $$ = (Node *) gep;
+ }
+ | '-'
+ {
+ GraphElementPattern *gep = makeNode(GraphElementPattern);
+
+ gep->kind = EDGE_PATTERN_ANY;
+ gep->location = @1;
+
+ $$ = (Node *) gep;
+ }
+ | '(' path_pattern_expression where_clause ')'
+ {
+ GraphElementPattern *gep = makeNode(GraphElementPattern);
+
+ gep->kind = PAREN_EXPR;
+ gep->subexpr = $2;
+ gep->whereClause = $3;
+ gep->location = @1;
+
+ $$ = (Node *) gep;
+ }
+ ;
+
+opt_colid:
+ ColId { $$ = $1; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+opt_is_label_expression:
+ IS label_expression { $$ = $2; }
+ | ':' label_expression { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+/*
+ * graph pattern quantifier
+ */
+
+opt_graph_pattern_quantifier:
+ LEFT_BRACE Iconst RIGHT_BRACE { $$ = list_make2_int($2, $2); }
+ | LEFT_BRACE ',' Iconst RIGHT_BRACE { $$ = list_make2_int(0, $3); }
+ | LEFT_BRACE Iconst ',' Iconst RIGHT_BRACE { $$ = list_make2_int($2, $4); }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+/*
+ * label expression
+ */
+
+label_expression:
+ label_term
+ | label_disjunction
+ ;
+
+label_disjunction:
+ label_expression '|' label_term
+ { $$ = makeOrExpr($1, $3, @2); }
+ ;
+
+label_term:
+ name
+ { $$ = makeColumnRef($1, NIL, @1, yyscanner); }
+ ;
+
+
/*****************************************************************************
*
* target list for SELECT
@@ -17202,6 +17857,7 @@ unreserved_keyword:
| DELIMITERS
| DEPENDS
| DEPTH
+ | DESTINATION
| DETACH
| DICTIONARY
| DISABLE_P
@@ -17211,6 +17867,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | EDGE
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -17238,6 +17895,7 @@ unreserved_keyword:
| GENERATED
| GLOBAL
| GRANTED
+ | GRAPH
| GROUPS
| HANDLER
| HEADER_P
@@ -17300,6 +17958,7 @@ unreserved_keyword:
| NFKC
| NFKD
| NO
+ | NODE
| NORMALIZED
| NOTHING
| NOTIFY
@@ -17338,6 +17997,8 @@ unreserved_keyword:
| PROCEDURE
| PROCEDURES
| PROGRAM
+ | PROPERTIES
+ | PROPERTY
| PUBLICATION
| QUOTE
| RANGE
@@ -17349,6 +18010,7 @@ unreserved_keyword:
| REFERENCING
| REFRESH
| REINDEX
+ | RELATIONSHIP
| RELATIVE_P
| RELEASE
| RENAME
@@ -17388,6 +18050,7 @@ unreserved_keyword:
| SIMPLE
| SKIP
| SNAPSHOT
+ | SOURCE
| SQL_P
| STABLE
| STANDALONE_P
@@ -17434,6 +18097,7 @@ unreserved_keyword:
| VALUE_P
| VARYING
| VERSION_P
+ | VERTEX
| VIEW
| VIEWS
| VOLATILE
@@ -17472,6 +18136,7 @@ col_name_keyword:
| EXISTS
| EXTRACT
| FLOAT_P
+ | GRAPH_TABLE
| GREATEST
| GROUPING
| INOUT
@@ -17757,6 +18422,7 @@ bare_label_keyword:
| DEPENDS
| DEPTH
| DESC
+ | DESTINATION
| DETACH
| DICTIONARY
| DISABLE_P
@@ -17768,6 +18434,7 @@ bare_label_keyword:
| DOUBLE_P
| DROP
| EACH
+ | EDGE
| ELSE
| ENABLE_P
| ENCODING
@@ -17803,6 +18470,8 @@ bare_label_keyword:
| GENERATED
| GLOBAL
| GRANTED
+ | GRAPH
+ | GRAPH_TABLE
| GREATEST
| GROUPING
| GROUPS
@@ -17891,6 +18560,7 @@ bare_label_keyword:
| NFKC
| NFKD
| NO
+ | NODE
| NONE
| NORMALIZE
| NORMALIZED
@@ -17942,6 +18612,8 @@ bare_label_keyword:
| PROCEDURE
| PROCEDURES
| PROGRAM
+ | PROPERTIES
+ | PROPERTY
| PUBLICATION
| QUOTE
| RANGE
@@ -17955,6 +18627,7 @@ bare_label_keyword:
| REFERENCING
| REFRESH
| REINDEX
+ | RELATIONSHIP
| RELATIVE_P
| RELEASE
| RENAME
@@ -18001,6 +18674,7 @@ bare_label_keyword:
| SMALLINT
| SNAPSHOT
| SOME
+ | SOURCE
| SQL_P
| STABLE
| STANDALONE_P
@@ -18065,6 +18739,7 @@ bare_label_keyword:
| VARIADIC
| VERBOSE
| VERSION_P
+ | VERTEX
| VIEW
| VIEWS
| VOLATILE
diff --git a/src/backend/parser/meson.build b/src/backend/parser/meson.build
index 8e8295640b..4c03346dec 100644
--- a/src/backend/parser/meson.build
+++ b/src/backend/parser/meson.build
@@ -10,6 +10,7 @@ backend_sources += files(
'parse_enr.c',
'parse_expr.c',
'parse_func.c',
+ 'parse_graphtable.c',
'parse_merge.c',
'parse_node.c',
'parse_oper.c',
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index d2ac86777c..69261b9ea8 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -35,6 +35,7 @@
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
+#include "parser/parse_graphtable.h"
#include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
@@ -67,6 +68,8 @@ static ParseNamespaceItem *transformRangeFunction(ParseState *pstate,
RangeFunction *r);
static ParseNamespaceItem *transformRangeTableFunc(ParseState *pstate,
RangeTableFunc *rtf);
+static ParseNamespaceItem *transformRangeGraphTable(ParseState *pstate,
+ RangeGraphTable *rgt);
static TableSampleClause *transformRangeTableSample(ParseState *pstate,
RangeTableSample *rts);
static ParseNamespaceItem *getNSItemForSpecialRelationTypes(ParseState *pstate,
@@ -896,6 +899,76 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
tf, rtf->alias, is_lateral, true);
}
+/*
+ * transformRangeGraphTable -- transform a GRAPH_TABLE clause
+ */
+static ParseNamespaceItem *
+transformRangeGraphTable(ParseState *pstate, RangeGraphTable *rgt)
+{
+ Relation rel;
+ Oid graphid;
+ ParseState *pstate2;
+ GraphTableParseState *gpstate = palloc0_object(GraphTableParseState);
+ Node *gp;
+ List *columns = NIL;
+ List *colnames = NIL;
+ ListCell *lc;
+ int resno = 0;
+
+ rel = parserOpenTable(pstate, rgt->graph_name, AccessShareLock);
+ if (rel->rd_rel->relkind != RELKIND_PROPGRAPH)
+ ereport(ERROR,
+ errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a property graph",
+ RelationGetRelationName(rel)),
+ parser_errposition(pstate, rgt->graph_name->location));
+
+ graphid = RelationGetRelid(rel);
+
+ gpstate->graphid = graphid;
+
+ gp = transformGraphPattern(gpstate, rgt->graph_pattern);
+
+ pstate2 = make_parsestate(NULL);
+ pstate2->p_pre_columnref_hook = graph_table_property_reference;
+ pstate2->p_ref_hook_state = gpstate;
+
+ foreach(lc, rgt->columns)
+ {
+ ResTarget *rt = lfirst_node(ResTarget, lc);
+ Node *colexpr;
+ TargetEntry *te;
+ char *colname;
+
+ colexpr = transformExpr(pstate2, rt->val, EXPR_KIND_OTHER);
+
+ if (rt->name)
+ colname = rt->name;
+ else
+ {
+ if (IsA(colexpr, GraphPropertyRef))
+ colname = pstrdup(castNode(GraphPropertyRef, colexpr)->propname);
+ else
+ {
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("complex graph table column must specify an explicit column name"),
+ parser_errposition(pstate, rt->location));
+ colname = NULL;
+ }
+ }
+
+ colnames = lappend(colnames, makeString(colname));
+
+ te = makeTargetEntry((Expr *) colexpr, ++resno, colname, false);
+ columns = lappend(columns, te);
+ }
+
+ table_close(rel, NoLock);
+
+ return addRangeTableEntryForGraphTable(pstate, graphid, castNode(GraphPattern, gp), columns, colnames, rgt->alias, false, true);
+}
+
/*
* transformRangeTableSample --- transform a TABLESAMPLE clause
*
@@ -1115,6 +1188,18 @@ transformFromClauseItem(ParseState *pstate, Node *n,
rtr->rtindex = nsitem->p_rtindex;
return (Node *) rtr;
}
+ else if (IsA(n, RangeGraphTable))
+ {
+ RangeTblRef *rtr;
+ ParseNamespaceItem *nsitem;
+
+ nsitem = transformRangeGraphTable(pstate, (RangeGraphTable *) n);
+ *top_nsitem = nsitem;
+ *namespace = list_make1(nsitem);
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = nsitem->p_rtindex;
+ return (Node *) rtr;
+ }
else if (IsA(n, RangeTableSample))
{
/* TABLESAMPLE clause (wrapping some other valid FROM node) */
diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c
index 44529bb49e..33a1f3f241 100644
--- a/src/backend/parser/parse_collate.c
+++ b/src/backend/parser/parse_collate.c
@@ -571,6 +571,13 @@ assign_collations_walker(Node *node, assign_collations_context *context)
location = exprLocation(node);
break;
+ case T_GraphPropertyRef:
+ /* FIXME */
+ collation = DEFAULT_COLLATION_OID;
+ strength = COLLATE_IMPLICIT;
+ location = -1;
+ break;
+
default:
{
/*
diff --git a/src/backend/parser/parse_graphtable.c b/src/backend/parser/parse_graphtable.c
new file mode 100644
index 0000000000..8679b72627
--- /dev/null
+++ b/src/backend/parser/parse_graphtable.c
@@ -0,0 +1,251 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_graphtable.c
+ * parsing of GRAPH_TABLE
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/parser/parse_graphtable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/htup_details.h"
+#include "access/table.h"
+#include "catalog/pg_propgraph_property.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "parser/parse_collate.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_graphtable.h"
+#include "parser/parse_node.h"
+#include "utils/fmgroids.h"
+#include "utils/relcache.h"
+
+
+/*
+ * Get the type of a property.
+ */
+static Oid
+get_property_type(Oid graphid, const char *propname)
+{
+ Relation rel;
+ SysScanDesc scan;
+ ScanKeyData key[2];
+ HeapTuple tuple;
+ Oid result = InvalidOid;
+
+ rel = table_open(PropgraphPropertyRelationId, RowShareLock);
+ ScanKeyInit(&key[0],
+ Anum_pg_propgraph_property_pgppgid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(graphid));
+ ScanKeyInit(&key[1],
+ Anum_pg_propgraph_property_pgpname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(propname));
+
+ scan = systable_beginscan(rel, PropgraphPropertyGraphNameIndexId, true, NULL, 2, key);
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ {
+ Form_pg_propgraph_property prop = (Form_pg_propgraph_property) GETSTRUCT(tuple);
+
+ result = prop->pgptypid;
+ break;
+ }
+
+ systable_endscan(scan);
+ table_close(rel, RowShareLock);
+
+ if (!result)
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("property \"%s\" does not exist", propname));
+
+ return result;
+}
+
+/*
+ * Resolve a property reference.
+ */
+Node *
+graph_table_property_reference(ParseState *pstate, ColumnRef *cref)
+{
+ GraphTableParseState *gpstate = pstate->p_ref_hook_state;
+ GraphPropertyRef *gpr = makeNode(GraphPropertyRef);
+
+ gpr->location = cref->location;
+
+ if (list_length(cref->fields) == 2)
+ {
+ Node *field1 = linitial(cref->fields);
+ Node *field2 = lsecond(cref->fields);
+ char *elvarname;
+ char *propname;
+
+ elvarname = strVal(field1);
+ propname = strVal(field2);
+
+ if (!list_member(gpstate->variables, field1))
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("graph pattern variable \"%s\" does not exist", elvarname),
+ parser_errposition(pstate, cref->location));
+
+ gpr->elvarname = elvarname;
+ gpr->propname = propname;
+
+ }
+ else
+ elog(ERROR, "invalid property reference");
+
+ gpr->typeId = get_property_type(gpstate->graphid, gpr->propname);
+
+ return (Node *) gpr;
+}
+
+/*
+ * Transform a label expression.
+ */
+static Node *
+transformLabelExpr(GraphTableParseState *gpstate, Node *labelexpr)
+{
+ Node *result;
+
+ if (labelexpr == NULL)
+ return NULL;
+
+ check_stack_depth();
+
+ switch (nodeTag(labelexpr))
+ {
+ case T_ColumnRef:
+ {
+ ColumnRef *cref = (ColumnRef *) labelexpr;
+ const char *labelname;
+ GraphLabelRef *lref;
+
+ Assert(list_length(cref->fields) == 1);
+ labelname = strVal(linitial(cref->fields));
+
+ lref = makeNode(GraphLabelRef);
+ lref->labelname = labelname;
+ lref->location = cref->location;
+
+ result = (Node *) lref;
+ break;
+ }
+
+ case T_BoolExpr:
+ {
+ BoolExpr *be = (BoolExpr *) labelexpr;
+ ListCell *lc;
+ List *args = NIL;
+
+ foreach(lc, be->args)
+ {
+ Node *arg = (Node *) lfirst(lc);
+
+ arg = transformLabelExpr(gpstate, arg);
+ args = lappend(args, arg);
+ }
+
+ result = (Node *) makeBoolExpr(be->boolop, args, be->location);
+ break;
+ }
+
+ default:
+ /* should not reach here */
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(labelexpr));
+ result = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * Transform a GraphElementPattern.
+ */
+static Node *
+transformGraphElementPattern(GraphTableParseState *gpstate, GraphElementPattern *gep)
+{
+ ParseState *pstate2;
+
+ pstate2 = make_parsestate(NULL);
+ pstate2->p_pre_columnref_hook = graph_table_property_reference;
+ pstate2->p_ref_hook_state = gpstate;
+
+ if (gep->variable)
+ gpstate->variables = lappend(gpstate->variables, makeString(pstrdup(gep->variable)));
+
+ gep->labelexpr = transformLabelExpr(gpstate, gep->labelexpr);
+
+ gep->whereClause = transformExpr(pstate2, gep->whereClause, EXPR_KIND_OTHER);
+ assign_expr_collations(pstate2, gep->whereClause);
+
+ return (Node *) gep;
+}
+
+/*
+ * Transform a path term (list of GraphElementPattern's).
+ */
+static Node *
+transformPathTerm(GraphTableParseState *gpstate, List *path_term)
+{
+ List *result = NIL;
+ ListCell *lc;
+
+ foreach(lc, path_term)
+ {
+ Node *n = transformGraphElementPattern(gpstate, lfirst_node(GraphElementPattern, lc));
+
+ result = lappend(result, n);
+ }
+
+ return (Node *) result;
+}
+
+/*
+ * Transform a path pattern list (list of path terms).
+ */
+static Node *
+transformPathPatternList(GraphTableParseState *gpstate, List *path_pattern)
+{
+ List *result = NIL;
+ ListCell *lc;
+
+ foreach(lc, path_pattern)
+ {
+ Node *n = transformPathTerm(gpstate, lfirst(lc));
+
+ result = lappend(result, n);
+ }
+
+ return (Node *) result;
+}
+
+/*
+ * Transform a GraphPattern.
+ */
+Node *
+transformGraphPattern(GraphTableParseState *gpstate, GraphPattern *graph_pattern)
+{
+ ParseState *pstate2;
+
+ pstate2 = make_parsestate(NULL);
+ pstate2->p_pre_columnref_hook = graph_table_property_reference;
+ pstate2->p_ref_hook_state = gpstate;
+
+ graph_pattern->path_pattern_list = (List *) transformPathPatternList(gpstate, graph_pattern->path_pattern_list);
+ graph_pattern->whereClause = transformExpr(pstate2, graph_pattern->whereClause, EXPR_KIND_OTHER);
+ assign_expr_collations(pstate2, graph_pattern->whereClause);
+
+ return (Node *) graph_pattern;
+}
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 34a0ec5901..c4b2b61390 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -2127,6 +2127,98 @@ addRangeTableEntryForTableFunc(ParseState *pstate,
rte->colcollations);
}
+ParseNamespaceItem *
+addRangeTableEntryForGraphTable(ParseState *pstate,
+ Oid graphid,
+ GraphPattern *graph_pattern,
+ List *columns,
+ List *colnames,
+ Alias *alias,
+ bool lateral,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ char *refname = alias ? alias->aliasname : pstrdup("graph_table");
+ Alias *eref;
+ int numaliases;
+ int varattno;
+ ListCell *lc;
+ List *coltypes = NIL;
+ List *coltypmods = NIL;
+ List *colcollations = NIL;
+ RTEPermissionInfo *perminfo;
+ ParseNamespaceItem *nsitem;
+
+ Assert(pstate != NULL);
+
+ rte->rtekind = RTE_GRAPH_TABLE;
+ rte->relid = graphid;
+ rte->relkind = RELKIND_PROPGRAPH;
+ rte->graph_pattern = graph_pattern;
+ rte->graph_table_columns = columns;
+ rte->alias = alias;
+
+ eref = alias ? copyObject(alias) : makeAlias(refname, NIL);
+
+ if (!eref->colnames)
+ eref->colnames = colnames;
+
+ numaliases = list_length(eref->colnames);
+
+ /* fill in any unspecified alias columns */
+ varattno = 0;
+ foreach(lc, colnames)
+ {
+ varattno++;
+ if (varattno > numaliases)
+ eref->colnames = lappend(eref->colnames, lfirst(lc));
+ }
+ if (varattno < numaliases)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("GRAPH_TABLE \"%s\" has %d columns available but %d columns specified",
+ refname, varattno, numaliases)));
+
+ rte->eref = eref;
+
+ foreach(lc, columns)
+ {
+ TargetEntry *te = lfirst_node(TargetEntry, lc);
+ Node *colexpr = (Node *) te->expr;
+
+ coltypes = lappend_oid(coltypes, exprType(colexpr));
+ coltypmods = lappend_int(coltypmods, exprTypmod(colexpr));
+ colcollations = lappend_oid(colcollations, exprCollation(colexpr));
+ }
+
+ /*
+ * Set flags and access permissions.
+ */
+ rte->lateral = lateral;
+ rte->inFromCl = inFromCl;
+
+ perminfo = addRTEPermissionInfo(&pstate->p_rteperminfos, rte);
+ perminfo->requiredPerms = ACL_SELECT;
+
+ /*
+ * Add completed RTE to pstate's range table list, so that we know its
+ * index. But we don't add it to the join list --- caller must do that if
+ * appropriate.
+ */
+ pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+ /*
+ * Build a ParseNamespaceItem, but don't add it to the pstate's namespace
+ * list --- caller must do that if appropriate.
+ */
+ nsitem = buildNSItemFromLists(rte, list_length(pstate->p_rtable),
+ coltypes, coltypmods, colcollations);
+
+ nsitem->p_perminfo = perminfo;
+
+ return nsitem;
+}
+
/*
* Add an entry for a VALUES list to the pstate's range table (p_rtable).
* Then, construct and return a ParseNamespaceItem for the new RTE.
@@ -2941,6 +3033,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
case RTE_VALUES:
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
+ case RTE_GRAPH_TABLE:
{
/* Tablefunc, Values, CTE, or ENR RTE */
ListCell *aliasp_item = list_head(rte->eref->colnames);
@@ -3318,6 +3411,7 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
case RTE_TABLEFUNC:
case RTE_VALUES:
case RTE_CTE:
+ case RTE_GRAPH_TABLE:
/*
* Subselect, Table Functions, Values, CTE RTEs never have dropped
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index f10fc420e6..ce839ef540 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -360,6 +360,10 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
tle->resorigtbl = rte->relid;
tle->resorigcol = attnum;
break;
+ case RTE_GRAPH_TABLE:
+ tle->resorigtbl = rte->relid;
+ tle->resorigcol = InvalidAttrNumber;
+ break;
case RTE_SUBQUERY:
/* Subselect-in-FROM: copy up from the subselect */
if (attnum != InvalidAttrNumber)
@@ -1564,6 +1568,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
case RTE_RELATION:
case RTE_VALUES:
case RTE_NAMEDTUPLESTORE:
+ case RTE_GRAPH_TABLE:
case RTE_RESULT:
/*
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index 5eaadf53b2..a8a5baef09 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -353,6 +353,11 @@ typecast "::"
dot_dot \.\.
colon_equals ":="
+bracket_right_arrow "]->"
+left_arrow_bracket "<-["
+minus_left_bracket "-["
+right_bracket_minus "]-"
+
/*
* These operator-like tokens (unlike the above ones) also match the {operator}
* rule, which means that they might be overridden by a longer match if they
@@ -367,6 +372,9 @@ greater_equals ">="
less_greater "<>"
not_equals "!="
+left_arrow "<-"
+right_arrow "->"
+
/*
* "self" is the set of chars that should be returned as single-character
* tokens. "op_chars" is the set of chars that can make up "Op" tokens,
@@ -850,6 +858,26 @@ other .
return COLON_EQUALS;
}
+{bracket_right_arrow} {
+ SET_YYLLOC();
+ return BRACKET_RIGHT_ARROW;
+ }
+
+{left_arrow_bracket} {
+ SET_YYLLOC();
+ return LEFT_ARROW_BRACKET;
+ }
+
+{minus_left_bracket} {
+ SET_YYLLOC();
+ return MINUS_LEFT_BRACKET;
+ }
+
+{right_bracket_minus} {
+ SET_YYLLOC();
+ return RIGHT_BRACKET_MINUS;
+ }
+
{equals_greater} {
SET_YYLLOC();
return EQUALS_GREATER;
@@ -877,11 +905,31 @@ other .
return NOT_EQUALS;
}
+{left_arrow} {
+ SET_YYLLOC();
+ return LEFT_ARROW;
+ }
+
+{right_arrow} {
+ SET_YYLLOC();
+ return RIGHT_ARROW;
+ }
+
{self} {
SET_YYLLOC();
return yytext[0];
}
+\{ {
+ SET_YYLLOC();
+ return LEFT_BRACE;
+ }
+
+\} {
+ SET_YYLLOC();
+ return RIGHT_BRACE;
+ }
+
{operator} {
/*
* Check for embedded slash-star or dash-dash; those
diff --git a/src/backend/rewrite/Makefile b/src/backend/rewrite/Makefile
index 4680752e6a..09070047b7 100644
--- a/src/backend/rewrite/Makefile
+++ b/src/backend/rewrite/Makefile
@@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = \
rewriteDefine.o \
+ rewriteGraphTable.o \
rewriteHandler.o \
rewriteManip.o \
rewriteRemove.o \
diff --git a/src/backend/rewrite/meson.build b/src/backend/rewrite/meson.build
index 23043ca6e5..2bea20233a 100644
--- a/src/backend/rewrite/meson.build
+++ b/src/backend/rewrite/meson.build
@@ -2,6 +2,7 @@
backend_sources += files(
'rewriteDefine.c',
+ 'rewriteGraphTable.c',
'rewriteHandler.c',
'rewriteManip.c',
'rewriteRemove.c',
diff --git a/src/backend/rewrite/rewriteGraphTable.c b/src/backend/rewrite/rewriteGraphTable.c
new file mode 100644
index 0000000000..94f1be3de5
--- /dev/null
+++ b/src/backend/rewrite/rewriteGraphTable.c
@@ -0,0 +1,422 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteGraphTable.c
+ * Support for rewriting GRAPH_TABLE clauses.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/rewrite/rewriteGraphTable.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/table.h"
+#include "catalog/pg_propgraph_element.h"
+#include "catalog/pg_propgraph_label.h"
+#include "catalog/pg_propgraph_property.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parsetree.h"
+#include "rewrite/rewriteGraphTable.h"
+#include "rewrite/rewriteHandler.h"
+#include "rewrite/rewriteManip.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+static Oid get_labelid(Oid graphid, const char *labelname);
+static List *get_elements_for_label(Oid graphid, const char *labelname);
+static Oid get_table_for_element(Oid elid);
+static Node *replace_property_refs(Node *node, const List *mappings);
+static List *build_edge_vertex_link_quals(HeapTuple edgetup, int edgerti, int refrti, AttrNumber catalog_key_attnum, AttrNumber catalog_ref_attnum);
+
+struct elvar_rt_mapping
+{
+ const char *elvarname;
+ Oid labelid;
+ int rt_index;
+};
+
+
+/*
+ * Convert GRAPH_TABLE clause into a subquery using relational
+ * operators.
+ */
+Query *
+rewriteGraphTable(Query *parsetree, int rt_index)
+{
+ RangeTblEntry *rte;
+ Query *newsubquery;
+ ListCell *lc;
+ List *element_patterns;
+ List *element_ids = NIL;
+ List *fromlist = NIL;
+ List *qual_exprs = NIL;
+ List *elvar_rt_mappings = NIL;
+
+ rte = rt_fetch(rt_index, parsetree->rtable);
+
+ newsubquery = makeNode(Query);
+ newsubquery->commandType = CMD_SELECT;
+
+ if (list_length(rte->graph_pattern->path_pattern_list) != 1)
+ elog(ERROR, "unsupported path pattern list length");
+
+ element_patterns = linitial(rte->graph_pattern->path_pattern_list);
+
+ foreach(lc, element_patterns)
+ {
+ GraphElementPattern *gep = lfirst_node(GraphElementPattern, lc);
+ Oid labelid = InvalidOid;
+ struct elvar_rt_mapping *erm;
+
+ if (!(gep->kind == VERTEX_PATTERN || gep->kind == EDGE_PATTERN_LEFT || gep->kind == EDGE_PATTERN_RIGHT))
+ elog(ERROR, "unsupported element pattern kind: %u", gep->kind);
+
+ if (gep->quantifier)
+ elog(ERROR, "element pattern quantifier not supported yet");
+
+ if (IsA(gep->labelexpr, GraphLabelRef))
+ {
+ GraphLabelRef *glr = castNode(GraphLabelRef, gep->labelexpr);
+ RangeTblEntry *r;
+ Oid elid;
+ Oid relid;
+ RTEPermissionInfo *rpi;
+ RangeTblRef *rtr;
+
+ r = makeNode(RangeTblEntry);
+ r->rtekind = RTE_RELATION;
+ elid = linitial_oid(get_elements_for_label(rte->relid, glr->labelname));
+ element_ids = lappend_oid(element_ids, elid);
+ relid = get_table_for_element(elid);
+ labelid = get_labelid(rte->relid, glr->labelname);
+ r->relid = relid;
+ r->relkind = get_rel_relkind(relid);
+ r->rellockmode = AccessShareLock;
+ r->inh = true;
+ newsubquery->rtable = lappend(newsubquery->rtable, r);
+
+ rpi = makeNode(RTEPermissionInfo);
+ rpi->relid = relid;
+ rpi->checkAsUser = 0;
+ rpi->requiredPerms = ACL_SELECT;
+ newsubquery->rteperminfos = lappend(newsubquery->rteperminfos, rpi);
+
+ r->perminfoindex = list_length(newsubquery->rteperminfos);
+
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = list_length(newsubquery->rtable);
+ fromlist = lappend(fromlist, rtr);
+ }
+ else
+ elog(ERROR, "unsupported label expression type: %d", (int) nodeTag(gep->labelexpr));
+
+ erm = palloc0_object(struct elvar_rt_mapping);
+
+ erm->elvarname = gep->variable;
+ erm->labelid = labelid;
+ erm->rt_index = list_length(newsubquery->rtable);
+
+ elvar_rt_mappings = lappend(elvar_rt_mappings, erm);
+
+ if (gep->whereClause)
+ {
+ Node *tr;
+
+ tr = replace_property_refs(gep->whereClause, list_make1(erm));
+
+ qual_exprs = lappend(qual_exprs, tr);
+ }
+ }
+
+ /* Iterate over edges only */
+ for (int k = 1; k < list_length(element_ids); k += 2)
+ {
+ Oid elid = list_nth_oid(element_ids, k);
+ HeapTuple tuple;
+ Form_pg_propgraph_element pgeform;
+ GraphElementPattern *gep = list_nth_node(GraphElementPattern, element_patterns, k);
+ int srcvertexoffset;
+ int destvertexoffset;
+
+ tuple = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(elid));
+ if (!tuple)
+ elog(ERROR, "cache lookup failed for property graph element %u", elid);
+ pgeform = ((Form_pg_propgraph_element) GETSTRUCT(tuple));
+
+ if (gep->kind == EDGE_PATTERN_RIGHT)
+ {
+ srcvertexoffset = -1;
+ destvertexoffset = +1;
+ }
+ else if (gep->kind == EDGE_PATTERN_LEFT)
+ {
+ srcvertexoffset = +1;
+ destvertexoffset = -1;
+ }
+ else
+ {
+ Assert(false);
+ srcvertexoffset = 0;
+ destvertexoffset = 0;
+ }
+
+ /*
+ * source link
+ */
+ if (pgeform->pgesrcvertexid != list_nth_oid(element_ids, k + srcvertexoffset))
+ {
+ qual_exprs = lappend(qual_exprs, makeBoolConst(false, false));
+ }
+ else
+ {
+ qual_exprs = list_concat(qual_exprs,
+ build_edge_vertex_link_quals(tuple, k + 1, k + 1 + srcvertexoffset,
+ Anum_pg_propgraph_element_pgesrckey, Anum_pg_propgraph_element_pgesrcref));
+ }
+
+ /*
+ * dest link
+ */
+ if (pgeform->pgedestvertexid != list_nth_oid(element_ids, k + destvertexoffset))
+ {
+ qual_exprs = lappend(qual_exprs, makeBoolConst(false, false));
+ }
+ else
+ {
+ qual_exprs = list_concat(qual_exprs,
+ build_edge_vertex_link_quals(tuple, k + 1, k + 1 + destvertexoffset,
+ Anum_pg_propgraph_element_pgedestkey, Anum_pg_propgraph_element_pgedestref));
+ }
+
+ ReleaseSysCache(tuple);
+ }
+
+ newsubquery->jointree = makeFromExpr(fromlist, (Node *) makeBoolExpr(AND_EXPR, qual_exprs, -1));
+
+ foreach(lc, rte->graph_table_columns)
+ {
+ TargetEntry *te = lfirst_node(TargetEntry, lc);
+ Node *nte;
+
+ nte = replace_property_refs((Node *) te, elvar_rt_mappings);
+ newsubquery->targetList = lappend(newsubquery->targetList, nte);
+ }
+
+ AcquireRewriteLocks(newsubquery, true, false);
+
+ rte->rtekind = RTE_SUBQUERY;
+ rte->subquery = newsubquery;
+
+ /*
+ * Reset no longer applicable fields, to appease
+ * WRITE_READ_PARSE_PLAN_TREES.
+ */
+ rte->graph_pattern = NULL;
+ rte->graph_table_columns = NIL;
+
+ return parsetree;
+}
+
+/*
+ * Get label OID from graph OID and label name.
+ *
+ * TODO: This could match more than one entry. Right now it only returns the
+ * first one.
+ */
+static Oid
+get_labelid(Oid graphid, const char *labelname)
+{
+ Relation rel;
+ SysScanDesc scan;
+ ScanKeyData key[2];
+ HeapTuple tup;
+ Oid result = InvalidOid;
+
+ rel = table_open(PropgraphLabelRelationId, RowShareLock);
+ ScanKeyInit(&key[0],
+ Anum_pg_propgraph_label_pglpgid,
+ BTEqualStrategyNumber,
+ F_OIDEQ, ObjectIdGetDatum(graphid));
+ ScanKeyInit(&key[1],
+ Anum_pg_propgraph_label_pgllabel,
+ BTEqualStrategyNumber,
+ F_NAMEEQ, CStringGetDatum(labelname));
+
+ scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId,
+ true, NULL, 2, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ result = ((Form_pg_propgraph_label) GETSTRUCT(tup))->oid;
+ break;
+ }
+
+ systable_endscan(scan);
+ table_close(rel, RowShareLock);
+
+ return result;
+}
+
+/*
+ * Get list of element OIDs that have a given label.
+ */
+static List *
+get_elements_for_label(Oid graphid, const char *labelname)
+{
+ Relation rel;
+ SysScanDesc scan;
+ ScanKeyData key[2];
+ HeapTuple tup;
+ List *result = NIL;
+
+ rel = table_open(PropgraphLabelRelationId, RowShareLock);
+ ScanKeyInit(&key[0],
+ Anum_pg_propgraph_label_pglpgid,
+ BTEqualStrategyNumber,
+ F_OIDEQ, ObjectIdGetDatum(graphid));
+ ScanKeyInit(&key[1],
+ Anum_pg_propgraph_label_pgllabel,
+ BTEqualStrategyNumber,
+ F_NAMEEQ, CStringGetDatum(labelname));
+
+ scan = systable_beginscan(rel, PropgraphLabelGraphNameIndexId,
+ true, NULL, 2, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ result = lappend_oid(result, ((Form_pg_propgraph_label) GETSTRUCT(tup))->pglelid);
+ }
+
+ systable_endscan(scan);
+ table_close(rel, RowShareLock);
+
+ return result;
+}
+
+/*
+ * Get the element table OID for a given element.
+ */
+static Oid
+get_table_for_element(Oid elid)
+{
+ return GetSysCacheOid1(PROPGRAPHELOID, Anum_pg_propgraph_element_pgerelid, ObjectIdGetDatum(elid));
+}
+
+/*
+ * Mutating property references into table variables
+ */
+
+struct replace_property_refs_context
+{
+ const List *mappings;
+};
+
+static Node *
+replace_property_refs_mutator(Node *node, struct replace_property_refs_context *context)
+{
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, GraphPropertyRef))
+ {
+ GraphPropertyRef *gpr = (GraphPropertyRef *) node;
+ HeapTuple tup;
+ Node *n;
+ ListCell *lc;
+ struct elvar_rt_mapping *found_mapping = NULL;
+
+ foreach(lc, context->mappings)
+ {
+ struct elvar_rt_mapping *m = lfirst(lc);
+
+ if (m->elvarname && strcmp(gpr->elvarname, m->elvarname) == 0)
+ {
+ found_mapping = m;
+ break;
+ }
+ }
+ if (!found_mapping)
+ elog(ERROR, "undefined element variable \"%s\"", gpr->elvarname);
+
+ tup = SearchSysCache2(PROPGRAPHPROPNAME, ObjectIdGetDatum(found_mapping->labelid), CStringGetDatum(gpr->propname));
+ if (!tup)
+ elog(ERROR, "property \"%s\" of label %u not found", gpr->propname, found_mapping->labelid);
+
+ n = stringToNode(TextDatumGetCString(SysCacheGetAttrNotNull(PROPGRAPHPROPNAME, tup, Anum_pg_propgraph_property_pgpexpr)));
+ ChangeVarNodes(n, 1, found_mapping->rt_index, 0);
+
+ ReleaseSysCache(tup);
+
+ return n;
+ }
+ return expression_tree_mutator(node, replace_property_refs_mutator, context);
+}
+
+static Node *
+replace_property_refs(Node *node, const List *mappings)
+{
+ struct replace_property_refs_context context;
+
+ context.mappings = mappings;
+
+ return expression_tree_mutator(node, replace_property_refs_mutator, &context);
+}
+
+/*
+ * Build join qualification expressions between edge and vertex tables.
+ */
+static List *
+build_edge_vertex_link_quals(HeapTuple edgetup, int edgerti, int refrti, AttrNumber catalog_key_attnum, AttrNumber catalog_ref_attnum)
+{
+ List *quals = NIL;
+ Form_pg_propgraph_element pgeform;
+ Datum datum;
+ Datum *d1,
+ *d2;
+ int n1,
+ n2;
+
+ pgeform = (Form_pg_propgraph_element) GETSTRUCT(edgetup);
+
+ datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_key_attnum);
+ deconstruct_array_builtin(DatumGetArrayTypeP(datum), INT2OID, &d1, NULL, &n1);
+
+ datum = SysCacheGetAttrNotNull(PROPGRAPHELOID, edgetup, catalog_ref_attnum);
+ deconstruct_array_builtin(DatumGetArrayTypeP(datum), INT2OID, &d2, NULL, &n2);
+
+ if (n1 != n2)
+ elog(ERROR, "array size key (%d) vs ref (%d) mismatch for element ID %u", catalog_key_attnum, catalog_ref_attnum, pgeform->oid);
+
+ for (int i = 0; i < n1; i++)
+ {
+ AttrNumber keyattn = DatumGetInt16(d1[i]);
+ AttrNumber refattn = DatumGetInt16(d2[i]);
+ Oid atttypid;
+ TypeCacheEntry *typentry;
+ OpExpr *op;
+
+ /*
+ * TODO: Assumes types the same on both sides; no collations yet. Some
+ * of this could probably be shared with foreign key triggers.
+ */
+ atttypid = get_atttype(pgeform->pgerelid, keyattn);
+ typentry = lookup_type_cache(atttypid, TYPECACHE_EQ_OPR);
+
+ op = makeNode(OpExpr);
+ op->location = -1;
+ op->opno = typentry->eq_opr;
+ op->opresulttype = BOOLOID;
+ op->args = list_make2(makeVar(edgerti, keyattn, atttypid, -1, 0, 0),
+ makeVar(refrti, refattn, atttypid, -1, 0, 0));
+ quals = lappend(quals, op);
+ }
+
+ return quals;
+}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 89187d9af2..6e41784a2d 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -37,6 +37,7 @@
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteDefine.h"
+#include "rewrite/rewriteGraphTable.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSearchCycle.h"
@@ -2032,6 +2033,17 @@ fireRIRrules(Query *parsetree, List *activeRIRs)
rte = rt_fetch(rt_index, parsetree->rtable);
+ /*
+ * Convert GRAPH_TABLE clause into a subquery using relational
+ * operators. (This will change the rtekind to subquery, so it must
+ * be done before the subquery handling below.)
+ */
+ if (rte->rtekind == RTE_GRAPH_TABLE)
+ {
+ parsetree = rewriteGraphTable(parsetree, rt_index);
+ continue;
+ }
+
/*
* A subquery RTE can't have associated rules, so there's nothing to
* do to this level of the query, but we must recurse into the
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 83f86a42f7..c71228cd27 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -44,6 +44,7 @@
#include "commands/portalcmds.h"
#include "commands/prepare.h"
#include "commands/proclang.h"
+#include "commands/propgraphcmds.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
@@ -148,6 +149,7 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
case T_AlterOperatorStmt:
case T_AlterOwnerStmt:
case T_AlterPolicyStmt:
+ case T_AlterPropGraphStmt:
case T_AlterPublicationStmt:
case T_AlterRoleSetStmt:
case T_AlterRoleStmt:
@@ -178,6 +180,7 @@ ClassifyUtilityCommandAsReadOnly(Node *parsetree)
case T_CreateOpFamilyStmt:
case T_CreatePLangStmt:
case T_CreatePolicyStmt:
+ case T_CreatePropGraphStmt:
case T_CreatePublicationStmt:
case T_CreateRangeStmt:
case T_CreateRoleStmt:
@@ -1736,6 +1739,14 @@ ProcessUtilitySlow(ParseState *pstate,
commandCollected = true;
break;
+ case T_CreatePropGraphStmt:
+ address = CreatePropGraph(pstate, (CreatePropGraphStmt *) parsetree);
+ break;
+
+ case T_AlterPropGraphStmt:
+ address = AlterPropGraph(pstate, (AlterPropGraphStmt *) parsetree);
+ break;
+
case T_CreateTransformStmt:
address = CreateTransform((CreateTransformStmt *) parsetree);
break;
@@ -2004,6 +2015,7 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel)
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
+ case OBJECT_PROPGRAPH:
RemoveRelations(stmt);
break;
default:
@@ -2281,6 +2293,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_PROCEDURE:
tag = CMDTAG_ALTER_PROCEDURE;
break;
+ case OBJECT_PROPGRAPH:
+ tag = CMDTAG_ALTER_PROPERTY_GRAPH;
+ break;
case OBJECT_ROLE:
tag = CMDTAG_ALTER_ROLE;
break;
@@ -2557,6 +2572,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_INDEX:
tag = CMDTAG_DROP_INDEX;
break;
+ case OBJECT_PROPGRAPH:
+ tag = CMDTAG_DROP_PROPERTY_GRAPH;
+ break;
case OBJECT_TYPE:
tag = CMDTAG_DROP_TYPE;
break;
@@ -2938,6 +2956,14 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_CreatePropGraphStmt:
+ tag = CMDTAG_CREATE_PROPERTY_GRAPH;
+ break;
+
+ case T_AlterPropGraphStmt:
+ tag = CMDTAG_ALTER_PROPERTY_GRAPH;
+ break;
+
case T_CreateTransformStmt:
tag = CMDTAG_CREATE_TRANSFORM;
break;
@@ -3635,6 +3661,14 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_CreatePropGraphStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_AlterPropGraphStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CreateTransformStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 2a1ee69970..2844ee2f85 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -34,6 +34,9 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_propgraph_element.h"
+#include "catalog/pg_propgraph_label.h"
+#include "catalog/pg_propgraph_property.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -345,6 +348,9 @@ static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
bool attrsOnly, bool keysOnly,
bool showTblSpc, bool inherits,
int prettyFlags, bool missing_ok);
+static void make_propgraphdef_elements(StringInfo buf, Oid pgrelid, char pgekind);
+static void make_propgraphdef_labels(StringInfo buf, Oid elid, const char *elalias, Oid elrelid);
+static void make_propgraphdef_properties(StringInfo buf, Oid labelid, Oid elrelid);
static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
bool missing_ok);
static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
@@ -1569,6 +1575,298 @@ pg_get_querydef(Query *query, bool pretty)
return buf.data;
}
+/*
+ * pg_get_propgraphdef - get the definition of a property graph
+ */
+Datum
+pg_get_propgraphdef(PG_FUNCTION_ARGS)
+{
+ Oid pgrelid = PG_GETARG_OID(0);
+ StringInfoData buf;
+ HeapTuple classtup;
+ Form_pg_class classform;
+ char *name;
+ char *nsp;
+
+ initStringInfo(&buf);
+
+ classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(pgrelid));
+ if (!HeapTupleIsValid(classtup))
+ PG_RETURN_NULL();
+
+ classform = (Form_pg_class) GETSTRUCT(classtup);
+ name = NameStr(classform->relname);
+
+ if (classform->relkind != RELKIND_PROPGRAPH)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a property graph", name)));
+
+ nsp = get_namespace_name(classform->relnamespace);
+
+ appendStringInfo(&buf, "CREATE PROPERTY GRAPH %s",
+ quote_qualified_identifier(nsp, name));
+
+ ReleaseSysCache(classtup);
+
+ make_propgraphdef_elements(&buf, pgrelid, PGEKIND_VERTEX);
+ make_propgraphdef_elements(&buf, pgrelid, PGEKIND_EDGE);
+
+ PG_RETURN_TEXT_P(string_to_text(buf.data));
+}
+
+/*
+ * Generates a VERTEX TABLES (...) or EDGE TABLES (...) clause. Pass in the
+ * property graph relation OID and the element kind (vertex or edge). Result
+ * is appended to buf.
+ */
+static void
+make_propgraphdef_elements(StringInfo buf, Oid pgrelid, char pgekind)
+{
+ Relation pgerel;
+ ScanKeyData scankey[1];
+ SysScanDesc scan;
+ bool first;
+ HeapTuple tup;
+
+ pgerel = table_open(PropgraphElementRelationId, AccessShareLock);
+
+ ScanKeyInit(&scankey[0],
+ Anum_pg_propgraph_element_pgepgid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(pgrelid));
+
+ scan = systable_beginscan(pgerel, PropgraphElementAliasIndexId, true, NULL, 1, scankey);
+
+ first = true;
+ while ((tup = systable_getnext(scan)))
+ {
+ Form_pg_propgraph_element pgeform = (Form_pg_propgraph_element) GETSTRUCT(tup);
+ char *relname;
+ Datum datum;
+ bool isnull;
+
+ if (pgeform->pgekind != pgekind)
+ continue;
+
+ if (first)
+ {
+ appendStringInfo(buf, "\n %s TABLES (\n", pgekind == PGEKIND_VERTEX ? "VERTEX" : "EDGE");
+ first = false;
+ }
+ else
+ appendStringInfo(buf, ",\n");
+
+ relname = get_rel_name(pgeform->pgerelid);
+ if (relname && strcmp(relname, NameStr(pgeform->pgealias)) == 0)
+ appendStringInfo(buf, " %s",
+ generate_relation_name(pgeform->pgerelid, NIL));
+ else
+ appendStringInfo(buf, " %s AS %s",
+ generate_relation_name(pgeform->pgerelid, NIL),
+ NameStr(pgeform->pgealias));
+
+ datum = heap_getattr(tup, Anum_pg_propgraph_element_pgekey, RelationGetDescr(pgerel), &isnull);
+ if (!isnull)
+ {
+ appendStringInfoString(buf, " KEY (");
+ decompile_column_index_array(datum, pgeform->pgerelid, buf);
+ appendStringInfoString(buf, ")");
+ }
+ else
+ elog(ERROR, "null pgekey for element %u", pgeform->oid);
+
+ if (pgekind == PGEKIND_EDGE)
+ {
+ Datum srckey;
+ Datum srcref;
+ Datum destkey;
+ Datum destref;
+ HeapTuple tup2;
+ Form_pg_propgraph_element pgeform2;
+
+ datum = heap_getattr(tup, Anum_pg_propgraph_element_pgesrckey, RelationGetDescr(pgerel), &isnull);
+ srckey = isnull ? 0 : datum;
+ datum = heap_getattr(tup, Anum_pg_propgraph_element_pgesrcref, RelationGetDescr(pgerel), &isnull);
+ srcref = isnull ? 0 : datum;
+ datum = heap_getattr(tup, Anum_pg_propgraph_element_pgedestkey, RelationGetDescr(pgerel), &isnull);
+ destkey = isnull ? 0 : datum;
+ datum = heap_getattr(tup, Anum_pg_propgraph_element_pgedestref, RelationGetDescr(pgerel), &isnull);
+ destref = isnull ? 0 : datum;
+
+ appendStringInfoString(buf, " SOURCE");
+ tup2 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(pgeform->pgesrcvertexid));
+ if (!tup2)
+ elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgesrcvertexid);
+ pgeform2 = (Form_pg_propgraph_element) GETSTRUCT(tup2);
+ if (srckey)
+ {
+ appendStringInfoString(buf, " KEY (");
+ decompile_column_index_array(srckey, pgeform->pgerelid, buf);
+ appendStringInfo(buf, ") REFERENCES %s (", NameStr(pgeform2->pgealias));
+ decompile_column_index_array(srcref, pgeform2->pgerelid, buf);
+ appendStringInfoString(buf, ")");
+ }
+ else
+ appendStringInfo(buf, " %s ", NameStr(pgeform2->pgealias));
+ ReleaseSysCache(tup2);
+
+ appendStringInfoString(buf, " DESTINATION");
+ tup2 = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(pgeform->pgedestvertexid));
+ if (!tup2)
+ elog(ERROR, "cache lookup failed for property graph element %u", pgeform->pgedestvertexid);
+ pgeform2 = (Form_pg_propgraph_element) GETSTRUCT(tup2);
+ if (destkey)
+ {
+ appendStringInfoString(buf, " KEY (");
+ decompile_column_index_array(destkey, pgeform->pgerelid, buf);
+ appendStringInfo(buf, ") REFERENCES %s (", NameStr(pgeform2->pgealias));
+ decompile_column_index_array(destref, pgeform2->pgerelid, buf);
+ appendStringInfoString(buf, ")");
+ }
+ else
+ appendStringInfo(buf, " %s", NameStr(pgeform2->pgealias));
+ ReleaseSysCache(tup2);
+ }
+
+ make_propgraphdef_labels(buf, pgeform->oid, NameStr(pgeform->pgealias), pgeform->pgerelid);
+ }
+ if (!first)
+ appendStringInfo(buf, "\n )");
+
+ systable_endscan(scan);
+ table_close(pgerel, AccessShareLock);
+}
+
+/*
+ * Generates label and properties list. Pass in the element OID, the element
+ * alias, and the graph relation OID. Result is append to buf.
+ */
+static void
+make_propgraphdef_labels(StringInfo buf, Oid elid, const char *elalias, Oid elrelid)
+{
+ Relation pglrel;
+ ScanKeyData scankey[1];
+ SysScanDesc scan;
+ int count;
+ HeapTuple tup;
+
+ pglrel = table_open(PropgraphLabelRelationId, AccessShareLock);
+
+ ScanKeyInit(&scankey[0],
+ Anum_pg_propgraph_label_pglelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(elid));
+
+ count = 0;
+ scan = systable_beginscan(pglrel, PropgraphLabelLabelIndexId, true, NULL, 1, scankey);
+ while ((tup = systable_getnext(scan)))
+ {
+ count++;
+ }
+ systable_endscan(scan);
+
+ /* XXX need to re-init scan key for second scan */
+ ScanKeyInit(&scankey[0],
+ Anum_pg_propgraph_label_pglelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(elid));
+
+ scan = systable_beginscan(pglrel, PropgraphLabelLabelIndexId, true, NULL, 1, scankey);
+
+ while ((tup = systable_getnext(scan)))
+ {
+ Form_pg_propgraph_label pglform = (Form_pg_propgraph_label) GETSTRUCT(tup);
+
+ if (strcmp(NameStr(pglform->pgllabel), elalias) == 0)
+ {
+ /* If the default label is the only label, don't print anything. */
+ if (count != 1)
+ appendStringInfo(buf, " DEFAULT LABEL");
+ }
+ else
+ appendStringInfo(buf, " LABEL %s",
+ quote_identifier(NameStr(pglform->pgllabel)));
+
+ make_propgraphdef_properties(buf, pglform->oid, elrelid);
+ }
+
+ systable_endscan(scan);
+
+ table_close(pglrel, AccessShareLock);
+}
+
+/*
+ * Generates element table properties clause (PROPERTIES (...) or NO
+ * PROPERTIES). Pass in label ODI and element table OID. Result is appended
+ * to buf.
+ */
+static void
+make_propgraphdef_properties(StringInfo buf, Oid labelid, Oid elrelid)
+{
+ List *context;
+ Relation pgprel;
+ ScanKeyData scankey[1];
+ SysScanDesc scan;
+ HeapTuple tup;
+ bool first;
+
+ context = deparse_context_for(get_relation_name(elrelid), elrelid);
+
+ pgprel = table_open(PropgraphPropertyRelationId, AccessShareLock);
+
+ ScanKeyInit(&scankey[0],
+ Anum_pg_propgraph_property_pgplabelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(labelid));
+
+ /*
+ * Note: Use of index on property name also ensures that properties are
+ * put out in a deterministic order.
+ */
+ scan = systable_beginscan(pgprel, PropgraphPropertyNameIndexId, true, NULL, 1, scankey);
+
+ first = true;
+ while ((tup = systable_getnext(scan)))
+ {
+ Form_pg_propgraph_property pgpform = (Form_pg_propgraph_property) GETSTRUCT(tup);
+ Datum exprDatum;
+ bool isnull;
+ char *tmp;
+ Node *expr;
+
+ if (first)
+ {
+ appendStringInfo(buf, " PROPERTIES (");
+ first = false;
+ }
+ else
+ appendStringInfo(buf, ", ");
+
+ exprDatum = heap_getattr(tup, Anum_pg_propgraph_property_pgpexpr, RelationGetDescr(pgprel), &isnull);
+ Assert(!isnull);
+ tmp = TextDatumGetCString(exprDatum);
+ expr = stringToNode(tmp);
+ pfree(tmp);
+
+ if (IsA(expr, Var) && strcmp(NameStr(pgpform->pgpname), get_attname(elrelid, castNode(Var, expr)->varattno, false)) == 0)
+ appendStringInfo(buf, "%s",
+ NameStr(pgpform->pgpname));
+ else
+ appendStringInfo(buf, "%s AS %s",
+ deparse_expression_pretty(expr, context, false, false, 0, 0),
+ NameStr(pgpform->pgpname));
+ }
+
+ if (first)
+ appendStringInfo(buf, " NO PROPERTIES");
+ else
+ appendStringInfo(buf, ")");
+
+ systable_endscan(scan);
+ table_close(pgprel, AccessShareLock);
+}
+
/*
* pg_get_statisticsobjdef
* Get the definition of an extended statistics object
@@ -7231,6 +7529,171 @@ get_utility_query_def(Query *query, deparse_context *context)
}
}
+
+/*
+ * Parse back a graph label expression
+ */
+static void
+get_graph_label_expr(Node *label_expr, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+
+ check_stack_depth();
+
+ switch (nodeTag(label_expr))
+ {
+ case T_GraphLabelRef:
+ {
+ GraphLabelRef *lref = (GraphLabelRef *) label_expr;
+
+ appendStringInfoString(buf, quote_identifier(lref->labelname));
+ break;
+ }
+
+ case T_BoolExpr:
+ {
+ BoolExpr *be = (BoolExpr *) label_expr;
+ ListCell *lc;
+ bool first = true;
+
+ Assert(be->boolop == OR_EXPR);
+
+ foreach(lc, be->args)
+ {
+ if (!first)
+ {
+ if (be->boolop == OR_EXPR)
+ appendStringInfoString(buf, "|");
+ }
+ else
+ first = false;
+ get_graph_label_expr(lfirst(lc), context);
+ }
+
+ break;
+ }
+
+ default:
+ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(label_expr));
+ break;
+ }
+}
+
+/*
+ * Parse back a path pattern expression
+ */
+static void
+get_path_pattern_expr_def(List *path_pattern_expr, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+ ListCell *lc;
+
+ foreach(lc, path_pattern_expr)
+ {
+ GraphElementPattern *gep = lfirst_node(GraphElementPattern, lc);
+ const char *sep = "";
+
+ switch (gep->kind)
+ {
+ case VERTEX_PATTERN:
+ appendStringInfoString(buf, "(");
+ break;
+ case EDGE_PATTERN_LEFT:
+ appendStringInfoString(buf, "<-[");
+ break;
+ case EDGE_PATTERN_RIGHT:
+ case EDGE_PATTERN_ANY:
+ appendStringInfoString(buf, "-[");
+ break;
+ case PAREN_EXPR:
+ appendStringInfoString(buf, "(");
+ break;
+ }
+
+ if (gep->variable)
+ {
+ appendStringInfoString(buf, gep->variable);
+ sep = " ";
+ }
+
+ if (gep->labelexpr)
+ {
+ appendStringInfoString(buf, sep);
+ appendStringInfoString(buf, "IS ");
+ get_graph_label_expr(gep->labelexpr, context);
+ sep = " ";
+ }
+
+ if (gep->subexpr)
+ {
+ appendStringInfoString(buf, sep);
+ get_path_pattern_expr_def(gep->subexpr, context);
+ sep = " ";
+ }
+
+ if (gep->whereClause)
+ {
+ appendStringInfoString(buf, sep);
+ appendStringInfoString(buf, "WHERE ");
+ get_rule_expr(gep->whereClause, context, false);
+ }
+
+ switch (gep->kind)
+ {
+ case VERTEX_PATTERN:
+ appendStringInfoString(buf, ")");
+ break;
+ case EDGE_PATTERN_LEFT:
+ case EDGE_PATTERN_ANY:
+ appendStringInfoString(buf, "]-");
+ break;
+ case EDGE_PATTERN_RIGHT:
+ appendStringInfoString(buf, "]->");
+ break;
+ case PAREN_EXPR:
+ appendStringInfoString(buf, ")");
+ break;
+ }
+
+ if (gep->quantifier)
+ {
+ int lower = linitial_int(gep->quantifier);
+ int upper = lsecond_int(gep->quantifier);
+
+ appendStringInfo(buf, "{%d,%d}", lower, upper);
+ }
+ }
+}
+
+/*
+ * Parse back a graph pattern
+ */
+static void
+get_graph_pattern_def(GraphPattern *graph_pattern, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+ ListCell *lc;
+ bool first = true;
+
+ foreach(lc, graph_pattern->path_pattern_list)
+ {
+ List *path_pattern_expr = lfirst_node(List, lc);
+
+ if (!first)
+ appendStringInfoString(buf, ", ");
+ else
+ first = false;
+
+ get_path_pattern_expr_def(path_pattern_expr, context);
+ }
+
+ if (graph_pattern->whereClause)
+ {
+ appendStringInfoString(buf, "WHERE ");
+ get_rule_expr(graph_pattern->whereClause, context, false);
+ }
+}
+
/*
* Display a Var appropriately.
*
@@ -7793,6 +8256,7 @@ get_name_for_var_field(Var *var, int fieldno,
case RTE_RELATION:
case RTE_VALUES:
case RTE_NAMEDTUPLESTORE:
+ case RTE_GRAPH_TABLE:
case RTE_RESULT:
/*
@@ -9812,6 +10276,14 @@ get_rule_expr(Node *node, deparse_context *context,
get_tablefunc((TableFunc *) node, context, showimplicit);
break;
+ case T_GraphPropertyRef:
+ {
+ GraphPropertyRef *gpr = (GraphPropertyRef *) node;
+
+ appendStringInfo(buf, "%s.%s", quote_identifier(gpr->elvarname), quote_identifier(gpr->propname));
+ break;
+ }
+
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
break;
@@ -11438,6 +11910,36 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
case RTE_TABLEFUNC:
get_tablefunc(rte->tablefunc, context, true);
break;
+ case RTE_GRAPH_TABLE:
+ appendStringInfoString(buf, "GRAPH_TABLE (");
+ appendStringInfoString(buf, generate_relation_name(rte->relid, context->namespaces));
+ appendStringInfoString(buf, " MATCH ");
+ get_graph_pattern_def(rte->graph_pattern, context);
+ appendStringInfoString(buf, " COLUMNS (");
+ {
+ ListCell *lc;
+ bool first = true;
+
+ foreach(lc, rte->graph_table_columns)
+ {
+ TargetEntry *te = lfirst_node(TargetEntry, lc);
+ deparse_context context = {0};
+
+ if (!first)
+ appendStringInfoString(buf, ", ");
+ else
+ first = false;
+
+ context.buf = buf;
+
+ get_rule_expr((Node *) te->expr, &context, false);
+ appendStringInfoString(buf, " AS ");
+ appendStringInfoString(buf, quote_identifier(te->resname));
+ }
+ }
+ appendStringInfoString(buf, ")");
+ appendStringInfoString(buf, ")");
+ break;
case RTE_VALUES:
/* Values list RTE */
appendStringInfoChar(buf, '(');
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 0ed18b72d6..bc34fc7d6b 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -519,7 +519,8 @@ flagInhAttrs(Archive *fout, DumpOptions *dopt, TableInfo *tblinfo, int numTables
/* Some kinds never have parents */
if (tbinfo->relkind == RELKIND_SEQUENCE ||
tbinfo->relkind == RELKIND_VIEW ||
- tbinfo->relkind == RELKIND_MATVIEW)
+ tbinfo->relkind == RELKIND_MATVIEW ||
+ tbinfo->relkind == RELKIND_PROPGRAPH)
continue;
/* Don't bother computing anything for non-target tables, either */
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index d97ebaff5b..ebd74e4053 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3456,6 +3456,7 @@ _getObjectDescription(PQExpBuffer buf, const TocEntry *te)
strcmp(type, "DOMAIN") == 0 ||
strcmp(type, "FOREIGN TABLE") == 0 ||
strcmp(type, "MATERIALIZED VIEW") == 0 ||
+ strcmp(type, "PROPERTY GRAPH") == 0 ||
strcmp(type, "SEQUENCE") == 0 ||
strcmp(type, "STATISTICS") == 0 ||
strcmp(type, "TABLE") == 0 ||
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 10cbf02beb..71842192e1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1601,10 +1601,10 @@ expand_table_name_patterns(Archive *fout,
"\n LEFT JOIN pg_catalog.pg_namespace n"
"\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
"\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
- "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
+ "\n (array['%c', '%c', '%c', '%c', '%c', '%c', '%c'])\n",
RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
- RELKIND_PARTITIONED_TABLE);
+ RELKIND_PARTITIONED_TABLE, RELKIND_PROPGRAPH);
initPQExpBuffer(&dbbuf);
processSQLNamePattern(GetConnection(fout), query, cell->val, true,
false, "n.nspname", "c.relname", NULL,
@@ -2748,6 +2748,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
if (tbinfo->dataObj != NULL)
return;
+ /* Skip property graphs (no data to dump) */
+ if (tbinfo->relkind == RELKIND_PROPGRAPH)
+ return;
/* Skip VIEWs (no data to dump) */
if (tbinfo->relkind == RELKIND_VIEW)
return;
@@ -6805,7 +6808,8 @@ getTables(Archive *fout, int *numTables)
CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
CppAsString2(RELKIND_MATVIEW) ", "
CppAsString2(RELKIND_FOREIGN_TABLE) ", "
- CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
+ CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
+ CppAsString2(RELKIND_PROPGRAPH) ")\n"
"ORDER BY c.oid");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -15869,8 +15873,6 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
reltypename = "VIEW";
- appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
-
if (dopt->binary_upgrade)
binary_upgrade_set_pg_class_oids(fout, q,
tbinfo->dobj.catId.oid, false);
@@ -15896,6 +15898,47 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
appendPQExpBufferStr(q, ";\n");
}
+ else if (tbinfo->relkind == RELKIND_PROPGRAPH)
+ {
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ int len;
+
+ reltypename = "PROPERTY GRAPH";
+
+ if (dopt->binary_upgrade)
+ binary_upgrade_set_pg_class_oids(fout, q,
+ tbinfo->dobj.catId.oid, false);
+
+ appendPQExpBuffer(query,
+ "SELECT pg_catalog.pg_get_propgraphdef('%u'::pg_catalog.oid) AS pgdef",
+ tbinfo->dobj.catId.oid);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ if (PQntuples(res) != 1)
+ {
+ if (PQntuples(res) < 1)
+ pg_fatal("query to obtain definition of property graph \"%s\" returned no data",
+ tbinfo->dobj.name);
+ else
+ pg_fatal("query to obtain definition of property graph \"%s\" returned more than one definition",
+ tbinfo->dobj.name);
+ }
+
+ len = PQgetlength(res, 0, 0);
+
+ if (len == 0)
+ pg_fatal("definition of property graph \"%s\" appears to be empty (length zero)",
+ tbinfo->dobj.name);
+
+ appendPQExpBufferStr(q, PQgetvalue(res, 0, 0));
+
+ PQclear(res);
+ destroyPQExpBuffer(query);
+
+ appendPQExpBufferStr(q, ";\n");
+ }
else
{
char *partkeydef = NULL;
@@ -15971,8 +16014,6 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
numParents = tbinfo->numParents;
parents = tbinfo->parents;
- appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
-
if (dopt->binary_upgrade)
binary_upgrade_set_pg_class_oids(fout, q,
tbinfo->dobj.catId.oid, false);
@@ -16590,6 +16631,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
qualrelname);
+ appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
+
if (dopt->binary_upgrade)
binary_upgrade_extension_member(q, &tbinfo->dobj,
reltypename, qrelname,
@@ -18423,6 +18466,16 @@ getDependencies(Archive *fout)
"classid = 'pg_amproc'::regclass AND objid = p.oid "
"AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
+ /*
+ * Translate dependencies of pg_propgraph_element entries into
+ * dependencies of their parent pg_class entry.
+ */
+ appendPQExpBufferStr(query, "UNION ALL\n"
+ "SELECT 'pg_class'::regclass AS classid, pgepgid AS objid, refclassid, refobjid, deptype "
+ "FROM pg_depend d, pg_propgraph_element pge "
+ "WHERE deptype NOT IN ('p', 'e', 'i') AND "
+ "classid = 'pg_propgraph_element'::regclass AND objid = pge.oid\n");
+
/* Sort the output for efficiency below */
appendPQExpBufferStr(query, "ORDER BY 1,2");
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 00b5092713..ea0d6beb09 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2959,6 +2959,17 @@ my %tests = (
},
},
+ 'CREATE PROPERTY GRAPH propgraph' => {
+ create_order => 20,
+ create_sql => 'CREATE PROPERTY GRAPH dump_test.propgraph;',
+ regexp => qr/^
+ \QCREATE PROPERTY GRAPH dump_test.propgraph\E;
+ /xm,
+ like =>
+ { %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
+ unlike => { exclude_dump_test_schema => 1, only_dump_measurement => 1, },
+ },
+
'CREATE PUBLICATION pub1' => {
create_order => 50,
create_sql => 'CREATE PUBLICATION pub1;',
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 5c906e4806..ea53da0f5a 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -775,7 +775,7 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
success = describeTableDetails(pattern, show_verbose, show_system);
else
/* standard listing of interesting things */
- success = listTables("tvmsE", NULL, show_verbose, show_system);
+ success = listTables("tvmsEG", NULL, show_verbose, show_system);
break;
case 'A':
{
@@ -906,6 +906,7 @@ exec_command_d(PsqlScanState scan_state, bool active_branch, const char *cmd)
case 'i':
case 's':
case 'E':
+ case 'G':
success = listTables(&cmd[1], pattern, show_verbose, show_system);
break;
case 'r':
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index c05befbb6f..d3018c416f 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -1025,6 +1025,7 @@ permissionsList(const char *pattern, bool showSystem)
" WHEN " CppAsString2(RELKIND_MATVIEW) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_SEQUENCE) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
+ " WHEN " CppAsString2(RELKIND_PROPGRAPH) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
" END as \"%s\",\n"
" ",
@@ -1035,6 +1036,7 @@ permissionsList(const char *pattern, bool showSystem)
gettext_noop("materialized view"),
gettext_noop("sequence"),
gettext_noop("foreign table"),
+ gettext_noop("property graph"),
gettext_noop("partitioned table"),
gettext_noop("Type"));
@@ -1126,6 +1128,7 @@ permissionsList(const char *pattern, bool showSystem)
CppAsString2(RELKIND_MATVIEW) ","
CppAsString2(RELKIND_SEQUENCE) ","
CppAsString2(RELKIND_FOREIGN_TABLE) ","
+ CppAsString2(RELKIND_PROPGRAPH) ","
CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
if (!showSystem && !pattern)
@@ -2009,6 +2012,10 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&title, _("Partitioned table \"%s.%s\""),
schemaname, relationname);
break;
+ case RELKIND_PROPGRAPH:
+ printfPQExpBuffer(&title, _("Property graph \"%s.%s\""),
+ schemaname, relationname);
+ break;
default:
/* untranslated unknown relkind */
printfPQExpBuffer(&title, "?%c? \"%s.%s\"",
@@ -3099,6 +3106,32 @@ describeOneTableDetails(const char *schemaname,
}
}
+ /* Add property graph definition in verbose mode */
+ if (tableinfo.relkind == RELKIND_PROPGRAPH && verbose)
+ {
+ PGresult *result;
+ char *pgdef = NULL;
+
+ printfPQExpBuffer(&buf,
+ "SELECT pg_catalog.pg_get_propgraphdef('%s'::pg_catalog.oid);",
+ oid);
+ result = PSQLexec(buf.data);
+ if (!result)
+ goto error_return;
+
+ if (PQntuples(result) > 0)
+ pgdef = pg_strdup(PQgetvalue(result, 0, 0));
+
+ PQclear(result);
+
+ if (pgdef)
+ {
+ printTableAddFooter(&cont, _("Property graph definition:"));
+ printfPQExpBuffer(&buf, " %s", pgdef);
+ printTableAddFooter(&cont, buf.data);
+ }
+ }
+
/* Get view_def if table is a view or materialized view */
if ((tableinfo.relkind == RELKIND_VIEW ||
tableinfo.relkind == RELKIND_MATVIEW) && verbose)
@@ -3950,6 +3983,7 @@ describeRoleGrants(const char *pattern, bool showSystem)
* m - materialized views
* s - sequences
* E - foreign table (Note: different from 'f', the relkind value)
+ * G - property graphs
* (any order of the above is fine)
*/
bool
@@ -3961,6 +3995,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
bool showMatViews = strchr(tabtypes, 'm') != NULL;
bool showSeq = strchr(tabtypes, 's') != NULL;
bool showForeign = strchr(tabtypes, 'E') != NULL;
+ bool showPropGraphs = strchr(tabtypes, 'G') != NULL;
PQExpBufferData buf;
PGresult *res;
@@ -3969,8 +4004,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
bool translate_columns[] = {false, false, true, false, false, false, false, false, false};
/* If tabtypes is empty, we default to \dtvmsE (but see also command.c) */
- if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign))
- showTables = showViews = showMatViews = showSeq = showForeign = true;
+ if (!(showTables || showIndexes || showViews || showMatViews || showSeq || showForeign || showPropGraphs))
+ showTables = showViews = showMatViews = showSeq = showForeign = showPropGraphs = true;
initPQExpBuffer(&buf);
@@ -3987,6 +4022,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
" WHEN " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_PARTITIONED_TABLE) " THEN '%s'"
" WHEN " CppAsString2(RELKIND_PARTITIONED_INDEX) " THEN '%s'"
+ " WHEN " CppAsString2(RELKIND_PROPGRAPH) " THEN '%s'"
" END as \"%s\",\n"
" pg_catalog.pg_get_userbyid(c.relowner) as \"%s\"",
gettext_noop("Schema"),
@@ -4000,6 +4036,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
gettext_noop("foreign table"),
gettext_noop("partitioned table"),
gettext_noop("partitioned index"),
+ gettext_noop("property graph"),
gettext_noop("Type"),
gettext_noop("Owner"));
cols_so_far = 4;
@@ -4083,6 +4120,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
appendPQExpBufferStr(&buf, "'s',"); /* was RELKIND_SPECIAL */
if (showForeign)
appendPQExpBufferStr(&buf, CppAsString2(RELKIND_FOREIGN_TABLE) ",");
+ if (showPropGraphs)
+ appendPQExpBufferStr(&buf, CppAsString2(RELKIND_PROPGRAPH) ",");
appendPQExpBufferStr(&buf, "''"); /* dummy */
appendPQExpBufferStr(&buf, ")\n");
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 4e79a819d8..a8f04c156b 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -246,6 +246,7 @@ slashUsage(unsigned short int pager)
HELP0(" \\dFp[+] [PATTERN] list text search parsers\n");
HELP0(" \\dFt[+] [PATTERN] list text search templates\n");
HELP0(" \\dg[S+] [PATTERN] list roles\n");
+ HELP0(" \\dG[S+] [PATTERN] list property graphs");
HELP0(" \\di[S+] [PATTERN] list indexes\n");
HELP0(" \\dl[+] list large objects, same as \\lo_list\n");
HELP0(" \\dL[S+] [PATTERN] list procedural languages\n");
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index aa1acf8523..a847cf553c 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -788,6 +788,14 @@ static const SchemaQuery Query_for_list_of_partitioned_indexes = {
.result = "c.relname",
};
+static const SchemaQuery Query_for_list_of_propgraphs = {
+ .catname = "pg_catalog.pg_class c",
+ .selcondition = "c.relkind IN (" CppAsString2(RELKIND_PROPGRAPH) ")",
+ .viscondition = "pg_catalog.pg_table_is_visible(c.oid)",
+ .namespace = "c.relnamespace",
+ .result = "pg_catalog.quote_ident(c.relname)",
+};
+
/* All relations */
static const SchemaQuery Query_for_list_of_relations = {
@@ -1256,6 +1264,7 @@ static const pgsql_thing_t words_after_create[] = {
{"PARSER", NULL, NULL, &Query_for_list_of_ts_parsers, NULL, THING_NO_SHOW},
{"POLICY", NULL, NULL, NULL},
{"PROCEDURE", NULL, NULL, Query_for_list_of_procedures},
+ {"PROPERTY GRAPH", NULL, NULL, &Query_for_list_of_propgraphs},
{"PUBLICATION", NULL, Query_for_list_of_publications},
{"ROLE", Query_for_list_of_roles},
{"ROUTINE", NULL, NULL, &Query_for_list_of_routines, NULL, THING_NO_CREATE},
@@ -2305,6 +2314,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches("ALTER", "POLICY", MatchAny, "ON", MatchAny, "WITH", "CHECK"))
COMPLETE_WITH("(");
+ /* ALTER PROPERTY GRAPH */
+ else if (Matches("ALTER", "PROPERTY", "GRAPH"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_propgraphs);
+ else if (Matches("ALTER", "PROPERTY", "GRAPH", MatchAny))
+ COMPLETE_WITH("OWNER TO", "RENAME TO", "SET SCHEMA");
+
/* ALTER RULE <name>, add ON */
else if (Matches("ALTER", "RULE", MatchAny))
COMPLETE_WITH("ON");
@@ -2782,7 +2797,7 @@ psql_completion(const char *text, int start, int end)
"FOREIGN DATA WRAPPER", "FOREIGN TABLE",
"FUNCTION", "INDEX", "LANGUAGE", "LARGE OBJECT",
"MATERIALIZED VIEW", "OPERATOR", "POLICY",
- "PROCEDURE", "PROCEDURAL LANGUAGE", "PUBLICATION", "ROLE",
+ "PROCEDURE", "PROCEDURAL LANGUAGE", "PROPERTY GRAPH", "PUBLICATION", "ROLE",
"ROUTINE", "RULE", "SCHEMA", "SEQUENCE", "SERVER",
"STATISTICS", "SUBSCRIPTION", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRANSFORM FOR",
@@ -2820,6 +2835,8 @@ psql_completion(const char *text, int start, int end)
}
else if (Matches("COMMENT", "ON", "PROCEDURAL", "LANGUAGE"))
COMPLETE_WITH_QUERY(Query_for_list_of_languages);
+ else if (Matches("COMMENT", "ON", "PROPERTY", "GRAPH"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_propgraphs);
else if (Matches("COMMENT", "ON", "RULE", MatchAny))
COMPLETE_WITH("ON");
else if (Matches("COMMENT", "ON", "RULE", MatchAny, "ON"))
@@ -3134,6 +3151,31 @@ psql_completion(const char *text, int start, int end)
else if (Matches("CREATE", "POLICY", MatchAny, "ON", MatchAny, "AS", MatchAny, "USING"))
COMPLETE_WITH("(");
+/* CREATE PROPERTY GRAPH */
+ else if (Matches("CREATE", "PROPERTY"))
+ COMPLETE_WITH("GRAPH");
+ else if (Matches("CREATE", "PROPERTY", "GRAPH", MatchAny))
+ COMPLETE_WITH("VERTEX");
+ else if (Matches("CREATE", "PROPERTY", "GRAPH", MatchAny, "VERTEX|NODE"))
+ COMPLETE_WITH("TABLES");
+ else if (Matches("CREATE", "PROPERTY", "GRAPH", MatchAny, "VERTEX|NODE", "TABLES"))
+ COMPLETE_WITH("(");
+ else if (Matches("CREATE", "PROPERTY", "GRAPH", MatchAny, "VERTEX|NODE", "TABLES", "("))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
+ else if (Matches("CREATE", "PROPERTY", "GRAPH", MatchAny, "VERTEX|NODE", "TABLES", "(", MatchAny))
+ COMPLETE_WITH(",", ")");
+ else if (HeadMatches("CREATE", "PROPERTY", "GRAPH", MatchAny, "VERTEX|NODE", "TABLES", "(") &&
+ TailMatches(")"))
+ COMPLETE_WITH("EDGE"); /* FIXME: doesn't seem to work */
+ else if (HeadMatches("CREATE", "PROPERTY", "GRAPH") &&
+ TailMatches("EDGE|RELATIONSHIP"))
+ COMPLETE_WITH("TABLES");
+ else if (HeadMatches("CREATE", "PROPERTY", "GRAPH") &&
+ TailMatches("EDGE|RELATIONSHIP", "TABLES"))
+ COMPLETE_WITH("(");
+ else if (HeadMatches("CREATE", "PROPERTY", "GRAPH") &&
+ TailMatches("EDGE|RELATIONSHIP", "TABLES", "("))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables);
/* CREATE PUBLICATION */
else if (Matches("CREATE", "PUBLICATION", MatchAny))
@@ -3801,6 +3843,12 @@ psql_completion(const char *text, int start, int end)
else if (Matches("DROP", "POLICY", MatchAny, "ON", MatchAny))
COMPLETE_WITH("CASCADE", "RESTRICT");
+ /* DROP PROPERTY GRAPH */
+ else if (Matches("DROP", "PROPERTY"))
+ COMPLETE_WITH("GRAPH");
+ else if (Matches("DROP", "PROPERTY", "GRAPH"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_propgraphs);
+
/* DROP RULE */
else if (Matches("DROP", "RULE", MatchAny))
COMPLETE_WITH("ON");
@@ -4035,6 +4083,7 @@ psql_completion(const char *text, int start, int end)
"LARGE OBJECT",
"PARAMETER",
"PROCEDURE",
+ "PROPERTY GRAPH",
"ROUTINE",
"SCHEMA",
"SEQUENCE",
@@ -4161,6 +4210,14 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("FROM");
}
+/* GRAPH_TABLE */
+ else if (TailMatches("GRAPH_TABLE"))
+ COMPLETE_WITH("(");
+ else if (TailMatches("GRAPH_TABLE", "("))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_propgraphs);
+ else if (TailMatches("GRAPH_TABLE", "(", MatchAny))
+ COMPLETE_WITH("MATCH");
+
/* GROUP BY */
else if (TailMatches("FROM", MatchAny, "GROUP"))
COMPLETE_WITH("BY");
@@ -4466,8 +4523,10 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("TABLE", "COLUMN", "AGGREGATE", "DATABASE", "DOMAIN",
"EVENT TRIGGER", "FOREIGN TABLE", "FUNCTION",
"LARGE OBJECT", "MATERIALIZED VIEW", "LANGUAGE",
- "PUBLICATION", "PROCEDURE", "ROLE", "ROUTINE", "SCHEMA",
+ "PROPERTY GRAPH", "PUBLICATION", "PROCEDURE", "ROLE", "ROUTINE", "SCHEMA",
"SEQUENCE", "SUBSCRIPTION", "TABLESPACE", "TYPE", "VIEW");
+ else if (Matches("SECURITY", "LABEL", "ON", "PROPERTY", "GRAPH"))
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_propgraphs);
else if (Matches("SECURITY", "LABEL", "ON", MatchAny, MatchAny))
COMPLETE_WITH("IS");
@@ -4884,6 +4943,8 @@ psql_completion(const char *text, int start, int end)
COMPLETE_WITH("OBJECT");
else if (TailMatches("CREATE|ALTER|DROP", "MATERIALIZED"))
COMPLETE_WITH("VIEW");
+ else if (TailMatches("CREATE|ALTER|DROP", "PROPERTY"))
+ COMPLETE_WITH("GRAPH");
else if (TailMatches("CREATE|ALTER|DROP", "TEXT"))
COMPLETE_WITH("SEARCH");
else if (TailMatches("CREATE|ALTER|DROP", "USER"))
diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l
index c9df0594fd..3a10a92418 100644
--- a/src/fe_utils/psqlscan.l
+++ b/src/fe_utils/psqlscan.l
@@ -788,6 +788,14 @@ other .
ECHO;
}
+\{ {
+ ECHO;
+ }
+
+\} {
+ ECHO;
+ }
+
{operator} {
/*
* Check for embedded slash-star or dash-dash; those
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 436b081738..df47d2ac62 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -123,6 +123,9 @@ typedef enum ObjectClass
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
OCLASS_PARAMETER_ACL, /* pg_parameter_acl */
OCLASS_POLICY, /* pg_policy */
+ OCLASS_PROPGRAPH_ELEMENT, /* pg_propgraph_element */
+ OCLASS_PROPGRAPH_LABEL, /* pg_propgraph_label */
+ OCLASS_PROPGRAPH_PROPERTY, /* pg_propgraph_property */
OCLASS_PUBLICATION, /* pg_publication */
OCLASS_PUBLICATION_NAMESPACE, /* pg_publication_namespace */
OCLASS_PUBLICATION_REL, /* pg_publication_rel */
diff --git a/src/include/catalog/meson.build b/src/include/catalog/meson.build
index 6b3c56c20e..e4b7b626be 100644
--- a/src/include/catalog/meson.build
+++ b/src/include/catalog/meson.build
@@ -69,6 +69,9 @@ catalog_headers = [
'pg_publication_rel.h',
'pg_subscription.h',
'pg_subscription_rel.h',
+ 'pg_propgraph_element.h',
+ 'pg_propgraph_label.h',
+ 'pg_propgraph_property.h',
]
# The .dat files we need can just be listed alphabetically.
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 3b7533e7bb..1635d2c5b3 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -171,6 +171,7 @@ MAKE_SYSCACHE(RELNAMENSP, pg_class_relname_nsp_index, 128);
#define RELKIND_FOREIGN_TABLE 'f' /* foreign table */
#define RELKIND_PARTITIONED_TABLE 'p' /* partitioned table */
#define RELKIND_PARTITIONED_INDEX 'I' /* partitioned index */
+#define RELKIND_PROPGRAPH 'g' /* property graph */
#define RELPERSISTENCE_PERMANENT 'p' /* regular table */
#define RELPERSISTENCE_UNLOGGED 'u' /* unlogged permanent table */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 291ed876fc..7b0971e0fc 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -3780,6 +3780,9 @@
proargtypes => 'oid oid', prosrc => 'oidge' },
# System-view support functions
+{ oid => '8302', descr => 'source text of a property graph',
+ proname => 'pg_get_propgraphdef', provolatile => 's', prorettype => 'text',
+ proargtypes => 'oid', prosrc => 'pg_get_propgraphdef' },
{ oid => '1573', descr => 'source text of a rule',
proname => 'pg_get_ruledef', provolatile => 's', prorettype => 'text',
proargtypes => 'oid', prosrc => 'pg_get_ruledef' },
diff --git a/src/include/catalog/pg_propgraph_element.h b/src/include/catalog/pg_propgraph_element.h
new file mode 100644
index 0000000000..fb4bbf8154
--- /dev/null
+++ b/src/include/catalog/pg_propgraph_element.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_propgraph_element.h
+ * definition of the "property graph elements" system catalog (pg_propgraph_element)
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_propgraph_element.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PROPGRAPH_ELEMENT_H
+#define PG_PROPGRAPH_ELEMENT_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_propgraph_element_d.h"
+
+/* ----------------
+ * pg_propgraph_element definition. cpp turns this into
+ * typedef struct FormData_pg_propgraph_element
+ * ----------------
+ */
+CATALOG(pg_propgraph_element,8299,PropgraphElementRelationId)
+{
+ Oid oid;
+
+ /* OID of the property graph relation */
+ Oid pgepgid BKI_LOOKUP(pg_class);
+
+ /* OID of the element table */
+ Oid pgerelid BKI_LOOKUP(pg_class);
+
+ /* element alias */
+ NameData pgealias;
+
+ /* vertex or edge? -- see PGEKIND_* below */
+ char pgekind;
+
+ /* for edges: source vertex */
+ Oid pgesrcvertexid BKI_LOOKUP_OPT(pg_propgraph_element);
+
+ /* for edges: destination vertex */
+ Oid pgedestvertexid BKI_LOOKUP_OPT(pg_propgraph_element);
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+ /* element key (column numbers in pgerelid relation) */
+ int16 pgekey[1] BKI_FORCE_NOT_NULL;
+
+ /*
+ * for edges: source vertex key (column numbers in pgerelid relation)
+ */
+ int16 pgesrckey[1];
+
+ /*
+ * for edges: source vertex table referenced columns (column numbers in
+ * relation reached via pgesrcvertexid)
+ */
+ int16 pgesrcref[1];
+
+ /*
+ * for edges: destination vertex key (column numbers in pgerelid relation)
+ */
+ int16 pgedestkey[1];
+
+ /*
+ * for edges: destination vertex table referenced columns (column numbers
+ * in relation reached via pgedestvertexid)
+ */
+ int16 pgedestref[1];
+#endif
+} FormData_pg_propgraph_element;
+
+/* ----------------
+ * Form_pg_propgraph_element corresponds to a pointer to a tuple with
+ * the format of pg_propgraph_element relation.
+ * ----------------
+ */
+typedef FormData_pg_propgraph_element *Form_pg_propgraph_element;
+
+DECLARE_TOAST(pg_propgraph_element, 8312, 8313);
+
+DECLARE_UNIQUE_INDEX_PKEY(pg_propgraph_element_oid_index, 8300, PropgraphElementObjectIndexId, pg_propgraph_element, btree(oid oid_ops));
+DECLARE_UNIQUE_INDEX(pg_propgraph_element_alias_index, 8301, PropgraphElementAliasIndexId, pg_propgraph_element, btree(pgepgid oid_ops, pgealias name_ops));
+
+MAKE_SYSCACHE(PROPGRAPHELOID, pg_propgraph_element_oid_index, 128);
+MAKE_SYSCACHE(PROPGRAPHELALIAS, pg_propgraph_element_alias_index, 128);
+
+#ifdef EXPOSE_TO_CLIENT_CODE
+
+/*
+ * Symbolic values for pgekind column
+ */
+#define PGEKIND_VERTEX 'v'
+#define PGEKIND_EDGE 'e'
+
+#endif /* EXPOSE_TO_CLIENT_CODE */
+
+#endif /* PG_PROPGRAPH_ELEMENT_H */
diff --git a/src/include/catalog/pg_propgraph_label.h b/src/include/catalog/pg_propgraph_label.h
new file mode 100644
index 0000000000..a704bdfabd
--- /dev/null
+++ b/src/include/catalog/pg_propgraph_label.h
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_propgraph_label.h
+ * definition of the "property graph labels" system catalog (pg_propgraph_label)
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_propgraph_label.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PROPGRAPH_LABEL_H
+#define PG_PROPGRAPH_LABEL_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_propgraph_label_d.h"
+
+/* ----------------
+ * pg_propgraph_label definition. cpp turns this into
+ * typedef struct FormData_pg_propgraph_label
+ * ----------------
+ */
+CATALOG(pg_propgraph_label,8303,PropgraphLabelRelationId)
+{
+ Oid oid;
+
+ /*
+ * OID of the property graph relation. This can also be found out by
+ * chasing via pglelid, but having it here is more efficient.
+ */
+ Oid pglpgid BKI_LOOKUP(pg_class);
+
+ /* label name */
+ NameData pgllabel;
+
+ /* OID of the property graph element */
+ Oid pglelid BKI_LOOKUP(pg_propgraph_element);
+} FormData_pg_propgraph_label;
+
+/* ----------------
+ * Form_pg_propgraph_label corresponds to a pointer to a tuple with
+ * the format of pg_propgraph_label relation.
+ * ----------------
+ */
+typedef FormData_pg_propgraph_label *Form_pg_propgraph_label;
+
+DECLARE_UNIQUE_INDEX_PKEY(pg_propgraph_label_oid_index, 8304, PropgraphLabelObjectIndexId, pg_propgraph_label, btree(oid oid_ops));
+DECLARE_UNIQUE_INDEX(pg_propgraph_label_label_index, 8305, PropgraphLabelLabelIndexId, pg_propgraph_label, btree(pglelid oid_ops, pgllabel name_ops));
+
+DECLARE_INDEX(pg_propgraph_label_graph_name_index, 8314, PropgraphLabelGraphNameIndexId, pg_propgraph_label, btree(pglpgid oid_ops, pgllabel name_ops));
+
+MAKE_SYSCACHE(PROPGRAPHLABELNAME, pg_propgraph_label_label_index, 128);
+
+#endif /* PG_PROPGRAPH_LABEL_H */
diff --git a/src/include/catalog/pg_propgraph_property.h b/src/include/catalog/pg_propgraph_property.h
new file mode 100644
index 0000000000..2d5f2b8fe7
--- /dev/null
+++ b/src/include/catalog/pg_propgraph_property.h
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_propgraph_property.h
+ * definition of the "property graph properties" system catalog (pg_propgraph_property)
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_propgraph_property.h
+ *
+ * NOTES
+ * The Catalog.pm module reads this file and derives schema
+ * information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PROPGRAPH_PROPERTY_H
+#define PG_PROPGRAPH_PROPERTY_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_propgraph_property_d.h"
+
+/* ----------------
+ * pg_propgraph_property definition. cpp turns this into
+ * typedef struct FormData_pg_propgraph_property
+ * ----------------
+ */
+CATALOG(pg_propgraph_property,8306,PropgraphPropertyRelationId)
+{
+ Oid oid;
+
+ /*
+ * OID of the property graph relation. This can also be found out by
+ * chasing via pgplabelid, but having it here is more efficient.
+ */
+ Oid pgppgid BKI_LOOKUP(pg_class);
+
+ /* property name */
+ NameData pgpname;
+
+ /*
+ * Type of the property. (This can be computed from pgpexpr, but storing
+ * it makes parsing GRAPH_TABLE more efficient.)
+ */
+ Oid pgptypid BKI_LOOKUP_OPT(pg_type);
+
+ /* OID of the label */
+ Oid pgplabelid BKI_LOOKUP(pg_propgraph_label);
+
+#ifdef CATALOG_VARLEN /* variable-length fields start here */
+
+ /* property expression */
+ pg_node_tree pgpexpr BKI_FORCE_NOT_NULL;
+
+#endif
+} FormData_pg_propgraph_property;
+
+/* ----------------
+ * Form_pg_propgraph_property corresponds to a pointer to a tuple with
+ * the format of pg_propgraph_property relation.
+ * ----------------
+ */
+typedef FormData_pg_propgraph_property *Form_pg_propgraph_property;
+
+DECLARE_TOAST(pg_propgraph_property, 8309, 8310);
+
+DECLARE_UNIQUE_INDEX_PKEY(pg_propgraph_property_oid_index, 8307, PropgraphPropertyObjectIndexId, pg_propgraph_property, btree(oid oid_ops));
+DECLARE_UNIQUE_INDEX(pg_propgraph_property_name_index, 8308, PropgraphPropertyNameIndexId, pg_propgraph_property, btree(pgplabelid oid_ops, pgpname name_ops));
+
+DECLARE_INDEX(pg_propgraph_property_graph_name_index, 8311, PropgraphPropertyGraphNameIndexId, pg_propgraph_property, btree(pgppgid oid_ops, pgpname name_ops));
+
+MAKE_SYSCACHE(PROPGRAPHPROPNAME, pg_propgraph_property_name_index, 128);
+
+#endif /* PG_PROPGRAPH_PROPERTY_H */
diff --git a/src/include/commands/propgraphcmds.h b/src/include/commands/propgraphcmds.h
new file mode 100644
index 0000000000..2440bd4a60
--- /dev/null
+++ b/src/include/commands/propgraphcmds.h
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * propgraphcmds.h
+ * prototypes for propgraphcmds.c.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/propgraphcmds.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PROPGRAPHCMDS_H
+#define PROPGRAPHCMDS_H
+
+#include "catalog/objectaddress.h"
+#include "parser/parse_node.h"
+
+extern ObjectAddress CreatePropGraph(ParseState *pstate, const CreatePropGraphStmt *stmt);
+extern ObjectAddress AlterPropGraph(ParseState *pstate, const AlterPropGraphStmt *stmt);
+
+#endif /* PROPGRAPHCMDS_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d60e148ff2..fa08a5ee29 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -677,6 +677,19 @@ typedef struct RangeTableFuncCol
int location; /* token location, or -1 if unknown */
} RangeTableFuncCol;
+/*
+ * RangeGraphTable - raw form of GRAPH_TABLE clause
+ */
+typedef struct RangeGraphTable
+{
+ NodeTag type;
+ RangeVar *graph_name;
+ struct GraphPattern *graph_pattern;
+ List *columns;
+ Alias *alias; /* table alias & optional column aliases */
+ int location; /* token location, or -1 if unknown */
+} RangeGraphTable;
+
/*
* RangeTableSample - TABLESAMPLE appearing in a raw FROM clause
*
@@ -943,6 +956,38 @@ typedef struct PartitionCmd
bool concurrent;
} PartitionCmd;
+/*
+ * Nodes for graph pattern
+ */
+
+typedef struct GraphPattern
+{
+ NodeTag type;
+ List *path_pattern_list;
+ Node *whereClause;
+} GraphPattern;
+
+typedef enum GraphElementPatternKind
+{
+ VERTEX_PATTERN,
+ EDGE_PATTERN_LEFT,
+ EDGE_PATTERN_RIGHT,
+ EDGE_PATTERN_ANY,
+ PAREN_EXPR,
+} GraphElementPatternKind;
+
+typedef struct GraphElementPattern
+{
+ NodeTag type;
+ GraphElementPatternKind kind;
+ const char *variable;
+ Node *labelexpr;
+ List *subexpr;
+ Node *whereClause;
+ List *quantifier;
+ int location;
+} GraphElementPattern;
+
/****************************************************************************
* Nodes for a Query tree
****************************************************************************/
@@ -1019,6 +1064,7 @@ typedef enum RTEKind
RTE_VALUES, /* VALUES (<exprlist>), (<exprlist>), ... */
RTE_CTE, /* common table expr (WITH list element) */
RTE_NAMEDTUPLESTORE, /* tuplestore, e.g. for AFTER triggers */
+ RTE_GRAPH_TABLE, /* GRAPH_TABLE clause */
RTE_RESULT, /* RTE represents an empty FROM clause; such
* RTEs are added by the planner, they're not
* present during parsing or rewriting */
@@ -1153,6 +1199,12 @@ typedef struct RangeTblEntry
*/
TableFunc *tablefunc;
+ /*
+ * Fields valid for a graph table RTE (else NULL):
+ */
+ GraphPattern *graph_pattern;
+ List *graph_table_columns;
+
/*
* Fields valid for a values RTE (else NIL):
*/
@@ -2131,6 +2183,7 @@ typedef enum ObjectType
OBJECT_PARAMETER_ACL,
OBJECT_POLICY,
OBJECT_PROCEDURE,
+ OBJECT_PROPGRAPH,
OBJECT_PUBLICATION,
OBJECT_PUBLICATION_NAMESPACE,
OBJECT_PUBLICATION_REL,
@@ -3858,6 +3911,88 @@ typedef struct CreateCastStmt
bool inout;
} CreateCastStmt;
+/* ----------------------
+ * CREATE PROPERTY GRAPH Statement
+ * ----------------------
+ */
+typedef struct CreatePropGraphStmt
+{
+ NodeTag type;
+ RangeVar *pgname;
+ List *vertex_tables;
+ List *edge_tables;
+} CreatePropGraphStmt;
+
+typedef struct PropGraphVertex
+{
+ NodeTag type;
+ RangeVar *vtable;
+ List *vkey;
+ List *labels;
+ int location;
+} PropGraphVertex;
+
+typedef struct PropGraphEdge
+{
+ NodeTag type;
+ RangeVar *etable;
+ List *ekey;
+ List *esrckey;
+ char *esrcvertex;
+ List *esrcvertexcols;
+ List *edestkey;
+ char *edestvertex;
+ List *edestvertexcols;
+ List *labels;
+ int location;
+} PropGraphEdge;
+
+typedef struct PropGraphLabelAndProperties
+{
+ NodeTag type;
+ const char *label;
+ struct PropGraphProperties *properties;
+ int location;
+} PropGraphLabelAndProperties;
+
+typedef struct PropGraphProperties
+{
+ NodeTag type;
+ List *properties;
+ bool all;
+ int location;
+} PropGraphProperties;
+
+/* ----------------------
+ * ALTER PROPERTY GRAPH Statement
+ * ----------------------
+ */
+
+typedef enum AlterPropGraphElementKind
+{
+ PROPGRAPH_ELEMENT_KIND_VERTEX = 1,
+ PROPGRAPH_ELEMENT_KIND_EDGE = 2,
+} AlterPropGraphElementKind;
+
+typedef struct AlterPropGraphStmt
+{
+ NodeTag type;
+ RangeVar *pgname;
+ bool missing_ok;
+ List *add_vertex_tables;
+ List *add_edge_tables;
+ List *drop_vertex_tables;
+ List *drop_edge_tables;
+ DropBehavior drop_behavior;
+ AlterPropGraphElementKind element_kind;
+ const char *element_alias;
+ List *add_labels;
+ const char *drop_label;
+ const char *alter_label;
+ PropGraphProperties *add_properties;
+ List *drop_properties;
+} AlterPropGraphStmt;
+
/* ----------------------
* CREATE TRANSFORM Statement
* ----------------------
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4a154606d2..43c94aa9f9 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1860,6 +1860,28 @@ typedef struct InferenceElem
Oid inferopclass; /* OID of att opclass, or InvalidOid */
} InferenceElem;
+/*
+ * GraphLabelRef - label reference in label expression inside GRAPH_TABLE clause
+ */
+typedef struct GraphLabelRef
+{
+ NodeTag type;
+ const char *labelname;
+ int location;
+} GraphLabelRef;
+
+/*
+ * GraphPropertyRef - property reference inside GRAPH_TABLE clause
+ */
+typedef struct GraphPropertyRef
+{
+ Expr xpr;
+ const char *elvarname;
+ const char *propname;
+ Oid typeId;
+ int location;
+} GraphPropertyRef;
+
/*--------------------
* TargetEntry -
* a target entry (used in query target lists)
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 2331acac09..34092ed075 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -135,6 +135,7 @@ PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("depends", DEPENDS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("depth", DEPTH, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("desc", DESC, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("destination", DESTINATION, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("detach", DETACH, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -146,6 +147,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("edge", EDGE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -187,6 +189,8 @@ PG_KEYWORD("generated", GENERATED, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("global", GLOBAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("grant", GRANT, RESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("graph", GRAPH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("graph_table", GRAPH_TABLE, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("greatest", GREATEST, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("group", GROUP_P, RESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("grouping", GROUPING, COL_NAME_KEYWORD, BARE_LABEL)
@@ -284,6 +288,7 @@ PG_KEYWORD("nfd", NFD, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("nfkc", NFKC, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("nfkd", NFKD, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("no", NO, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("node", NODE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("none", NONE, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("normalize", NORMALIZE, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("normalized", NORMALIZED, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -342,6 +347,8 @@ PG_KEYWORD("procedural", PROCEDURAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("procedure", PROCEDURE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("procedures", PROCEDURES, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("program", PROGRAM, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("properties", PROPERTIES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("property", PROPERTY, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("publication", PUBLICATION, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("quote", QUOTE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("range", RANGE, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -355,6 +362,7 @@ PG_KEYWORD("references", REFERENCES, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("referencing", REFERENCING, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("refresh", REFRESH, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("relationship", RELATIONSHIP, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -403,6 +411,7 @@ PG_KEYWORD("skip", SKIP, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("smallint", SMALLINT, COL_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("snapshot", SNAPSHOT, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("some", SOME, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("source", SOURCE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("sql", SQL_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("stable", STABLE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("standalone", STANDALONE_P, UNRESERVED_KEYWORD, BARE_LABEL)
@@ -470,6 +479,7 @@ PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("version", VERSION_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("vertex", VERTEX, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("view", VIEW, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("views", VIEWS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("volatile", VOLATILE, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/include/parser/parse_graphtable.h b/src/include/parser/parse_graphtable.h
new file mode 100644
index 0000000000..7de98a71f7
--- /dev/null
+++ b/src/include/parser/parse_graphtable.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_graphtable.h
+ * parsing of GRAPH_TABLE
+ *
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/parser/parse_graphtable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_GRAPHTABLE_H
+#define PARSE_GRAPHTABLE_H
+
+#include "nodes/parsenodes.h"
+#include "nodes/pg_list.h"
+#include "parser/parse_node.h"
+
+typedef struct GraphTableParseState
+{
+ Oid graphid;
+ List *variables;
+} GraphTableParseState;
+
+extern Node *graph_table_property_reference(ParseState *pstate, ColumnRef *cref);
+
+Node *transformGraphPattern(GraphTableParseState *gpstate, GraphPattern *graph_pattern);
+
+#endif /* PARSE_GRAPHTABLE_H */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index bea2da5496..c11b895b7b 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -81,6 +81,14 @@ extern ParseNamespaceItem *addRangeTableEntryForTableFunc(ParseState *pstate,
Alias *alias,
bool lateral,
bool inFromCl);
+extern ParseNamespaceItem *addRangeTableEntryForGraphTable(ParseState *pstate,
+ Oid graphid,
+ GraphPattern *graph_pattern,
+ List *columns,
+ List *colnames,
+ Alias *alias,
+ bool lateral,
+ bool inFromCl);
extern ParseNamespaceItem *addRangeTableEntryForJoin(ParseState *pstate,
List *colnames,
ParseNamespaceColumn *nscolumns,
diff --git a/src/include/rewrite/rewriteGraphTable.h b/src/include/rewrite/rewriteGraphTable.h
new file mode 100644
index 0000000000..0c0319f5cf
--- /dev/null
+++ b/src/include/rewrite/rewriteGraphTable.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewriteGraphTable.h
+ * Support for rewriting GRAPH_TABLE clauses.
+ *
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/rewrite/rewriteGraphTable.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef REWRITEGRAPHTABLE_H
+#define REWRITEGRAPHTABLE_H
+
+#include "nodes/parsenodes.h"
+
+extern Query *rewriteGraphTable(Query *parsetree, int rt_index);
+
+#endif /* REWRITEGRAPHTABLE_H */
diff --git a/src/include/tcop/cmdtaglist.h b/src/include/tcop/cmdtaglist.h
index 7fdcec6dd9..5582483d06 100644
--- a/src/include/tcop/cmdtaglist.h
+++ b/src/include/tcop/cmdtaglist.h
@@ -48,6 +48,7 @@ PG_CMDTAG(CMDTAG_ALTER_OPERATOR_CLASS, "ALTER OPERATOR CLASS", true, false, fals
PG_CMDTAG(CMDTAG_ALTER_OPERATOR_FAMILY, "ALTER OPERATOR FAMILY", true, false, false)
PG_CMDTAG(CMDTAG_ALTER_POLICY, "ALTER POLICY", true, false, false)
PG_CMDTAG(CMDTAG_ALTER_PROCEDURE, "ALTER PROCEDURE", true, false, false)
+PG_CMDTAG(CMDTAG_ALTER_PROPERTY_GRAPH, "ALTER PROPERTY GRAPH", true, false, false)
PG_CMDTAG(CMDTAG_ALTER_PUBLICATION, "ALTER PUBLICATION", true, false, false)
PG_CMDTAG(CMDTAG_ALTER_ROLE, "ALTER ROLE", false, false, false)
PG_CMDTAG(CMDTAG_ALTER_ROUTINE, "ALTER ROUTINE", true, false, false)
@@ -103,6 +104,7 @@ PG_CMDTAG(CMDTAG_CREATE_OPERATOR_CLASS, "CREATE OPERATOR CLASS", true, false, fa
PG_CMDTAG(CMDTAG_CREATE_OPERATOR_FAMILY, "CREATE OPERATOR FAMILY", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_POLICY, "CREATE POLICY", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_PROCEDURE, "CREATE PROCEDURE", true, false, false)
+PG_CMDTAG(CMDTAG_CREATE_PROPERTY_GRAPH, "CREATE PROPERTY GRAPH", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_PUBLICATION, "CREATE PUBLICATION", true, false, false)
PG_CMDTAG(CMDTAG_CREATE_ROLE, "CREATE ROLE", false, false, false)
PG_CMDTAG(CMDTAG_CREATE_ROUTINE, "CREATE ROUTINE", true, false, false)
@@ -156,6 +158,7 @@ PG_CMDTAG(CMDTAG_DROP_OPERATOR_FAMILY, "DROP OPERATOR FAMILY", true, false, fals
PG_CMDTAG(CMDTAG_DROP_OWNED, "DROP OWNED", true, false, false)
PG_CMDTAG(CMDTAG_DROP_POLICY, "DROP POLICY", true, false, false)
PG_CMDTAG(CMDTAG_DROP_PROCEDURE, "DROP PROCEDURE", true, false, false)
+PG_CMDTAG(CMDTAG_DROP_PROPERTY_GRAPH, "DROP PROPERTY GRAPH", true, false, false)
PG_CMDTAG(CMDTAG_DROP_PUBLICATION, "DROP PUBLICATION", true, false, false)
PG_CMDTAG(CMDTAG_DROP_ROLE, "DROP ROLE", false, false, false)
PG_CMDTAG(CMDTAG_DROP_ROUTINE, "DROP ROUTINE", true, false, false)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index a803eb1291..b659040716 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -165,6 +165,7 @@ typedef struct ArrayType Acl;
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
#define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE)
#define ACL_ALL_RIGHTS_PARAMETER_ACL (ACL_SET|ACL_ALTER_SYSTEM)
+#define ACL_ALL_RIGHTS_PROPGRAPH (ACL_SELECT)
#define ACL_ALL_RIGHTS_SCHEMA (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index bef33d58a2..67d14c58aa 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -233,6 +233,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%token <ival> ICONST PARAM
%token TYPECAST DOT_DOT COLON_EQUALS EQUALS_GREATER
%token LESS_EQUALS GREATER_EQUALS NOT_EQUALS
+%token BRACKET_RIGHT_ARROW LEFT_ARROW_BRACKET MINUS_LEFT_BRACKET RIGHT_BRACKET_MINUS
/*
* Other tokens recognized by plpgsql's lexer interface layer (pl_scanner.c).
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index ae54cb254f..ab9ce9e9ac 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -520,6 +520,49 @@ ERROR: left and right associated data types for operator class options parsing
ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) test_opclass_options_func(internal); -- Ok
ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 5 (int4, int4);
DROP OPERATOR FAMILY alt_opf19 USING btree;
+--
+-- Property Graph
+--
+SET SESSION AUTHORIZATION regress_alter_generic_user1;
+CREATE PROPERTY GRAPH alt_graph1;
+CREATE PROPERTY GRAPH alt_graph2;
+CREATE PROPERTY GRAPH alt_graph3;
+ALTER PROPERTY GRAPH alt_graph1 RENAME TO alt_graph2; -- failed (name conflict)
+ERROR: relation "alt_graph2" already exists
+ALTER PROPERTY GRAPH alt_graph1 RENAME TO alt_graph4; -- OK
+ALTER PROPERTY GRAPH alt_graph2 OWNER TO regress_alter_generic_user2; -- failed (no role membership)
+ERROR: must be able to SET ROLE "regress_alter_generic_user2"
+ALTER PROPERTY GRAPH alt_graph2 OWNER TO regress_alter_generic_user3; -- OK
+ALTER PROPERTY GRAPH alt_graph4 SET SCHEMA alt_nsp2; -- OK
+ALTER PROPERTY GRAPH alt_nsp2.alt_graph4 RENAME TO alt_graph2; -- OK
+ALTER PROPERTY GRAPH alt_graph2 SET SCHEMA alt_nsp2; -- failed (name conflict)
+ERROR: relation "alt_graph2" already exists in schema "alt_nsp2"
+SET SESSION AUTHORIZATION regress_alter_generic_user2;
+CREATE PROPERTY GRAPH alt_graph5;
+ALTER PROPERTY GRAPH alt_graph3 RENAME TO alt_graph5; -- failed (not owner)
+ERROR: must be owner of property graph alt_graph3
+ALTER PROPERTY GRAPH alt_graph5 RENAME TO alt_graph6; -- OK
+ALTER PROPERTY GRAPH alt_graph3 OWNER TO regress_alter_generic_user2; -- failed (not owner)
+ERROR: must be owner of property graph alt_graph3
+ALTER PROPERTY GRAPH alt_graph6 OWNER TO regress_alter_generic_user3; -- failed (no role membership)
+ERROR: must be able to SET ROLE "regress_alter_generic_user3"
+ALTER PROPERTY GRAPH alt_graph3 SET SCHEMA alt_nsp2; -- failed (not owner)
+ERROR: must be owner of property graph alt_graph3
+RESET SESSION AUTHORIZATION;
+SELECT nspname, relname, rolname
+ FROM pg_class c, pg_namespace n, pg_authid a
+ WHERE c.relnamespace = n.oid AND c.relowner = a.oid
+ AND n.nspname in ('alt_nsp1', 'alt_nsp2')
+ AND c.relkind = 'g'
+ ORDER BY nspname, relname;
+ nspname | relname | rolname
+----------+------------+-----------------------------
+ alt_nsp1 | alt_graph2 | regress_alter_generic_user3
+ alt_nsp1 | alt_graph3 | regress_alter_generic_user1
+ alt_nsp1 | alt_graph6 | regress_alter_generic_user2
+ alt_nsp2 | alt_graph2 | regress_alter_generic_user1
+(4 rows)
+
--
-- Statistics
--
@@ -710,7 +753,7 @@ NOTICE: drop cascades to server alt_fserv3
DROP LANGUAGE alt_lang2 CASCADE;
DROP LANGUAGE alt_lang3 CASCADE;
DROP SCHEMA alt_nsp1 CASCADE;
-NOTICE: drop cascades to 28 other objects
+NOTICE: drop cascades to 31 other objects
DETAIL: drop cascades to function alt_func3(integer)
drop cascades to function alt_agg3(integer)
drop cascades to function alt_func4(integer)
@@ -727,6 +770,9 @@ drop cascades to operator family alt_opc1 for access method hash
drop cascades to operator family alt_opc2 for access method hash
drop cascades to operator family alt_opf4 for access method hash
drop cascades to operator family alt_opf2 for access method hash
+drop cascades to property graph alt_graph2
+drop cascades to property graph alt_graph3
+drop cascades to property graph alt_graph6
drop cascades to table alt_regress_1
drop cascades to table alt_regress_2
drop cascades to text search dictionary alt_ts_dict3
@@ -740,12 +786,13 @@ drop cascades to text search template alt_ts_temp2
drop cascades to text search parser alt_ts_prs3
drop cascades to text search parser alt_ts_prs2
DROP SCHEMA alt_nsp2 CASCADE;
-NOTICE: drop cascades to 9 other objects
+NOTICE: drop cascades to 10 other objects
DETAIL: drop cascades to function alt_nsp2.alt_func2(integer)
drop cascades to function alt_nsp2.alt_agg2(integer)
drop cascades to conversion alt_nsp2.alt_conv2
drop cascades to operator alt_nsp2.@-@(integer,integer)
drop cascades to operator family alt_nsp2.alt_opf2 for access method hash
+drop cascades to property graph alt_nsp2.alt_graph2
drop cascades to text search dictionary alt_nsp2.alt_ts_dict2
drop cascades to text search configuration alt_nsp2.alt_ts_conf2
drop cascades to text search template alt_nsp2.alt_ts_temp2
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
new file mode 100644
index 0000000000..9ad28f6fc0
--- /dev/null
+++ b/src/test/regress/expected/create_property_graph.out
@@ -0,0 +1,370 @@
+CREATE SCHEMA create_property_graph_tests;
+GRANT USAGE ON SCHEMA create_property_graph_tests TO PUBLIC;
+SET search_path = create_property_graph_tests;
+CREATE ROLE regress_graph_user1;
+CREATE ROLE regress_graph_user2;
+CREATE PROPERTY GRAPH g1;
+COMMENT ON PROPERTY GRAPH g1 IS 'a graph';
+CREATE PROPERTY GRAPH g1; -- error: duplicate
+ERROR: relation "g1" already exists
+CREATE TABLE t1 (a int, b text);
+CREATE TABLE t2 (i int PRIMARY KEY, j int, k int);
+CREATE TABLE t3 (x int, y text, z text);
+CREATE TABLE e1 (a int, i int, t text, PRIMARY KEY (a, i));
+CREATE TABLE e2 (a int, x int, t text);
+CREATE PROPERTY GRAPH g2
+ VERTEX TABLES (t1 KEY (a), t2 DEFAULT LABEL, t3 KEY (x) LABEL t3l1 LABEL t3l2)
+ EDGE TABLES (
+ e1
+ SOURCE KEY (a) REFERENCES t1 (a)
+ DESTINATION KEY (i) REFERENCES t2 (i),
+ e2 KEY (a, x)
+ SOURCE KEY (a) REFERENCES t1 (a)
+ DESTINATION KEY (x, t) REFERENCES t3 (x, y)
+ );
+-- like g2 but assembled with ALTER
+CREATE PROPERTY GRAPH g3;
+ALTER PROPERTY GRAPH g3 ADD VERTEX TABLES (t1 KEY (a), t2 DEFAULT LABEL);
+ALTER PROPERTY GRAPH g3
+ ADD VERTEX TABLES (t3 KEY (x) LABEL t3l1)
+ ADD EDGE TABLES (
+ e1 SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (i) REFERENCES t2 (i),
+ e2 KEY (a, x) SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (x, t) REFERENCES t3 (x, y)
+ );
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 ADD LABEL t3l2 PROPERTIES ALL COLUMNS ADD LABEL t3l3 PROPERTIES ALL COLUMNS;
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3x; -- error
+ERROR: property graph "g3" element "t3" has no label "t3l3x"
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3;
+ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2); -- fail (TODO: dubious error message)
+ERROR: cannot drop element t2 in property graph g3 because other objects depend on it
+DETAIL: element e1 in property graph g3 depends on element t2 in property graph g3
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2) CASCADE;
+NOTICE: drop cascades to element e1 in property graph g3
+ALTER PROPERTY GRAPH g3 DROP EDGE TABLES (e2);
+CREATE PROPERTY GRAPH g4
+ VERTEX TABLES (
+ t1 KEY (a) NO PROPERTIES,
+ t2 DEFAULT LABEL PROPERTIES (i + j AS i_j, k),
+ t3 KEY (x) LABEL t3l1 PROPERTIES (x, y AS yy) LABEL t3l2 PROPERTIES (x, z AS zz)
+ )
+ EDGE TABLES (
+ e1
+ SOURCE KEY (a) REFERENCES t1 (a)
+ DESTINATION KEY (i) REFERENCES t2 (i)
+ PROPERTIES ALL COLUMNS,
+ e2 KEY (a, x)
+ SOURCE KEY (a) REFERENCES t1 (a)
+ DESTINATION KEY (x, t) REFERENCES t3 (x, y)
+ PROPERTIES ALL COLUMNS
+ );
+ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 ADD PROPERTIES (k * 2 AS kk);
+ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 DROP PROPERTIES (k);
+-- error cases
+CREATE PROPERTY GRAPH gx VERTEX TABLES (xx, yy);
+ERROR: relation "xx" does not exist
+CREATE PROPERTY GRAPH gx VERTEX TABLES (t1 KEY (a), t2 KEY (i), t1 KEY (a));
+ERROR: alias "t1" used more than once as element table
+LINE 1: ...Y GRAPH gx VERTEX TABLES (t1 KEY (a), t2 KEY (i), t1 KEY (a)...
+ ^
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (t1 AS tt KEY (a), t2 KEY (i))
+ EDGE TABLES (
+ e1 SOURCE t1 DESTINATION t2
+ );
+ERROR: source vertex "t1" of edge "e1" does not exist
+LINE 4: e1 SOURCE t1 DESTINATION t2
+ ^
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (t1 KEY (a), t2 KEY (i))
+ EDGE TABLES (
+ e1 SOURCE t1 DESTINATION tx
+ );
+ERROR: destination vertex "tx" of edge "e1" does not exist
+LINE 4: e1 SOURCE t1 DESTINATION tx
+ ^
+COMMENT ON PROPERTY GRAPH gx IS 'not a graph';
+ERROR: relation "gx" does not exist
+ALTER PROPERTY GRAPH g1 OWNER TO regress_graph_user1;
+SET ROLE regress_graph_user1;
+GRANT SELECT ON PROPERTY GRAPH g1 TO regress_graph_user2;
+GRANT UPDATE ON PROPERTY GRAPH g1 TO regress_graph_user2; -- fail
+ERROR: invalid privilege type UPDATE for property graph
+RESET ROLE;
+-- information schema
+SELECT * FROM information_schema.property_graphs ORDER BY property_graph_name;
+ property_graph_catalog | property_graph_schema | property_graph_name
+------------------------+-----------------------------+---------------------
+ regression | create_property_graph_tests | g1
+ regression | create_property_graph_tests | g2
+ regression | create_property_graph_tests | g3
+ regression | create_property_graph_tests | g4
+(4 rows)
+
+SELECT * FROM information_schema.pg_element_tables ORDER BY property_graph_name, element_table_alias;
+ property_graph_catalog | property_graph_schema | property_graph_name | element_table_alias | element_table_kind | table_catalog | table_schema | table_name | element_table_definition
+------------------------+-----------------------------+---------------------+---------------------+--------------------+---------------+-----------------------------+------------+--------------------------
+ regression | create_property_graph_tests | g2 | e1 | EDGE | regression | create_property_graph_tests | e1 |
+ regression | create_property_graph_tests | g2 | e2 | EDGE | regression | create_property_graph_tests | e2 |
+ regression | create_property_graph_tests | g2 | t1 | VERTEX | regression | create_property_graph_tests | t1 |
+ regression | create_property_graph_tests | g2 | t2 | VERTEX | regression | create_property_graph_tests | t2 |
+ regression | create_property_graph_tests | g2 | t3 | VERTEX | regression | create_property_graph_tests | t3 |
+ regression | create_property_graph_tests | g3 | t1 | VERTEX | regression | create_property_graph_tests | t1 |
+ regression | create_property_graph_tests | g3 | t3 | VERTEX | regression | create_property_graph_tests | t3 |
+ regression | create_property_graph_tests | g4 | e1 | EDGE | regression | create_property_graph_tests | e1 |
+ regression | create_property_graph_tests | g4 | e2 | EDGE | regression | create_property_graph_tests | e2 |
+ regression | create_property_graph_tests | g4 | t1 | VERTEX | regression | create_property_graph_tests | t1 |
+ regression | create_property_graph_tests | g4 | t2 | VERTEX | regression | create_property_graph_tests | t2 |
+ regression | create_property_graph_tests | g4 | t3 | VERTEX | regression | create_property_graph_tests | t3 |
+(12 rows)
+
+SELECT * FROM information_schema.pg_element_table_key_columns ORDER BY property_graph_name, element_table_alias, ordinal_position;
+ property_graph_catalog | property_graph_schema | property_graph_name | element_table_alias | column_name | ordinal_position
+------------------------+-----------------------------+---------------------+---------------------+-------------+------------------
+ regression | create_property_graph_tests | g2 | e1 | a | 1
+ regression | create_property_graph_tests | g2 | e1 | i | 2
+ regression | create_property_graph_tests | g2 | e2 | a | 1
+ regression | create_property_graph_tests | g2 | e2 | x | 2
+ regression | create_property_graph_tests | g2 | t1 | a | 1
+ regression | create_property_graph_tests | g2 | t2 | i | 1
+ regression | create_property_graph_tests | g2 | t3 | x | 1
+ regression | create_property_graph_tests | g3 | t1 | a | 1
+ regression | create_property_graph_tests | g3 | t3 | x | 1
+ regression | create_property_graph_tests | g4 | e1 | a | 1
+ regression | create_property_graph_tests | g4 | e1 | i | 2
+ regression | create_property_graph_tests | g4 | e2 | a | 1
+ regression | create_property_graph_tests | g4 | e2 | x | 2
+ regression | create_property_graph_tests | g4 | t1 | a | 1
+ regression | create_property_graph_tests | g4 | t2 | i | 1
+ regression | create_property_graph_tests | g4 | t3 | x | 1
+(16 rows)
+
+SELECT * FROM information_schema.pg_edge_table_components ORDER BY property_graph_name, edge_table_alias, edge_end DESC, ordinal_position;
+ property_graph_catalog | property_graph_schema | property_graph_name | edge_table_alias | vertex_table_alias | edge_end | edge_table_column_name | vertex_table_column_name | ordinal_position
+------------------------+-----------------------------+---------------------+------------------+--------------------+-------------+------------------------+--------------------------+------------------
+ regression | create_property_graph_tests | g2 | e1 | t1 | SOURCE | a | a | 1
+ regression | create_property_graph_tests | g2 | e1 | t2 | DESTINATION | i | i | 1
+ regression | create_property_graph_tests | g2 | e2 | t1 | SOURCE | a | a | 1
+ regression | create_property_graph_tests | g2 | e2 | t3 | DESTINATION | x | x | 1
+ regression | create_property_graph_tests | g2 | e2 | t3 | DESTINATION | t | y | 2
+ regression | create_property_graph_tests | g4 | e1 | t1 | SOURCE | a | a | 1
+ regression | create_property_graph_tests | g4 | e1 | t2 | DESTINATION | i | i | 1
+ regression | create_property_graph_tests | g4 | e2 | t1 | SOURCE | a | a | 1
+ regression | create_property_graph_tests | g4 | e2 | t3 | DESTINATION | x | x | 1
+ regression | create_property_graph_tests | g4 | e2 | t3 | DESTINATION | t | y | 2
+(10 rows)
+
+SELECT * FROM information_schema.pg_element_table_labels ORDER BY property_graph_name, element_table_alias, label_name;
+ property_graph_catalog | property_graph_schema | property_graph_name | element_table_alias | label_name
+------------------------+-----------------------------+---------------------+---------------------+------------
+ regression | create_property_graph_tests | g2 | e1 | e1
+ regression | create_property_graph_tests | g2 | e2 | e2
+ regression | create_property_graph_tests | g2 | t1 | t1
+ regression | create_property_graph_tests | g2 | t2 | t2
+ regression | create_property_graph_tests | g2 | t3 | t3l1
+ regression | create_property_graph_tests | g2 | t3 | t3l2
+ regression | create_property_graph_tests | g3 | t1 | t1
+ regression | create_property_graph_tests | g3 | t3 | t3l1
+ regression | create_property_graph_tests | g3 | t3 | t3l2
+ regression | create_property_graph_tests | g4 | e1 | e1
+ regression | create_property_graph_tests | g4 | e2 | e2
+ regression | create_property_graph_tests | g4 | t1 | t1
+ regression | create_property_graph_tests | g4 | t2 | t2
+ regression | create_property_graph_tests | g4 | t3 | t3l1
+ regression | create_property_graph_tests | g4 | t3 | t3l2
+(15 rows)
+
+SELECT * FROM information_schema.pg_element_table_properties ORDER BY property_graph_name, element_table_alias, property_name;
+ property_graph_catalog | property_graph_schema | property_graph_name | element_table_alias | property_name | property_expression
+------------------------+-----------------------------+---------------------+---------------------+---------------+---------------------
+ regression | create_property_graph_tests | g2 | e1 | a | a
+ regression | create_property_graph_tests | g2 | e1 | i | i
+ regression | create_property_graph_tests | g2 | e1 | t | t
+ regression | create_property_graph_tests | g2 | e2 | a | a
+ regression | create_property_graph_tests | g2 | e2 | t | t
+ regression | create_property_graph_tests | g2 | e2 | x | x
+ regression | create_property_graph_tests | g2 | t1 | a | a
+ regression | create_property_graph_tests | g2 | t1 | b | b
+ regression | create_property_graph_tests | g2 | t2 | i | i
+ regression | create_property_graph_tests | g2 | t2 | j | j
+ regression | create_property_graph_tests | g2 | t2 | k | k
+ regression | create_property_graph_tests | g2 | t3 | x | x
+ regression | create_property_graph_tests | g2 | t3 | y | y
+ regression | create_property_graph_tests | g2 | t3 | z | z
+ regression | create_property_graph_tests | g3 | t1 | a | a
+ regression | create_property_graph_tests | g3 | t1 | b | b
+ regression | create_property_graph_tests | g3 | t3 | x | x
+ regression | create_property_graph_tests | g3 | t3 | y | y
+ regression | create_property_graph_tests | g3 | t3 | z | z
+ regression | create_property_graph_tests | g4 | e1 | a | a
+ regression | create_property_graph_tests | g4 | e1 | i | i
+ regression | create_property_graph_tests | g4 | e1 | t | t
+ regression | create_property_graph_tests | g4 | e2 | a | a
+ regression | create_property_graph_tests | g4 | e2 | t | t
+ regression | create_property_graph_tests | g4 | e2 | x | x
+ regression | create_property_graph_tests | g4 | t2 | i_j | (i + j)
+ regression | create_property_graph_tests | g4 | t2 | kk | (k * 2)
+ regression | create_property_graph_tests | g4 | t3 | x | x
+ regression | create_property_graph_tests | g4 | t3 | yy | y
+ regression | create_property_graph_tests | g4 | t3 | zz | z
+(30 rows)
+
+SELECT * FROM information_schema.pg_label_properties ORDER BY property_graph_name, label_name, property_name;
+ property_graph_catalog | property_graph_schema | property_graph_name | label_name | property_name
+------------------------+-----------------------------+---------------------+------------+---------------
+ regression | create_property_graph_tests | g2 | e1 | a
+ regression | create_property_graph_tests | g2 | e1 | i
+ regression | create_property_graph_tests | g2 | e1 | t
+ regression | create_property_graph_tests | g2 | e2 | a
+ regression | create_property_graph_tests | g2 | e2 | t
+ regression | create_property_graph_tests | g2 | e2 | x
+ regression | create_property_graph_tests | g2 | t1 | a
+ regression | create_property_graph_tests | g2 | t1 | b
+ regression | create_property_graph_tests | g2 | t2 | i
+ regression | create_property_graph_tests | g2 | t2 | j
+ regression | create_property_graph_tests | g2 | t2 | k
+ regression | create_property_graph_tests | g2 | t3l1 | x
+ regression | create_property_graph_tests | g2 | t3l1 | y
+ regression | create_property_graph_tests | g2 | t3l1 | z
+ regression | create_property_graph_tests | g2 | t3l2 | x
+ regression | create_property_graph_tests | g2 | t3l2 | y
+ regression | create_property_graph_tests | g2 | t3l2 | z
+ regression | create_property_graph_tests | g3 | t1 | a
+ regression | create_property_graph_tests | g3 | t1 | b
+ regression | create_property_graph_tests | g3 | t3l1 | x
+ regression | create_property_graph_tests | g3 | t3l1 | y
+ regression | create_property_graph_tests | g3 | t3l1 | z
+ regression | create_property_graph_tests | g3 | t3l2 | x
+ regression | create_property_graph_tests | g3 | t3l2 | y
+ regression | create_property_graph_tests | g3 | t3l2 | z
+ regression | create_property_graph_tests | g4 | e1 | a
+ regression | create_property_graph_tests | g4 | e1 | i
+ regression | create_property_graph_tests | g4 | e1 | t
+ regression | create_property_graph_tests | g4 | e2 | a
+ regression | create_property_graph_tests | g4 | e2 | t
+ regression | create_property_graph_tests | g4 | e2 | x
+ regression | create_property_graph_tests | g4 | t2 | i_j
+ regression | create_property_graph_tests | g4 | t2 | kk
+ regression | create_property_graph_tests | g4 | t3l1 | x
+ regression | create_property_graph_tests | g4 | t3l1 | yy
+ regression | create_property_graph_tests | g4 | t3l2 | x
+ regression | create_property_graph_tests | g4 | t3l2 | zz
+(37 rows)
+
+SELECT * FROM information_schema.pg_labels ORDER BY property_graph_name, label_name;
+ property_graph_catalog | property_graph_schema | property_graph_name | label_name
+------------------------+-----------------------------+---------------------+------------
+ regression | create_property_graph_tests | g2 | e1
+ regression | create_property_graph_tests | g2 | e2
+ regression | create_property_graph_tests | g2 | t1
+ regression | create_property_graph_tests | g2 | t2
+ regression | create_property_graph_tests | g2 | t3l1
+ regression | create_property_graph_tests | g2 | t3l2
+ regression | create_property_graph_tests | g3 | t1
+ regression | create_property_graph_tests | g3 | t3l1
+ regression | create_property_graph_tests | g3 | t3l2
+ regression | create_property_graph_tests | g4 | e1
+ regression | create_property_graph_tests | g4 | e2
+ regression | create_property_graph_tests | g4 | t1
+ regression | create_property_graph_tests | g4 | t2
+ regression | create_property_graph_tests | g4 | t3l1
+ regression | create_property_graph_tests | g4 | t3l2
+(15 rows)
+
+SELECT * FROM information_schema.pg_property_data_types ORDER BY property_graph_name, property_name;
+ property_graph_catalog | property_graph_schema | property_graph_name | property_name | data_type | character_maximum_length | character_octet_length | character_set_catalog | character_set_schema | character_set_name | collation_catalog | collation_schema | collation_name | numeric_precision | numeric_precision_radix | numeric_scale | datetime_precision | interval_type | interval_precision | user_defined_type_catalog | user_defined_type_schema | user_defined_type_name | scope_catalog | scope_schema | scope_name | maximum_cardinality | dtd_identifier
+------------------------+-----------------------------+---------------------+---------------+-----------+--------------------------+------------------------+-----------------------+----------------------+--------------------+-------------------+------------------+----------------+-------------------+-------------------------+---------------+--------------------+---------------+--------------------+---------------------------+--------------------------+------------------------+---------------+--------------+------------+---------------------+----------------
+ regression | create_property_graph_tests | g2 | a | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | a
+ regression | create_property_graph_tests | g2 | b | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | b
+ regression | create_property_graph_tests | g2 | i | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | i
+ regression | create_property_graph_tests | g2 | j | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | j
+ regression | create_property_graph_tests | g2 | k | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | k
+ regression | create_property_graph_tests | g2 | t | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | t
+ regression | create_property_graph_tests | g2 | x | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | x
+ regression | create_property_graph_tests | g2 | y | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | y
+ regression | create_property_graph_tests | g2 | z | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | z
+ regression | create_property_graph_tests | g3 | a | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | a
+ regression | create_property_graph_tests | g3 | b | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | b
+ regression | create_property_graph_tests | g3 | x | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | x
+ regression | create_property_graph_tests | g3 | y | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | y
+ regression | create_property_graph_tests | g3 | z | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | z
+ regression | create_property_graph_tests | g4 | a | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | a
+ regression | create_property_graph_tests | g4 | i | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | i
+ regression | create_property_graph_tests | g4 | i_j | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | i_j
+ regression | create_property_graph_tests | g4 | kk | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | kk
+ regression | create_property_graph_tests | g4 | t | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | t
+ regression | create_property_graph_tests | g4 | x | integer | | | | | | | | | | | | | | | regression | pg_catalog | int4 | | | | | x
+ regression | create_property_graph_tests | g4 | yy | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | yy
+ regression | create_property_graph_tests | g4 | zz | text | | | | | | | | | | | | | | | regression | pg_catalog | text | | | | | zz
+(22 rows)
+
+SELECT * FROM information_schema.pg_property_graph_privileges WHERE grantee LIKE 'regress%' ORDER BY property_graph_name;
+ grantor | grantee | property_graph_catalog | property_graph_schema | property_graph_name | privilege_type | is_grantable
+---------------------+---------------------+------------------------+-----------------------------+---------------------+----------------+--------------
+ regress_graph_user1 | regress_graph_user1 | regression | create_property_graph_tests | g1 | SELECT | YES
+ regress_graph_user1 | regress_graph_user2 | regression | create_property_graph_tests | g1 | SELECT | NO
+(2 rows)
+
+\a\t
+SELECT pg_get_propgraphdef('g2'::regclass);
+CREATE PROPERTY GRAPH create_property_graph_tests.g2
+ VERTEX TABLES (
+ t1 KEY (a) PROPERTIES (a, b),
+ t2 KEY (i) PROPERTIES (i, j, k),
+ t3 KEY (x) LABEL t3l1 PROPERTIES (x, y, z) LABEL t3l2 PROPERTIES (x, y, z)
+ )
+ EDGE TABLES (
+ e1 KEY (a, i) SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (i) REFERENCES t2 (i) PROPERTIES (a, i, t),
+ e2 KEY (a, x) SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (x, t) REFERENCES t3 (x, y) PROPERTIES (a, t, x)
+ )
+SELECT pg_get_propgraphdef('g3'::regclass);
+CREATE PROPERTY GRAPH create_property_graph_tests.g3
+ VERTEX TABLES (
+ t1 KEY (a) PROPERTIES (a, b),
+ t3 KEY (x) LABEL t3l1 PROPERTIES (x, y, z) LABEL t3l2 PROPERTIES (x, y, z)
+ )
+SELECT pg_get_propgraphdef('g4'::regclass);
+CREATE PROPERTY GRAPH create_property_graph_tests.g4
+ VERTEX TABLES (
+ t1 KEY (a) NO PROPERTIES,
+ t2 KEY (i) PROPERTIES ((i + j) AS i_j, (k * 2) AS kk),
+ t3 KEY (x) LABEL t3l1 PROPERTIES (x, y AS yy) LABEL t3l2 PROPERTIES (x, z AS zz)
+ )
+ EDGE TABLES (
+ e1 KEY (a, i) SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (i) REFERENCES t2 (i) PROPERTIES (a, i, t),
+ e2 KEY (a, x) SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (x, t) REFERENCES t3 (x, y) PROPERTIES (a, t, x)
+ )
+SELECT pg_get_propgraphdef('pg_type'::regclass); -- error
+ERROR: "pg_type" is not a property graph
+\a\t
+\dG g1
+ List of relations
+ Schema | Name | Type | Owner
+-----------------------------+------+----------------+---------------------
+ create_property_graph_tests | g1 | property graph | regress_graph_user1
+(1 row)
+
+-- TODO
+\d g1
+Property graph "create_property_graph_tests.g1"
+ Column | Type
+--------+------
+
+\d+ g1
+Property graph "create_property_graph_tests.g1"
+ Column | Type | Storage
+--------+------+---------
+Property graph definition:
+ CREATE PROPERTY GRAPH create_property_graph_tests.g1
+
+DROP TABLE g2; -- error: wrong object type
+ERROR: "g2" is not a table
+HINT: Use DROP PROPERTY GRAPH to remove a property graph.
+DROP PROPERTY GRAPH g1;
+DROP PROPERTY GRAPH g1; -- error: does not exist
+ERROR: property graph "g1" does not exist
+DROP PROPERTY GRAPH IF EXISTS g1;
+NOTICE: property graph "g1" does not exist, skipping
+-- leave for pg_upgrade/pg_dump tests
+--DROP SCHEMA create_property_graph_tests CASCADE;
+DROP ROLE regress_graph_user1, regress_graph_user2;
diff --git a/src/test/regress/expected/graph_table.out b/src/test/regress/expected/graph_table.out
new file mode 100644
index 0000000000..f22ba091a7
--- /dev/null
+++ b/src/test/regress/expected/graph_table.out
@@ -0,0 +1,122 @@
+CREATE SCHEMA graph_table_tests;
+GRANT USAGE ON SCHEMA graph_table_tests TO PUBLIC;
+SET search_path = graph_table_tests;
+CREATE TABLE products (
+ product_no integer PRIMARY KEY,
+ name varchar,
+ price numeric
+);
+CREATE TABLE customers (
+ customer_id integer PRIMARY KEY,
+ name varchar,
+ address varchar
+);
+CREATE TABLE orders (
+ order_id integer PRIMARY KEY,
+ ordered_when date
+);
+CREATE TABLE order_items (
+ order_items_id integer PRIMARY KEY,
+ order_id integer REFERENCES orders (order_id),
+ product_no integer REFERENCES products (product_no),
+ quantity integer
+);
+CREATE TABLE customer_orders (
+ customer_orders_id integer PRIMARY KEY,
+ customer_id integer REFERENCES customers (customer_id),
+ order_id integer REFERENCES orders (order_id)
+);
+CREATE PROPERTY GRAPH myshop
+ VERTEX TABLES (
+ products,
+ customers,
+ orders
+ )
+ EDGE TABLES (
+ order_items KEY (order_items_id)
+ SOURCE KEY (order_id) REFERENCES orders (order_id)
+ DESTINATION KEY (product_no) REFERENCES products (product_no),
+ customer_orders KEY (customer_orders_id)
+ SOURCE KEY (customer_id) REFERENCES customers (customer_id)
+ DESTINATION KEY (order_id) REFERENCES orders (order_id)
+ );
+SELECT customer_name FROM GRAPH_TABLE (xxx MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name)); -- error
+ERROR: relation "xxx" does not exist
+LINE 1: SELECT customer_name FROM GRAPH_TABLE (xxx MATCH (c IS custo...
+ ^
+SELECT customer_name FROM GRAPH_TABLE (pg_class MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name)); -- error
+ERROR: "pg_class" is not a property graph
+LINE 1: SELECT customer_name FROM GRAPH_TABLE (pg_class MATCH (c IS ...
+ ^
+SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (cx.name AS customer_name)); -- error
+ERROR: graph pattern variable "cx" does not exist
+SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.namex AS customer_name)); -- error
+ERROR: property "namex" does not exist
+INSERT INTO products VALUES
+ (1, 'product1', 10),
+ (2, 'product2', 20),
+ (3, 'product3', 30);
+INSERT INTO customers VALUES
+ (1, 'customer1', 'US'),
+ (2, 'customer2', 'CA'),
+ (3, 'customer3', 'GL');
+INSERT INTO orders VALUES
+ (1, '2024-01-01'),
+ (2, '2024-01-02'),
+ (3, '2024-01-03');
+INSERT INTO order_items (order_items_id, order_id, product_no, quantity) VALUES
+ (1, 1, 1, 5),
+ (2, 1, 2, 10),
+ (3, 2, 1, 7);
+INSERT INTO customer_orders (customer_orders_id, customer_id, order_id) VALUES
+ (1, 1, 1),
+ (2, 2, 2);
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name));
+ customer_name
+---------------
+ customer1
+(1 row)
+
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c:customers)-[co:customer_orders]->(o:orders WHERE o.ordered_when = '2024-01-02') COLUMNS (c.name, c.address));
+ name | address
+-----------+---------
+ customer2 | CA
+(1 row)
+
+SELECT * FROM GRAPH_TABLE (myshop MATCH (o IS orders)-[IS customer_orders]->(c IS customers) COLUMNS (c.name, o.ordered_when));
+ name | ordered_when
+------+--------------
+(0 rows)
+
+SELECT * FROM GRAPH_TABLE (myshop MATCH (o IS orders)<-[IS customer_orders]-(c IS customers) COLUMNS (c.name, o.ordered_when));
+ name | ordered_when
+-----------+--------------
+ customer1 | 01-01-2024
+ customer2 | 01-02-2024
+(2 rows)
+
+-- TODO: should approximately match this query:
+SET debug_print_parse = on;
+SELECT customer_name FROM (
+ SELECT c.name AS customer_name
+ FROM customers c, customer_orders _co, orders o
+ WHERE _co.customer_id = c.customer_id
+ AND _co.order_id = o.order_id
+ AND c.address = 'US'
+) myshop;
+ customer_name
+---------------
+ customer1
+(1 row)
+
+RESET debug_print_parse;
+CREATE VIEW customers_us AS SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name));
+SELECT pg_get_viewdef('customers_us'::regclass);
+ pg_get_viewdef
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ SELECT customer_name +
+ FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE ((c.address)::text = 'US'::text))-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name));
+(1 row)
+
+-- leave for pg_upgrade/pg_dump tests
+--DROP SCHEMA graph_table_tests CASCADE;
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index fc42d418bf..432ba471fe 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -34,6 +34,7 @@ CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END
CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig();
CREATE POLICY genpol ON addr_nsp.gentable;
CREATE PROCEDURE addr_nsp.proc(int4) LANGUAGE SQL AS $$ $$;
+CREATE PROPERTY GRAPH addr_nsp.gengraph;
CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
CREATE USER MAPPING FOR regress_addr_user SERVER "integer";
ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user;
@@ -98,7 +99,7 @@ DECLARE
BEGIN
FOR objtype IN VALUES
('table'), ('index'), ('sequence'), ('view'),
- ('materialized view'), ('foreign table'),
+ ('materialized view'), ('foreign table'), ('property graph'),
('table column'), ('foreign table column'),
('aggregate'), ('function'), ('procedure'), ('type'), ('cast'),
('table constraint'), ('domain constraint'), ('conversion'), ('default value'),
@@ -159,6 +160,12 @@ WARNING: error for foreign table,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" d
WARNING: error for foreign table,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist
WARNING: error for foreign table,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei"
WARNING: error for foreign table,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei"
+WARNING: error for property graph,{eins},{}: relation "eins" does not exist
+WARNING: error for property graph,{eins},{integer}: relation "eins" does not exist
+WARNING: error for property graph,{addr_nsp,zwei},{}: relation "addr_nsp.zwei" does not exist
+WARNING: error for property graph,{addr_nsp,zwei},{integer}: relation "addr_nsp.zwei" does not exist
+WARNING: error for property graph,{eins,zwei,drei},{}: cross-database references are not implemented: "eins.zwei.drei"
+WARNING: error for property graph,{eins,zwei,drei},{integer}: cross-database references are not implemented: "eins.zwei.drei"
WARNING: error for table column,{eins},{}: column name must be qualified
WARNING: error for table column,{eins},{integer}: column name must be qualified
WARNING: error for table column,{addr_nsp,zwei},{}: relation "addr_nsp" does not exist
@@ -398,6 +405,7 @@ WITH objects (type, name, args) AS (VALUES
('view', '{addr_nsp, genview}', '{}'),
('materialized view', '{addr_nsp, genmatview}', '{}'),
('foreign table', '{addr_nsp, genftable}', '{}'),
+ ('property graph', '{addr_nsp, gengraph}', '{}'),
('table column', '{addr_nsp, gentable, b}', '{}'),
('foreign table column', '{addr_nsp, genftable, a}', '{}'),
('aggregate', '{addr_nsp, genaggr}', '{int4}'),
@@ -474,6 +482,7 @@ view|addr_nsp|genview|addr_nsp.genview|t
materialized view|addr_nsp|genmatview|addr_nsp.genmatview|t
foreign table|addr_nsp|genftable|addr_nsp.genftable|t
foreign table column|addr_nsp|genftable|addr_nsp.genftable.a|t
+property graph|addr_nsp|gengraph|addr_nsp.gengraph|t
role|NULL|regress_addr_user|regress_addr_user|t
server|NULL|addr_fserv|addr_fserv|t
user mapping|NULL|NULL|regress_addr_user on server integer|t
@@ -518,7 +527,7 @@ DROP PUBLICATION addr_pub;
DROP PUBLICATION addr_pub_schema;
DROP SUBSCRIPTION regress_addr_sub;
DROP SCHEMA addr_nsp CASCADE;
-NOTICE: drop cascades to 14 other objects
+NOTICE: drop cascades to 15 other objects
DETAIL: drop cascades to text search dictionary addr_ts_dict
drop cascades to text search configuration addr_ts_conf
drop cascades to text search template addr_ts_temp
@@ -533,6 +542,7 @@ drop cascades to function genaggr(integer)
drop cascades to type gendomain
drop cascades to function trig()
drop cascades to function proc(integer)
+drop cascades to property graph gengraph
DROP OWNED BY regress_addr_user;
DROP USER regress_addr_user;
--
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 215eb899be..08b84962c8 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -266,3 +266,12 @@ NOTICE: checking pg_subscription {subdbid} => pg_database {oid}
NOTICE: checking pg_subscription {subowner} => pg_authid {oid}
NOTICE: checking pg_subscription_rel {srsubid} => pg_subscription {oid}
NOTICE: checking pg_subscription_rel {srrelid} => pg_class {oid}
+NOTICE: checking pg_propgraph_element {pgepgid} => pg_class {oid}
+NOTICE: checking pg_propgraph_element {pgerelid} => pg_class {oid}
+NOTICE: checking pg_propgraph_element {pgesrcvertexid} => pg_propgraph_element {oid}
+NOTICE: checking pg_propgraph_element {pgedestvertexid} => pg_propgraph_element {oid}
+NOTICE: checking pg_propgraph_label {pglpgid} => pg_class {oid}
+NOTICE: checking pg_propgraph_label {pglelid} => pg_propgraph_element {oid}
+NOTICE: checking pg_propgraph_property {pgppgid} => pg_class {oid}
+NOTICE: checking pg_propgraph_property {pgptypid} => pg_type {oid}
+NOTICE: checking pg_propgraph_property {pgplabelid} => pg_propgraph_label {oid}
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1d8a414eea..09e69e16cd 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -48,7 +48,7 @@ test: create_index create_index_spgist create_view index_including index_includi
# ----------
# Another group of parallel tests
# ----------
-test: create_aggregate create_function_sql create_cast constraints triggers select inherit typed_table vacuum drop_if_exists updatable_views roleattributes create_am hash_func errors infinite_recurse
+test: create_aggregate create_function_sql create_cast constraints triggers select inherit typed_table vacuum drop_if_exists updatable_views roleattributes create_am hash_func errors infinite_recurse create_property_graph
# ----------
# sanity_check does a vacuum, affecting the sort order of SELECT *
@@ -78,7 +78,7 @@ test: brin_bloom brin_multi
# psql depends on create_am
# amutils depends on geometry, create_index_spgist, hash_index, brin
# ----------
-test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps
+test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.icu.utf8 incremental_sort create_role without_overlaps graph_table
# collate.*.utf8 tests cannot be run in parallel with each other
test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index de58d268d3..067d5c2b49 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -456,6 +456,40 @@ ALTER OPERATOR FAMILY alt_opf19 USING btree ADD FUNCTION 5 (int4) test_opclass_o
ALTER OPERATOR FAMILY alt_opf19 USING btree DROP FUNCTION 5 (int4, int4);
DROP OPERATOR FAMILY alt_opf19 USING btree;
+--
+-- Property Graph
+--
+SET SESSION AUTHORIZATION regress_alter_generic_user1;
+CREATE PROPERTY GRAPH alt_graph1;
+CREATE PROPERTY GRAPH alt_graph2;
+CREATE PROPERTY GRAPH alt_graph3;
+
+ALTER PROPERTY GRAPH alt_graph1 RENAME TO alt_graph2; -- failed (name conflict)
+ALTER PROPERTY GRAPH alt_graph1 RENAME TO alt_graph4; -- OK
+ALTER PROPERTY GRAPH alt_graph2 OWNER TO regress_alter_generic_user2; -- failed (no role membership)
+ALTER PROPERTY GRAPH alt_graph2 OWNER TO regress_alter_generic_user3; -- OK
+ALTER PROPERTY GRAPH alt_graph4 SET SCHEMA alt_nsp2; -- OK
+ALTER PROPERTY GRAPH alt_nsp2.alt_graph4 RENAME TO alt_graph2; -- OK
+ALTER PROPERTY GRAPH alt_graph2 SET SCHEMA alt_nsp2; -- failed (name conflict)
+
+SET SESSION AUTHORIZATION regress_alter_generic_user2;
+CREATE PROPERTY GRAPH alt_graph5;
+
+ALTER PROPERTY GRAPH alt_graph3 RENAME TO alt_graph5; -- failed (not owner)
+ALTER PROPERTY GRAPH alt_graph5 RENAME TO alt_graph6; -- OK
+ALTER PROPERTY GRAPH alt_graph3 OWNER TO regress_alter_generic_user2; -- failed (not owner)
+ALTER PROPERTY GRAPH alt_graph6 OWNER TO regress_alter_generic_user3; -- failed (no role membership)
+ALTER PROPERTY GRAPH alt_graph3 SET SCHEMA alt_nsp2; -- failed (not owner)
+
+RESET SESSION AUTHORIZATION;
+
+SELECT nspname, relname, rolname
+ FROM pg_class c, pg_namespace n, pg_authid a
+ WHERE c.relnamespace = n.oid AND c.relowner = a.oid
+ AND n.nspname in ('alt_nsp1', 'alt_nsp2')
+ AND c.relkind = 'g'
+ ORDER BY nspname, relname;
+
--
-- Statistics
--
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
new file mode 100644
index 0000000000..2f0b42ecd2
--- /dev/null
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -0,0 +1,130 @@
+CREATE SCHEMA create_property_graph_tests;
+GRANT USAGE ON SCHEMA create_property_graph_tests TO PUBLIC;
+SET search_path = create_property_graph_tests;
+
+CREATE ROLE regress_graph_user1;
+CREATE ROLE regress_graph_user2;
+
+CREATE PROPERTY GRAPH g1;
+
+COMMENT ON PROPERTY GRAPH g1 IS 'a graph';
+
+CREATE PROPERTY GRAPH g1; -- error: duplicate
+
+CREATE TABLE t1 (a int, b text);
+CREATE TABLE t2 (i int PRIMARY KEY, j int, k int);
+CREATE TABLE t3 (x int, y text, z text);
+
+CREATE TABLE e1 (a int, i int, t text, PRIMARY KEY (a, i));
+CREATE TABLE e2 (a int, x int, t text);
+
+CREATE PROPERTY GRAPH g2
+ VERTEX TABLES (t1 KEY (a), t2 DEFAULT LABEL, t3 KEY (x) LABEL t3l1 LABEL t3l2)
+ EDGE TABLES (
+ e1
+ SOURCE KEY (a) REFERENCES t1 (a)
+ DESTINATION KEY (i) REFERENCES t2 (i),
+ e2 KEY (a, x)
+ SOURCE KEY (a) REFERENCES t1 (a)
+ DESTINATION KEY (x, t) REFERENCES t3 (x, y)
+ );
+
+-- like g2 but assembled with ALTER
+CREATE PROPERTY GRAPH g3;
+ALTER PROPERTY GRAPH g3 ADD VERTEX TABLES (t1 KEY (a), t2 DEFAULT LABEL);
+ALTER PROPERTY GRAPH g3
+ ADD VERTEX TABLES (t3 KEY (x) LABEL t3l1)
+ ADD EDGE TABLES (
+ e1 SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (i) REFERENCES t2 (i),
+ e2 KEY (a, x) SOURCE KEY (a) REFERENCES t1 (a) DESTINATION KEY (x, t) REFERENCES t3 (x, y)
+ );
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 ADD LABEL t3l2 PROPERTIES ALL COLUMNS ADD LABEL t3l3 PROPERTIES ALL COLUMNS;
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3x; -- error
+ALTER PROPERTY GRAPH g3 ALTER VERTEX TABLE t3 DROP LABEL t3l3;
+ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2); -- fail (TODO: dubious error message)
+ALTER PROPERTY GRAPH g3 DROP VERTEX TABLES (t2) CASCADE;
+ALTER PROPERTY GRAPH g3 DROP EDGE TABLES (e2);
+
+CREATE PROPERTY GRAPH g4
+ VERTEX TABLES (
+ t1 KEY (a) NO PROPERTIES,
+ t2 DEFAULT LABEL PROPERTIES (i + j AS i_j, k),
+ t3 KEY (x) LABEL t3l1 PROPERTIES (x, y AS yy) LABEL t3l2 PROPERTIES (x, z AS zz)
+ )
+ EDGE TABLES (
+ e1
+ SOURCE KEY (a) REFERENCES t1 (a)
+ DESTINATION KEY (i) REFERENCES t2 (i)
+ PROPERTIES ALL COLUMNS,
+ e2 KEY (a, x)
+ SOURCE KEY (a) REFERENCES t1 (a)
+ DESTINATION KEY (x, t) REFERENCES t3 (x, y)
+ PROPERTIES ALL COLUMNS
+ );
+
+ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 ADD PROPERTIES (k * 2 AS kk);
+ALTER PROPERTY GRAPH g4 ALTER VERTEX TABLE t2 ALTER LABEL t2 DROP PROPERTIES (k);
+
+-- error cases
+CREATE PROPERTY GRAPH gx VERTEX TABLES (xx, yy);
+CREATE PROPERTY GRAPH gx VERTEX TABLES (t1 KEY (a), t2 KEY (i), t1 KEY (a));
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (t1 AS tt KEY (a), t2 KEY (i))
+ EDGE TABLES (
+ e1 SOURCE t1 DESTINATION t2
+ );
+CREATE PROPERTY GRAPH gx
+ VERTEX TABLES (t1 KEY (a), t2 KEY (i))
+ EDGE TABLES (
+ e1 SOURCE t1 DESTINATION tx
+ );
+COMMENT ON PROPERTY GRAPH gx IS 'not a graph';
+
+
+ALTER PROPERTY GRAPH g1 OWNER TO regress_graph_user1;
+SET ROLE regress_graph_user1;
+GRANT SELECT ON PROPERTY GRAPH g1 TO regress_graph_user2;
+GRANT UPDATE ON PROPERTY GRAPH g1 TO regress_graph_user2; -- fail
+RESET ROLE;
+
+
+-- information schema
+
+SELECT * FROM information_schema.property_graphs ORDER BY property_graph_name;
+SELECT * FROM information_schema.pg_element_tables ORDER BY property_graph_name, element_table_alias;
+SELECT * FROM information_schema.pg_element_table_key_columns ORDER BY property_graph_name, element_table_alias, ordinal_position;
+SELECT * FROM information_schema.pg_edge_table_components ORDER BY property_graph_name, edge_table_alias, edge_end DESC, ordinal_position;
+SELECT * FROM information_schema.pg_element_table_labels ORDER BY property_graph_name, element_table_alias, label_name;
+SELECT * FROM information_schema.pg_element_table_properties ORDER BY property_graph_name, element_table_alias, property_name;
+SELECT * FROM information_schema.pg_label_properties ORDER BY property_graph_name, label_name, property_name;
+SELECT * FROM information_schema.pg_labels ORDER BY property_graph_name, label_name;
+SELECT * FROM information_schema.pg_property_data_types ORDER BY property_graph_name, property_name;
+SELECT * FROM information_schema.pg_property_graph_privileges WHERE grantee LIKE 'regress%' ORDER BY property_graph_name;
+
+
+\a\t
+SELECT pg_get_propgraphdef('g2'::regclass);
+SELECT pg_get_propgraphdef('g3'::regclass);
+SELECT pg_get_propgraphdef('g4'::regclass);
+
+SELECT pg_get_propgraphdef('pg_type'::regclass); -- error
+\a\t
+
+\dG g1
+
+-- TODO
+\d g1
+\d+ g1
+
+DROP TABLE g2; -- error: wrong object type
+
+DROP PROPERTY GRAPH g1;
+
+DROP PROPERTY GRAPH g1; -- error: does not exist
+
+DROP PROPERTY GRAPH IF EXISTS g1;
+
+-- leave for pg_upgrade/pg_dump tests
+--DROP SCHEMA create_property_graph_tests CASCADE;
+
+DROP ROLE regress_graph_user1, regress_graph_user2;
diff --git a/src/test/regress/sql/graph_table.sql b/src/test/regress/sql/graph_table.sql
new file mode 100644
index 0000000000..0632cbda8b
--- /dev/null
+++ b/src/test/regress/sql/graph_table.sql
@@ -0,0 +1,96 @@
+CREATE SCHEMA graph_table_tests;
+GRANT USAGE ON SCHEMA graph_table_tests TO PUBLIC;
+SET search_path = graph_table_tests;
+
+CREATE TABLE products (
+ product_no integer PRIMARY KEY,
+ name varchar,
+ price numeric
+);
+
+CREATE TABLE customers (
+ customer_id integer PRIMARY KEY,
+ name varchar,
+ address varchar
+);
+
+CREATE TABLE orders (
+ order_id integer PRIMARY KEY,
+ ordered_when date
+);
+
+CREATE TABLE order_items (
+ order_items_id integer PRIMARY KEY,
+ order_id integer REFERENCES orders (order_id),
+ product_no integer REFERENCES products (product_no),
+ quantity integer
+);
+
+CREATE TABLE customer_orders (
+ customer_orders_id integer PRIMARY KEY,
+ customer_id integer REFERENCES customers (customer_id),
+ order_id integer REFERENCES orders (order_id)
+);
+
+CREATE PROPERTY GRAPH myshop
+ VERTEX TABLES (
+ products,
+ customers,
+ orders
+ )
+ EDGE TABLES (
+ order_items KEY (order_items_id)
+ SOURCE KEY (order_id) REFERENCES orders (order_id)
+ DESTINATION KEY (product_no) REFERENCES products (product_no),
+ customer_orders KEY (customer_orders_id)
+ SOURCE KEY (customer_id) REFERENCES customers (customer_id)
+ DESTINATION KEY (order_id) REFERENCES orders (order_id)
+ );
+
+SELECT customer_name FROM GRAPH_TABLE (xxx MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name)); -- error
+SELECT customer_name FROM GRAPH_TABLE (pg_class MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name)); -- error
+SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (cx.name AS customer_name)); -- error
+SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.namex AS customer_name)); -- error
+
+INSERT INTO products VALUES
+ (1, 'product1', 10),
+ (2, 'product2', 20),
+ (3, 'product3', 30);
+INSERT INTO customers VALUES
+ (1, 'customer1', 'US'),
+ (2, 'customer2', 'CA'),
+ (3, 'customer3', 'GL');
+INSERT INTO orders VALUES
+ (1, '2024-01-01'),
+ (2, '2024-01-02'),
+ (3, '2024-01-03');
+INSERT INTO order_items (order_items_id, order_id, product_no, quantity) VALUES
+ (1, 1, 1, 5),
+ (2, 1, 2, 10),
+ (3, 2, 1, 7);
+INSERT INTO customer_orders (customer_orders_id, customer_id, order_id) VALUES
+ (1, 1, 1),
+ (2, 2, 2);
+
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name));
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c:customers)-[co:customer_orders]->(o:orders WHERE o.ordered_when = '2024-01-02') COLUMNS (c.name, c.address));
+SELECT * FROM GRAPH_TABLE (myshop MATCH (o IS orders)-[IS customer_orders]->(c IS customers) COLUMNS (c.name, o.ordered_when));
+SELECT * FROM GRAPH_TABLE (myshop MATCH (o IS orders)<-[IS customer_orders]-(c IS customers) COLUMNS (c.name, o.ordered_when));
+
+-- TODO: should approximately match this query:
+SET debug_print_parse = on;
+SELECT customer_name FROM (
+ SELECT c.name AS customer_name
+ FROM customers c, customer_orders _co, orders o
+ WHERE _co.customer_id = c.customer_id
+ AND _co.order_id = o.order_id
+ AND c.address = 'US'
+) myshop;
+RESET debug_print_parse;
+
+CREATE VIEW customers_us AS SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.name AS customer_name));
+
+SELECT pg_get_viewdef('customers_us'::regclass);
+
+-- leave for pg_upgrade/pg_dump tests
+--DROP SCHEMA graph_table_tests CASCADE;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1a6c61f49d..93f9f9c704 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -37,6 +37,7 @@ CREATE FUNCTION addr_nsp.trig() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN END
CREATE TRIGGER t BEFORE INSERT ON addr_nsp.gentable FOR EACH ROW EXECUTE PROCEDURE addr_nsp.trig();
CREATE POLICY genpol ON addr_nsp.gentable;
CREATE PROCEDURE addr_nsp.proc(int4) LANGUAGE SQL AS $$ $$;
+CREATE PROPERTY GRAPH addr_nsp.gengraph;
CREATE SERVER "integer" FOREIGN DATA WRAPPER addr_fdw;
CREATE USER MAPPING FOR regress_addr_user SERVER "integer";
ALTER DEFAULT PRIVILEGES FOR ROLE regress_addr_user IN SCHEMA public GRANT ALL ON TABLES TO regress_addr_user;
@@ -90,7 +91,7 @@ DECLARE
BEGIN
FOR objtype IN VALUES
('table'), ('index'), ('sequence'), ('view'),
- ('materialized view'), ('foreign table'),
+ ('materialized view'), ('foreign table'), ('property graph'),
('table column'), ('foreign table column'),
('aggregate'), ('function'), ('procedure'), ('type'), ('cast'),
('table constraint'), ('domain constraint'), ('conversion'), ('default value'),
@@ -163,6 +164,7 @@ WITH objects (type, name, args) AS (VALUES
('view', '{addr_nsp, genview}', '{}'),
('materialized view', '{addr_nsp, genmatview}', '{}'),
('foreign table', '{addr_nsp, genftable}', '{}'),
+ ('property graph', '{addr_nsp, gengraph}', '{}'),
('table column', '{addr_nsp, gentable, b}', '{}'),
('foreign table column', '{addr_nsp, genftable, a}', '{}'),
('aggregate', '{addr_nsp, genaggr}', '{int4}'),
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 95ae7845d8..4219b72526 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -4054,3 +4054,22 @@ rfile
ws_options
ws_file_info
PathKeyInfo
+
+# TODO
+AlterPropGraphElementKind
+AlterPropGraphStmt
+CreatePropGraphStmt
+FormData_pg_propgraph_element
+FormData_pg_propgraph_label
+FormData_pg_propgraph_property
+GraphElementPattern
+GraphElementPatternKind
+GraphLabelRef
+GraphPattern
+GraphPropertyRef
+GraphTableParseState
+PropGraphEdge
+PropGraphLabelAndProperties
+PropGraphProperties
+PropGraphVertex
+RangeGraphTable
--
2.25.1