SQL Property Graph Queries (SQL/PGQ)

Implementation of SQL property graph queries, according to SQL/PGQ
standard (ISO/IEC 9075-16:2023).

This adds:

- GRAPH_TABLE table function for graph pattern matching
- DDL commands CREATE/ALTER/DROP PROPERTY GRAPH
- several new system catalogs and information schema views
- psql \dG command
- pg_get_propgraphdef() function for pg_dump and psql

A property graph is a relation with a new relkind RELKIND_PROPGRAPH.
It acts like a view in many ways.  It is rewritten to a standard
relational query in the rewriter.  Access privileges act similar to a
security invoker view.  (The security definer variant is not currently
implemented.)

Starting documentation can be found in doc/src/sgml/ddl.sgml and
doc/src/sgml/queries.sgml.

Author: Peter Eisentraut <peter@eisentraut.org>
Author: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reviewed-by: Junwang Zhao <zhjwpku@gmail.com>
Reviewed-by: Ajay Pal <ajay.pal.k@gmail.com>
Reviewed-by: Henson Choi <assam258@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/a855795d-e697-4fa5-8698-d20122126567@eisentraut.org
This commit is contained in:
Peter Eisentraut 2026-03-16 10:14:18 +01:00
parent fd6ecbfa75
commit 2f094e7ac6
122 changed files with 14888 additions and 72 deletions

View file

@ -612,3 +612,57 @@ SELECT * FROM vegetables v,
Unprunable RTIs: 1 3 4 5 6
(51 rows)
-- Property graph test
CREATE PROPERTY GRAPH vegetables_graph
VERTEX TABLES
(
daucus KEY(name) DEFAULT LABEL LABEL vegetables,
brassica KEY(name) DEFAULT LABEL LABEL vegetables
);
EXPLAIN (RANGE_TABLE, COSTS OFF)
SELECT * FROM GRAPH_TABLE (vegetables_graph MATCH (v1 IS vegetables) WHERE v1.genus = 'daucus' COLUMNS (v1.name));
QUERY PLAN
----------------------------------------------
Append
Append RTIs: 1
Child Append RTIs: none
-> Seq Scan on daucus
Filter: (genus = 'daucus'::text)
Scan RTI: 4
Elided Node Type: SubqueryScan
Elided Node RTIs: 2
-> Seq Scan on brassica
Filter: (genus = 'daucus'::text)
Scan RTI: 5
Elided Node Type: SubqueryScan
Elided Node RTIs: 3
RTI 1 (subquery, inherited, in-from-clause):
Eref: "graph_table" (name)
Relation: vegetables_graph
Relation Kind: property_graph
Relation Lock Mode: AccessShareLock
Permission Info Index: 1
Lateral: true
RTI 2 (subquery):
Eref: unnamed_subquery (name)
Lateral: true
RTI 3 (subquery):
Eref: unnamed_subquery (name)
Lateral: true
RTI 4 (relation):
Subplan: unnamed_subquery
Eref: daucus (id, name, genus)
Relation: daucus
Relation Kind: relation
Relation Lock Mode: AccessShareLock
Permission Info Index: 2
RTI 5 (relation):
Subplan: unnamed_subquery_1
Eref: brassica (id, name, genus)
Relation: brassica
Relation Kind: relation
Relation Lock Mode: AccessShareLock
Permission Info Index: 3
Unprunable RTIs: 1 4 5
(41 rows)

View file

@ -505,6 +505,17 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
case RTE_GROUP:
kind = "group";
break;
case RTE_GRAPH_TABLE:
/*
* We should not see RTE of this kind here since property
* graph RTE gets converted to subquery RTE in
* RewriteGraphTable(). In case we decide not to do the
* conversion and leave RTEkind unchanged in future, print
* correct name of RTE kind.
*/
kind = "graph_table";
break;
}
/* Begin group for this specific RTE */
@ -618,6 +629,9 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
case RELKIND_PARTITIONED_INDEX:
relkind = "partitioned_index";
break;
case RELKIND_PROPGRAPH:
relkind = "property_graph";
break;
case '\0':
relkind = NULL;
break;
@ -740,6 +754,12 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
ExplainPropertyFloat("ENR Tuples", NULL, rte->enrtuples, 0, es);
}
/*
* rewriteGraphTable() clears graph_pattern and graph_table_columns
* fields, so skip them. No graph table specific fields are required
* to be printed.
*/
/*
* add_rte_to_flat_rtable will clear groupexprs and securityQuals, so
* skip that field. We have handled inFromCl above, so the only thing

View file

@ -120,3 +120,14 @@ SELECT * FROM vegetables v,
EXPLAIN (RANGE_TABLE, COSTS OFF)
SELECT * FROM vegetables v,
(SELECT * FROM vegetables WHERE genus = 'daucus' OFFSET 0);
-- Property graph test
CREATE PROPERTY GRAPH vegetables_graph
VERTEX TABLES
(
daucus KEY(name) DEFAULT LABEL LABEL vegetables,
brassica KEY(name) DEFAULT LABEL LABEL vegetables
);
EXPLAIN (RANGE_TABLE, COSTS OFF)
SELECT * FROM GRAPH_TABLE (vegetables_graph MATCH (v1 IS vegetables) WHERE v1.genus = 'daucus' COLUMNS (v1.name));

View file

@ -240,6 +240,31 @@
<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-element-label"><structname>pg_propgraph_element_label</structname></link></entry>
<entry>property graph links between elements and labels</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-label-property"><structname>pg_propgraph_label_property</structname></link></entry>
<entry>property graph label-specific property definitions</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>
@ -2141,7 +2166,8 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</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>
@ -6308,6 +6334,498 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</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>
The catalog <structname>pg_propgraph_element</structname> stores
information about the vertices and edges of a property graph, collectively
called the elements of the property graph.
</para>
<table>
<title><structname>pg_propgraph_element</structname> Columns</title>
<tgroup cols="1">
<thead>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
Column Type
</para>
<para>
Description
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>oid</structfield> <type>oid</type>
</para>
<para>
Row identifier
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgepgid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>oid</structfield>)
</para>
<para>
Reference to the property graph that this element belongs to
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgerelid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>oid</structfield>)
</para>
<para>
Reference to the table that contains the data for this property graph element
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgealias</structfield> <type>name</type>
</para>
<para>
The alias of the element. This is a unique identifier for the element
within the graph. It is set when the property graph is defined and
defaults to the name of the underlying element table.
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgekind</structfield> <type>char</type>
</para>
<para>
<literal>v</literal> for a vertex, <literal>e</literal> for an edge
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgesrcvertexid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-propgraph-element"><structname>pg_propgraph_element</structname></link>.<structfield>oid</structfield>)
</para>
<para>
For an edge, a link to the source vertex. (Zero for a vertex.)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgedestvertexid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-propgraph-element"><structname>pg_propgraph_element</structname></link>.<structfield>oid</structfield>)
</para>
<para>
For an edge, a link to the destination vertex. (Zero for a vertex.)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgekey</structfield> <type>int2[]</type>
(references <link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.<structfield>attnum</structfield>)
</para>
<para>
An array of column numbers in the table referenced by
<structname>pgerelid</structname> that defines the key to use for this
element table. (This defaults to the primary key when the property
graph is created.)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgesrckey</structfield> <type>int2[]</type>
(references <link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.<structfield>attnum</structfield>)
</para>
<para>
For an edge, an array of column numbers in the table referenced by
<structname>pgerelid</structname> that defines the source key to use
for this element table. (Null for a vertex.) The combination of
<structfield>pgesrckey</structfield> and
<structfield>pgesrcref</structfield> creates the link between the edge
and the source vertex.
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgesrcref</structfield> <type>int2[]</type>
(references <link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.<structfield>attnum</structfield>)
</para>
<para>
For an edge, an array of column numbers in the table reached via
<structname>pgesrcvertexid</structname>. (Null for a vertex.) The
combination of <structfield>pgesrckey</structfield> and
<structfield>pgesrcref</structfield> creates the link between the edge
and the source vertex.
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgesrceqop</structfield> <type>oid[]</type>
(references <link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.<structfield>oid</structfield>)
</para>
<para>
For an edge, an array of equality operators for
<structfield>pgesrcref</structfield> =
<structfield>pgesrckey</structfield> comparison. (Null for a vertex.)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgedestkey</structfield> <type>int2[]</type>
(references <link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.<structfield>attnum</structfield>)
</para>
<para>
For an edge, an array of column numbers in the table referenced by
<structname>pgerelid</structname> that defines the destination key to use
for this element table. (Null for a vertex.) The combination of
<structfield>pgedestkey</structfield> and
<structfield>pgedestref</structfield> creates the link between the edge
and the destination vertex.
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgedestref</structfield> <type>int2[]</type>
(references <link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.<structfield>attnum</structfield>)
</para>
<para>
For an edge, an array of column numbers in the table reached via
<structname>pgedestvertexid</structname>. (Null for a vertex.) The
combination of <structfield>pgedestkey</structfield> and
<structfield>pgedestref</structfield> creates the link between the edge
and the destination vertex.
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgedesteqop</structfield> <type>oid[]</type>
(references <link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.<structfield>oid</structfield>)
</para>
<para>
For an edge, an array of equality operators for
<structfield>pgedestref</structfield> =
<structfield>pgedestkey</structfield> comparison. (Null for a vertex.)
</para></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="catalog-pg-propgraph-element-label">
<title><structname>pg_propgraph_element_label</structname></title>
<indexterm zone="catalog-pg-propgraph-element-label">
<primary>pg_propgraph_element_label</primary>
</indexterm>
<para>
The catalog <structname>pg_propgraph_element_label</structname> stores
information about which labels apply to which elements.
</para>
<table>
<title><structname>pg_propgraph_element_label</structname> Columns</title>
<tgroup cols="1">
<thead>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
Column Type
</para>
<para>
Description
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>oid</structfield> <type>oid</type>
</para>
<para>
Row identifier
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgellabelid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-propgraph-label"><structname>pg_propgraph_label</structname></link>.<structfield>oid</structfield>)
</para>
<para>
Reference to the label
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgelelid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-propgraph-element"><structname>pg_propgraph_element</structname></link>.<structfield>oid</structfield>)
</para>
<para>
Reference to the element
</para></entry>
</row>
</tbody>
</tgroup>
</table>
</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>
The catalog <structname>pg_propgraph_label</structname> stores
information about the labels in a property graph.
</para>
<table>
<title><structname>pg_propgraph_label</structname> Columns</title>
<tgroup cols="1">
<thead>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
Column Type
</para>
<para>
Description
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>oid</structfield> <type>oid</type>
</para>
<para>
Row identifier
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pglpgid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>oid</structfield>)
</para>
<para>
Reference to the property graph that this label belongs to
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgllabel</structfield> <type>name</type>
</para>
<para>
The name of the label. This is unique among the labels in a graph.
</para></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="catalog-pg-propgraph-label-property">
<title><structname>pg_propgraph_label_property</structname></title>
<indexterm zone="catalog-pg-propgraph-label-property">
<primary>pg_propgraph_label_property</primary>
</indexterm>
<para>
The catalog <structname>pg_propgraph_label_property</structname> stores
information about the properties in a property graph that are specific to a
label. In particular, this stores the expression that defines the
property.
</para>
<table>
<title><structname>pg_propgraph_label_property</structname> Columns</title>
<tgroup cols="1">
<thead>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
Column Type
</para>
<para>
Description
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>oid</structfield> <type>oid</type>
</para>
<para>
Row identifier
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>plppropid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-propgraph-property"><structname>pg_propgraph_property</structname></link>.<structfield>oid</structfield>)
</para>
<para>
Reference to the property
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>plpellabelid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-propgraph-element-label"><structname>pg_propgraph_element_label</structname></link>.<structfield>oid</structfield>)
</para>
<para>
Reference to the label (indirectly via
<structname>pg_propgraph_element_label</structname>, which then links
to <structname>pg_propgraph_label</structname>)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>plpexpr</structfield> <type>pg_node_tree</type>
</para>
<para>
Expression tree (in <function>nodeToString()</function> representation)
for the property's definition. The expression references the table
reached via <structname>pg_propgraph_element_label</structname> and
<structname>pg_propgraph_element</structname>.
</para></entry>
</row>
</tbody>
</tgroup>
</table>
</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>
The catalog <structname>pg_propgraph_property</structname> stores
information about the properties in a property graph. This only stores
information that applies to a property throughout the graph, independent of
what label or element it is on. Additional information, including the
actual expressions that define the properties are in the catalog <link
linkend="catalog-pg-propgraph-label-property"><structname>pg_propgraph_label_property</structname></link>.
</para>
<table>
<title><structname>pg_propgraph_property</structname> Columns</title>
<tgroup cols="1">
<thead>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
Column Type
</para>
<para>
Description
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>oid</structfield> <type>oid</type>
</para>
<para>
Row identifier
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgppgid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>oid</structfield>)
</para>
<para>
Reference to the property graph that this property belongs to
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgpname</structfield> <type>name</type>
</para>
<para>
The name of the property. This is unique among the properties in a
graph.
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgptypid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-type"><structname>pg_type</structname></link>.<structfield>oid</structfield>)
</para>
<para>
The data type of this property. (This is required to be fixed for a
given property in a property graph, even if the property is defined
multiple times in different elements and labels.)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgptypmod</structfield> <type>int4</type>
</para>
<para>
<literal>typmod</literal> to be applied to the data type of this property.
(This is required to be fixed for a given property in a property graph,
even if the property is defined multiple times in different elements and
labels.)
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>pgpcollation</structfield> <type>oid</type>
(references <link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.<structfield>oid</structfield>)
</para>
<para>
The defined collation of this property, or zero if the property is not of
a collatable data type. (This is required to be fixed for a given
property in a property graph, even if the property is defined multiple
times in different elements and labels.)
</para></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="catalog-pg-publication">
<title><structname>pg_publication</structname></title>

View file

@ -2315,6 +2315,8 @@ REVOKE ALL ON accounts FROM PUBLIC;
This privilege is also needed to reference existing column values in
<command>UPDATE</command>, <command>DELETE</command>,
or <command>MERGE</command>.
For property graphs, this privilege allows the object to be referenced
in a <literal>GRAPH_TABLE</literal> clause.
For sequences, this privilege also allows use of the
<function>currval</function> function.
For large objects, this privilege allows the object to be read.
@ -5693,6 +5695,242 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= 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, as an alternative
to the usual (in SQL) approach of representing database contents 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 infrastructure, 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 o.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 nouns as labels and
edges typically have verbs or phrases derived from verbs as labels, such as
<quote>has</quote>, <quote>contains</quote>, or something specific like
<quote>approved_by</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_placed
);
</programlisting>
</para>
<para>
With this definition, we can write a query like this:
<programlisting>
SELECT customer_name FROM GRAPH_TABLE (myshop MATCH (c IS customer)-[IS has_placed]->(o IS "order" WHERE o.ordered_when = current_date) COLUMNS (c.name AS customer_name));
</programlisting>
With the new labels the <literal>MATCH</literal> clause is now more intuitive.
</para>
<para>
Notice that the label <literal>order</literal> is quoted. If we run above
statements without adding quotes around <literal>order</literal>, we will get
a syntax error since <literal>order</literal> is a keyword.
</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.) Then we can run a query like this (incomplete):
<programlisting>
SELECT ... FROM GRAPH_TABLE (myshop MATCH (IS 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>
Using more than one label associated with an element table and each label
exposing a different set of properties, the same relational data, and the
graph structure contained therein, can be exposed through multiple
co-existing logical views, which can be queried using graph pattern matching
constructs.
</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>

View file

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

View file

@ -1677,6 +1677,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>

File diff suppressed because it is too large Load diff

View file

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

View file

@ -0,0 +1,12 @@
ALL_DIFFERENT
BINDING_COUNT
ELEMENT_ID
ELEMENT_NUMBER
EXPORT
GRAPH
GRAPH_TABLE
MATCHNUM
PATH_LENGTH
PATH_NAME
PROPERTY_EXISTS
SAME

View file

@ -863,6 +863,11 @@ ORDER BY p;
to columns provided by preceding <literal>FROM</literal> items in any case.
</para>
<para>
A <literal>GRAPH_TABLE</literal> <literal>FROM</literal> item can also
always contain lateral references.
</para>
<para>
A <literal>LATERAL</literal> item can appear at the top level in the
<literal>FROM</literal> list, or within a <literal>JOIN</literal> tree. In the latter
@ -2771,4 +2776,161 @@ 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>
<para>
These characters can also be separated by whitespace, for example:
<programlisting>
( ) - [ ] - > ( )
</programlisting>
</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 by <literal>IS
<replaceable>labelname</replaceable></literal>. For example, this would
match all vertices with the label <literal>person</literal>:
<programlisting>
(IS person)
</programlisting>
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>
(IS person)-[IS has]->(IS account)
</programlisting>
Multiple labels can also be matched, using <quote>or</quote> semantics:
<programlisting>
(IS person)-[IS has]->(IS account|creditcard)
</programlisting>
</para>
<para>
Recall that edges are directed. The other direction is also possible in a
path pattern, for example:
<programlisting>
(IS account)&lt;-[IS has]-(IS person)
</programlisting>
It is also possible to match both directions:
<programlisting>
(IS person)-[IS is_friend_of]-(IS 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>
(IS person)->(IS account)
(IS account)&lt;-(IS person)
(IS person)-(IS 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 IS person)-[h IS has]->(a IS 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>
(IS person)-[IS has]->(a IS account WHERE a.type = 'savings')
</programlisting>
</para>
<!-- TODO: multiple path patterns in a graph pattern (comma-separated) -->
<!-- TODO: quantifiers -->
</sect2>
</sect1>
</chapter>

View file

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

View file

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

View file

@ -0,0 +1,299 @@
<!--
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_definition</replaceable> [, ...] ) ]
[ {EDGE|RELATIONSHIP} TABLES ( <replaceable class="parameter">edge_table_definition</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>
<command>ALTER PROPERTY GRAPH</command> changes the definition of an
existing property graph. There are several subforms:
<variablelist>
<varlistentry>
<term><literal>ADD {VERTEX|NODE|EDGE|RELATIONSHIP} TABLES</literal></term>
<listitem>
<para>
This form adds new vertex or edge tables to the property graph, using the
same syntax as <link linkend="sql-create-property-graph"><command>CREATE
PROPERTY GRAPH</command></link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>DROP {VERTEX|NODE|EDGE|RELATIONSHIP} TABLES</literal></term>
<listitem>
<para>
This form removes vertex or edge tables from the property graph. (Only
the association of the tables with the graph is removed. The tables
themself are not dropped.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ALTER {VERTEX|NODE|EDGE|RELATIONSHIP} TABLE ... ADD LABEL</literal></term>
<listitem>
<para>
This form adds a new label to an existing vertex or edge table, using
the same syntax as <link
linkend="sql-create-property-graph"><command>CREATE PROPERTY
GRAPH</command></link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ALTER {VERTEX|NODE|EDGE|RELATIONSHIP} TABLE ... DROP LABEL</literal></term>
<listitem>
<para>
This form removes a label from an existing vertex or edge table.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ALTER {VERTEX|NODE|EDGE|RELATIONSHIP} TABLE ... ALTER LABEL ... ADD PROPERTIES</literal></term>
<listitem>
<para>
This form adds new properties to an existing label on an existing
vertex or edge table.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ALTER {VERTEX|NODE|EDGE|RELATIONSHIP} TABLE ... ALTER LABEL ... DROP PROPERTIES</literal></term>
<listitem>
<para>
This form removes properties from an existing label on an existing
vertex or edge table.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>OWNER</literal></term>
<listitem>
<para>
This form changes the owner of the property graph to the specified user.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>RENAME</literal></term>
<listitem>
<para>
This form changes the name of a property graph.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>SET SCHEMA</literal></term>
<listitem>
<para>
This form moves the property graph into another schema.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
You must own the property graph to use <command>ALTER PROPERTY
GRAPH</command>. To change a property graph's schema, you must also have
<literal>CREATE</literal> privilege on the new schema. To alter the owner,
you must be able to <literal>SET ROLE</literal> to the new owning role, and
that role must have <literal>CREATE</literal> privilege on the property
graph's schema. (These restrictions enforce that altering the owner
doesn't do anything you couldn't do by dropping and recreating the property
graph. However, a superuser can alter ownership of any property graph
anyway.)
</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><replaceable class="parameter">vertex_table_definition</replaceable></term>
<term><replaceable class="parameter">edge_table_definition</replaceable></term>
<listitem>
<para>
See <link linkend="sql-create-property-graph"><command>CREATE PROPERTY
GRAPH</command></link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">vertex_table_alias</replaceable></term>
<term><replaceable class="parameter">edge_table_alias</replaceable></term>
<listitem>
<para>
The alias of an existing vertex or edge table to operate on. (Note that
the alias is potentially different from the name of the underlying
table, if the vertex or edge table was created with <literal>AS
<replaceable class="parameter">alias</replaceable></literal>.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">label_name</replaceable></term>
<term><replaceable class="parameter">property_name</replaceable></term>
<term><replaceable class="parameter">expression</replaceable></term>
<listitem>
<para>
See <link linkend="sql-create-property-graph"><command>CREATE PROPERTY
GRAPH</command></link>.
</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>Notes</title>
<para>
The consistency checks on a property graph described at <xref
linkend="sql-create-property-graph-notes"/> must be maintained by
<command>ALTER PROPERTY GRAPH</command> operations. In some cases, it
might be necessary to make multiple alterations in a single command to
satisfy the checks.
</para>
</refsect1>
<refsect1>
<title>Examples</title>
<para>
<programlisting>
ALTER PROPERTY GRAPH g1 ADD VERTEX TABLES (v2);
ALTER PROPERTY GRAPH g1 ALTER VERTEX TABLE v1 DROP LABEL foo;
ALTER PROPERTY GRAPH g1 RENAME TO g2;
</programlisting></para>
</refsect1>
<refsect1>
<title>Compatibility</title>
<para>
<command>ALTER 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-create-property-graph"/></member>
<member><xref linkend="sql-drop-property-graph"/></member>
</simplelist>
</refsect1>
</refentry>

View file

@ -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> [, ...] ] ) ] |

View file

@ -0,0 +1,318 @@
<!--
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_name</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 the source and destination vertex tables respectively.
</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 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 id="sql-create-property-graph-notes">
<title>Notes</title>
<para>
The following consistency checks must be satisfied by a property graph definition:
<itemizedlist>
<listitem>
<para>
In a property graph, labels with the same name applied to different
property graph elements must have the same number of properties and
those properties must have the same names. For example, the following
would be allowed:
<programlisting>
CREATE PROPERTY GRAPH g1
VERTEX TABLES (
v1 LABEL foo PROPERTIES (x, y),
v2 LABEL foo PROPERTIES (x, y)
) ...
</programlisting>
but this would not:
<programlisting>
CREATE PROPERTY GRAPH g1
VERTEX TABLES (
v1 LABEL foo PROPERTIES (x, y),
v2 LABEL foo PROPERTIES (z)
) ...
</programlisting></para>
</listitem>
<listitem>
<para>
In a property graph, all properties with the same name must have the
same data type, independent of which label they are on. For example,
this would be allowed:
<programlisting>
CREATE TABLE v1 (a int, b int);
CREATE TABLE v2 (a int, b int);
CREATE PROPERTY GRAPH g1
VERTEX TABLES (
v1 LABEL foo PROPERTIES (a, b),
v2 LABEL bar PROPERTIES (a, b)
) ...
</programlisting>
but this would not:
<programlisting>
CREATE TABLE v1 (a int, b int);
CREATE TABLE v2 (a int, b varchar);
CREATE PROPERTY GRAPH g1
VERTEX TABLES (
v1 LABEL foo PROPERTIES (a, b),
v2 LABEL bar PROPERTIES (a, b)
) ...
</programlisting></para>
</listitem>
<listitem>
<para>
For each property graph element, all properties with the same name must
have the same expression for each label. For example, this would be
allowed:
<programlisting>
CREATE PROPERTY GRAPH g1
VERTEX TABLES (
v1 LABEL foo PROPERTIES (a * 2 AS x) LABEL bar PROPERTIES (a * 2 AS x)
) ...
</programlisting>
but this would not:
<programlisting>
CREATE PROPERTY GRAPH g1
VERTEX TABLES (
v1 LABEL foo PROPERTIES (a * 2 AS x) LABEL bar PROPERTIES (a * 10 AS x)
) ...
</programlisting></para>
</listitem>
</itemizedlist>
</para>
<para>
Property graphs are queried using the <literal>GRAPH_TABLE</literal> clause
of <xref linkend="sql-select"/>.
</para>
<para>
Access to the base relations underlying the <literal>GRAPH_TABLE</literal>
clause is determined by the permissions of the user executing the query,
rather than the property graph owner. Thus, the user of a property graph must
have the relevant permissions on the property graph and base relations
underlying the <literal>GRAPH_TABLE</literal> clause.
</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>

View file

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

View file

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

View file

@ -1293,7 +1293,7 @@ SELECT $1 \parse stmt1
<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
@ -1333,9 +1333,9 @@ SELECT $1 \parse stmt1
<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>
<para>
@ -1643,6 +1643,7 @@ SELECT $1 \parse stmt1
<varlistentry id="app-psql-meta-command-de">
<term><literal>\dE[Sx+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<term><literal>\dG[Sx+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<term><literal>\di[Sx+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<term><literal>\dm[Sx+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
<term><literal>\ds[Sx+] [ <link linkend="app-psql-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term>
@ -1651,10 +1652,10 @@ SELECT $1 \parse stmt1
<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

View file

@ -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> [, ...]

View file

@ -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> [, ...] ] ) ] |

View file

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

View file

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

View file

@ -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:
@ -700,6 +708,7 @@ objectNamesToOids(ObjectType objtype, List *objnames, bool is_grant)
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
case OBJECT_PROPGRAPH:
/*
* Here, we don't use get_object_address(). It requires that the
@ -817,6 +826,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:
@ -1022,6 +1035,10 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT;
errormsg = gettext_noop("invalid privilege type %s for large object");
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);
@ -1836,11 +1853,20 @@ ExecGrant_Relation(InternalGrant *istmt)
errmsg("\"%s\" is not a sequence",
NameStr(pg_class_tuple->relname))));
if (istmt->objtype == OBJECT_PROPGRAPH &&
pg_class_tuple->relkind != RELKIND_PROPGRAPH)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a property graph",
NameStr(pg_class_tuple->relname))));
/* Adjust the default permissions based on object type */
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
{
if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
else if (pg_class_tuple->relkind == RELKIND_PROPGRAPH)
this_privileges = ACL_ALL_RIGHTS_PROPGRAPH;
else
this_privileges = ACL_ALL_RIGHTS_RELATION;
}
@ -1934,6 +1960,9 @@ ExecGrant_Relation(InternalGrant *istmt)
case RELKIND_SEQUENCE:
old_acl = acldefault(OBJECT_SEQUENCE, ownerId);
break;
case RELKIND_PROPGRAPH:
old_acl = acldefault(OBJECT_PROPGRAPH, ownerId);
break;
default:
old_acl = acldefault(OBJECT_TABLE, ownerId);
break;
@ -2731,6 +2760,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;
@ -2857,6 +2889,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;
@ -2993,6 +3028,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);

View file

@ -51,6 +51,11 @@
#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_element_label.h"
#include "catalog/pg_propgraph_label.h"
#include "catalog/pg_propgraph_label_property.h"
#include "catalog/pg_propgraph_property.h"
#include "catalog/pg_publication.h"
#include "catalog/pg_publication_namespace.h"
#include "catalog/pg_publication_rel.h"
@ -1514,6 +1519,11 @@ doDeletion(const ObjectAddress *object, int flags)
case AccessMethodRelationId:
case AccessMethodOperatorRelationId:
case AccessMethodProcedureRelationId:
case PropgraphElementRelationId:
case PropgraphElementLabelRelationId:
case PropgraphLabelRelationId:
case PropgraphLabelPropertyRelationId:
case PropgraphPropertyRelationId:
case NamespaceRelationId:
case TSParserRelationId:
case TSDictionaryRelationId:
@ -2267,6 +2277,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;

View file

@ -3009,3 +3009,369 @@ 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_element_label el, pg_propgraph_label l
WHERE pg.relnamespace = npg.oid
AND e.pgepgid = pg.oid
AND el.pgelelid = e.oid
AND el.pgellabelid = 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_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(plp.plpexpr, e.pgerelid) AS character_data) AS property_expression
FROM pg_namespace npg, pg_class pg, pg_propgraph_element e, pg_propgraph_element_label el, pg_propgraph_label_property plp, pg_propgraph_property pr
WHERE pg.relnamespace = npg.oid
AND e.pgepgid = pg.oid
AND el.pgelelid = e.oid
AND plp.plpellabelid = el.oid
AND pr.oid = plp.plppropid
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_element_label el, pg_propgraph_label_property plp, pg_propgraph_property pr
WHERE pg.relnamespace = npg.oid
AND e.pgepgid = pg.oid
AND el.pgelelid = e.oid
AND plp.plpellabelid = el.oid
AND pr.oid = plp.plppropid
AND el.pgellabelid = 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 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_label l
WHERE pg.relnamespace = npg.oid
AND l.pglpgid = pg.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 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(current_database() AS sql_identifier) AS collation_catalog,
CAST(nc.nspname AS sql_identifier) AS collation_schema,
CAST(c.collname AS sql_identifier) AS collation_name,
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)
LEFT JOIN (pg_collation c JOIN pg_namespace nc ON (c.collnamespace = nc.oid))
ON pgp.pgpcollation = c.oid AND (nc.nspname, c.collname) <> ('pg_catalog', 'default')
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;

View file

@ -47,6 +47,11 @@
#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_element_label.h"
#include "catalog/pg_propgraph_label.h"
#include "catalog/pg_propgraph_label_property.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 +375,76 @@ 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 element label",
PropgraphElementLabelRelationId,
PropgraphElementLabelObjectIndexId,
-1,
-1,
Anum_pg_propgraph_element_label_oid,
InvalidAttrNumber,
InvalidAttrNumber,
InvalidAttrNumber,
InvalidAttrNumber,
-1,
false
},
{
"property graph label",
PropgraphLabelRelationId,
PropgraphLabelObjectIndexId,
PROPGRAPHLABELOID,
PROPGRAPHLABELNAME,
Anum_pg_propgraph_label_oid,
Anum_pg_propgraph_label_pgllabel,
InvalidAttrNumber,
InvalidAttrNumber,
InvalidAttrNumber,
-1,
false
},
{
"property graph label property",
PropgraphLabelPropertyRelationId,
PropgraphLabelPropertyObjectIndexId,
-1,
-1,
Anum_pg_propgraph_label_property_oid,
InvalidAttrNumber,
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,
@ -679,6 +754,9 @@ static const struct object_type_map
{
"foreign table", OBJECT_FOREIGN_TABLE
},
{
"property graph", OBJECT_PROPGRAPH
},
{
"table column", OBJECT_COLUMN
},
@ -814,6 +892,15 @@ static const struct object_type_map
{
"policy", OBJECT_POLICY
},
{
"property graph element", -1
},
{
"property graph label", -1
},
{
"property graph property", -1
},
{
"publication", OBJECT_PUBLICATION
},
@ -949,6 +1036,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,
@ -1361,6 +1449,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,
@ -2280,6 +2375,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:
@ -2399,6 +2495,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:
@ -3976,6 +4073,156 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
break;
}
case PropgraphElementRelationId:
{
HeapTuple tup;
Form_pg_propgraph_element pgeform;
tup = SearchSysCache1(PROPGRAPHELOID, ObjectIdGetDatum(object->objectId));
if (!HeapTupleIsValid(tup))
{
if (!missing_ok)
elog(ERROR, "cache lookup failed for property graph element %u",
object->objectId);
break;
}
pgeform = (Form_pg_propgraph_element) GETSTRUCT(tup);
if (pgeform->pgekind == PGEKIND_VERTEX)
/* translator: followed by, e.g., "property graph %s" */
appendStringInfo(&buffer, _("vertex %s of "), NameStr(pgeform->pgealias));
else if (pgeform->pgekind == PGEKIND_EDGE)
/* translator: followed by, e.g., "property graph %s" */
appendStringInfo(&buffer, _("edge %s of "), NameStr(pgeform->pgealias));
else
appendStringInfo(&buffer, "??? element %s of ", NameStr(pgeform->pgealias));
getRelationDescription(&buffer, pgeform->pgepgid, false);
ReleaseSysCache(tup);
break;
}
case PropgraphElementLabelRelationId:
{
Relation rel;
SysScanDesc scan;
ScanKeyData key[1];
HeapTuple tuple;
Form_pg_propgraph_element_label pgelform;
ObjectAddress oa;
rel = table_open(PropgraphElementLabelRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_propgraph_element_label_oid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
scan = systable_beginscan(rel, PropgraphElementLabelObjectIndexId, true, NULL, 1, key);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
{
if (!missing_ok)
elog(ERROR, "could not find tuple for element label %u", object->objectId);
systable_endscan(scan);
table_close(rel, AccessShareLock);
break;
}
pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tuple);
appendStringInfo(&buffer, _("label %s of "), get_propgraph_label_name(pgelform->pgellabelid));
ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
appendStringInfoString(&buffer, getObjectDescription(&oa, false));
systable_endscan(scan);
table_close(rel, AccessShareLock);
break;
}
case PropgraphLabelRelationId:
{
HeapTuple tuple;
Form_pg_propgraph_label pglform;
tuple = SearchSysCache1(PROPGRAPHLABELOID, object->objectId);
if (!HeapTupleIsValid(tuple))
{
if (!missing_ok)
elog(ERROR, "could not find tuple for label %u", object->objectId);
break;
}
pglform = (Form_pg_propgraph_label) GETSTRUCT(tuple);
/* translator: followed by, e.g., "property graph %s" */
appendStringInfo(&buffer, _("label %s of "), NameStr(pglform->pgllabel));
getRelationDescription(&buffer, pglform->pglpgid, false);
ReleaseSysCache(tuple);
break;
}
case PropgraphLabelPropertyRelationId:
{
Relation rel;
SysScanDesc scan;
ScanKeyData key[1];
HeapTuple tuple;
Form_pg_propgraph_label_property plpform;
ObjectAddress oa;
rel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_propgraph_label_property_oid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
scan = systable_beginscan(rel, PropgraphLabelPropertyObjectIndexId, true, NULL, 1, key);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
{
if (!missing_ok)
elog(ERROR, "could not find tuple for label property %u", object->objectId);
systable_endscan(scan);
table_close(rel, AccessShareLock);
break;
}
plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tuple);
appendStringInfo(&buffer, _("property %s of "), get_propgraph_property_name(plpform->plppropid));
ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
appendStringInfoString(&buffer, getObjectDescription(&oa, false));
systable_endscan(scan);
table_close(rel, AccessShareLock);
break;
}
case PropgraphPropertyRelationId:
{
HeapTuple tuple;
Form_pg_propgraph_property pgpform;
tuple = SearchSysCache1(PROPGRAPHPROPOID, object->objectId);
if (!HeapTupleIsValid(tuple))
{
if (!missing_ok)
elog(ERROR, "could not find tuple for property %u", object->objectId);
break;
}
pgpform = (Form_pg_propgraph_property) GETSTRUCT(tuple);
/* translator: followed by, e.g., "property graph %s" */
appendStringInfo(&buffer, _("property %s of "), NameStr(pgpform->pgpname));
getRelationDescription(&buffer, pgpform->pgppgid, false);
ReleaseSysCache(tuple);
break;
}
case PublicationRelationId:
{
char *pubname = get_publication_name(object->objectId,
@ -4161,6 +4408,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"),
@ -4650,6 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
appendStringInfoString(&buffer, "policy");
break;
case PropgraphElementRelationId:
appendStringInfoString(&buffer, "property graph element");
break;
case PropgraphLabelRelationId:
appendStringInfoString(&buffer, "property graph label");
break;
case PropgraphPropertyRelationId:
appendStringInfoString(&buffer, "property graph property");
break;
case PublicationRelationId:
appendStringInfoString(&buffer, "publication");
break;
@ -4731,6 +4994,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");
@ -5895,6 +6161,73 @@ getObjectIdentityParts(const ObjectAddress *object,
break;
}
case PropgraphElementRelationId:
{
HeapTuple tup;
Form_pg_propgraph_element pge;
tup = SearchSysCache1(PROPGRAPHELOID, object->objectId);
if (!HeapTupleIsValid(tup))
{
if (!missing_ok)
elog(ERROR, "cache lookup failed for property graph element %u", object->objectId);
break;
}
pge = (Form_pg_propgraph_element) GETSTRUCT(tup);
appendStringInfo(&buffer, "%s of ", quote_identifier(NameStr(pge->pgealias)));
getRelationIdentity(&buffer, pge->pgepgid, objname, false);
if (objname)
*objname = lappend(*objname, pstrdup(NameStr(pge->pgealias)));
ReleaseSysCache(tup);
break;
}
case PropgraphLabelRelationId:
{
HeapTuple tup;
Form_pg_propgraph_label pgl;
tup = SearchSysCache1(PROPGRAPHLABELOID, object->objectId);
if (!HeapTupleIsValid(tup))
{
if (!missing_ok)
elog(ERROR, "cache lookup failed for property graph label %u", object->objectId);
break;
}
pgl = (Form_pg_propgraph_label) GETSTRUCT(tup);
appendStringInfo(&buffer, "%s of ", quote_identifier(NameStr(pgl->pgllabel)));
getRelationIdentity(&buffer, pgl->pglpgid, objname, false);
if (objname)
*objname = lappend(*objname, pstrdup(NameStr(pgl->pgllabel)));
ReleaseSysCache(tup);
break;
}
case PropgraphPropertyRelationId:
{
HeapTuple tup;
Form_pg_propgraph_property pgp;
tup = SearchSysCache1(PROPGRAPHPROPOID, object->objectId);
if (!HeapTupleIsValid(tup))
{
if (!missing_ok)
elog(ERROR, "cache lookup failed for property graph property %u", object->objectId);
break;
}
pgp = (Form_pg_propgraph_property) GETSTRUCT(tup);
appendStringInfo(&buffer, "%s of ", quote_identifier(NameStr(pgp->pgpname)));
getRelationIdentity(&buffer, pgp->pgppgid, objname, false);
if (objname)
*objname = lappend(*objname, pstrdup(NameStr(pgp->pgpname)));
ReleaseSysCache(tup);
break;
}
case PublicationRelationId:
{
char *pubname;
@ -6201,6 +6534,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:

View file

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

View file

@ -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
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 YES 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 NO
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 YES
G941 Implicit removal of incomplete edges YES
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

View file

@ -49,6 +49,7 @@ OBJS = \
portalcmds.o \
prepare.o \
proclang.o \
propgraphcmds.o \
publicationcmds.o \
schemacmds.o \
seclabel.o \

View file

@ -390,6 +390,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_MATVIEW:
case OBJECT_INDEX:
case OBJECT_FOREIGN_TABLE:
case OBJECT_PROPGRAPH:
return RenameRelation(stmt);
case OBJECT_COLUMN:
@ -543,6 +544,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
case OBJECT_TABLE:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_PROPGRAPH:
address = AlterTableNamespace(stmt,
oldSchemaAddr ? &oldNspOid : NULL);
break;
@ -876,6 +878,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:
@ -884,11 +887,26 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
{
ObjectAddress address;
address = get_object_address(stmt->objectType,
stmt->object,
NULL,
AccessExclusiveLock,
false);
if (stmt->relation)
{
Relation relation;
address = get_object_address_rv(stmt->objectType,
stmt->relation,
NIL,
&relation,
AccessExclusiveLock,
false);
relation_close(relation, NoLock);
}
else
{
address = get_object_address(stmt->objectType,
stmt->object,
NULL,
AccessExclusiveLock,
false);
}
AlterObjectOwner_internal(address.classId, address.objectId,
newowner);

View file

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

View file

@ -2305,6 +2305,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:
@ -2389,6 +2390,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:

View file

@ -37,6 +37,7 @@ backend_sources += files(
'portalcmds.c',
'prepare.c',
'proclang.c',
'propgraphcmds.c',
'publicationcmds.c',
'schemacmds.c',
'seclabel.c',

File diff suppressed because it is too large Load diff

View file

@ -80,6 +80,7 @@ SecLabelSupportsObjectType(ObjectType objtype)
case OBJECT_OPFAMILY:
case OBJECT_PARAMETER_ACL:
case OBJECT_POLICY:
case OBJECT_PROPGRAPH:
case OBJECT_PUBLICATION_NAMESPACE:
case OBJECT_PUBLICATION_REL:
case OBJECT_RULE:

View file

@ -308,6 +308,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}
};
@ -1550,7 +1556,7 @@ DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
/*
* RemoveRelations
* Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
* DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
* DROP MATERIALIZED VIEW, DROP FOREIGN TABLE, DROP PROPERTY GRAPH
*/
void
RemoveRelations(DropStmt *drop)
@ -1614,6 +1620,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);
@ -4219,7 +4229,7 @@ RenameConstraint(RenameStmt *stmt)
}
/*
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
* Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE/PROPERTY GRAPH
* RENAME
*/
ObjectAddress
@ -16329,6 +16339,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:
@ -19897,6 +19908,11 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a composite type", rv->relname)));
if (reltype == OBJECT_PROPGRAPH && relkind != RELKIND_PROPGRAPH)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a property graph", rv->relname)));
if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
relkind != RELKIND_PARTITIONED_INDEX
&& !IsA(stmt, RenameStmt))

View file

@ -599,11 +599,11 @@ ExecCheckPermissions(List *rangeTable, List *rteperminfos,
/*
* Only relation RTEs and subquery RTEs that were once relation
* RTEs (views) have their perminfoindex set.
* RTEs (views, property graphs) have their perminfoindex set.
*/
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 */
@ -1163,6 +1163,12 @@ CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation,
break;
}
break;
case RELKIND_PROPGRAPH:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change property graph \"%s\"",
RelationGetRelationName(resultRel))));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@ -1227,6 +1233,13 @@ CheckValidRowMarkRel(Relation rel, RowMarkType markType)
errmsg("cannot lock rows in foreign table \"%s\"",
RelationGetRelationName(rel))));
break;
case RELKIND_PROPGRAPH:
/* Should not get here; rewriter should have expanded the graph */
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg_internal("cannot lock rows in property graph \"%s\"",
RelationGetRelationName(rel))));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),

View file

@ -284,6 +284,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 */
@ -536,6 +539,8 @@ exprTypmod(const Node *expr)
return exprTypmod((Node *) ((const ReturningExpr *) expr)->retexpr);
case T_PlaceHolderVar:
return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
case T_GraphPropertyRef:
return ((const GraphPropertyRef *) expr)->typmod;
default:
break;
}
@ -1058,6 +1063,9 @@ exprCollation(const Node *expr)
case T_PlaceHolderVar:
coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
break;
case T_GraphPropertyRef:
coll = ((const GraphPropertyRef *) expr)->collation;
break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
coll = InvalidOid; /* keep compiler quiet */
@ -2127,6 +2135,7 @@ expression_tree_walker_impl(Node *node,
case T_RangeTblRef:
case T_SortGroupClause:
case T_CTESearchClause:
case T_GraphPropertyRef:
case T_MergeSupportFunc:
/* primitive node types with no expression subnodes */
break;
@ -2667,6 +2676,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));
@ -2860,6 +2889,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:
@ -3912,6 +3947,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:
@ -4546,6 +4585,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;
@ -4704,6 +4755,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));

View file

@ -565,6 +565,15 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
/* we re-use these RELATION fields, too: */
WRITE_OID_FIELD(relid);
break;
case RTE_GRAPH_TABLE:
WRITE_NODE_FIELD(graph_pattern);
WRITE_NODE_FIELD(graph_table_columns);
/* we re-use these RELATION fields, too: */
WRITE_OID_FIELD(relid);
WRITE_CHAR_FIELD(relkind);
WRITE_INT_FIELD(rellockmode);
WRITE_UINT_FIELD(perminfoindex);
break;
case RTE_RESULT:
/* no extra fields */
break;

View file

@ -304,6 +304,10 @@ print_rt(const List *rtable)
printf("%d\t%s\t[group]",
i, rte->eref->aliasname);
break;
case RTE_GRAPH_TABLE:
printf("%d\t%s\t[graph table]",
i, rte->eref->aliasname);
break;
default:
printf("%d\t%s\t[unknown rtekind]",
i, rte->eref->aliasname);

View file

@ -423,6 +423,15 @@ _readRangeTblEntry(void)
/* we re-use these RELATION fields, too: */
READ_OID_FIELD(relid);
break;
case RTE_GRAPH_TABLE:
READ_NODE_FIELD(graph_pattern);
READ_NODE_FIELD(graph_table_columns);
/* we re-use these RELATION fields, too: */
READ_OID_FIELD(relid);
READ_CHAR_FIELD(relkind);
READ_INT_FIELD(rellockmode);
READ_UINT_FIELD(perminfoindex);
break;
case RTE_RESULT:
/* no extra fields */
break;

View file

@ -787,6 +787,16 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
case RTE_RESULT:
/* RESULT RTEs, in themselves, are no problem. */
break;
case RTE_GRAPH_TABLE:
/*
* Shouldn't happen since these are replaced by subquery RTEs when
* rewriting queries.
*/
Assert(false);
return;
case RTE_GROUP:
/* Shouldn't happen; we're only considering baserels here. */
Assert(false);

View file

@ -1631,6 +1631,10 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
case RTE_GROUP:
/* these can't contain any lateral references */
break;
case RTE_GRAPH_TABLE:
/* shouldn't happen here */
Assert(false);
break;
}
}
}
@ -2696,6 +2700,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;
}
}
}

View file

@ -23,6 +23,7 @@ OBJS = \
parse_enr.o \
parse_expr.o \
parse_func.o \
parse_graphtable.o \
parse_jsontable.o \
parse_merge.o \
parse_node.o \

View file

@ -79,9 +79,6 @@ static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
bool isTopLevel, List **targetlist);
static void constructSetOpTargetlist(ParseState *pstate, SetOperationStmt *op,
const List *ltargetlist, const List *rtargetlist,
List **targetlist, const char *context, bool recursive);
static void determineRecursiveColTypes(ParseState *pstate,
Node *larg, List *nrtargetlist);
static Query *transformReturnStmt(ParseState *pstate, ReturnStmt *stmt);
@ -2271,7 +2268,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
* given SetOperationStmt node. context is a string for error messages
* ("UNION" etc.). recursive is true if it is a recursive union.
*/
static void
void
constructSetOpTargetlist(ParseState *pstate, SetOperationStmt *op,
const List *ltargetlist, const List *rtargetlist,
List **targetlist, const char *context, bool recursive)

View file

@ -295,6 +295,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
@ -685,6 +686,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.
* They must be listed first so that their numeric codes do not depend on
@ -727,18 +758,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 EMPTY_P ENABLE_P ENCODING ENCRYPTED END_P ENFORCED ENUM_P ERROR_P
ESCAPE EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXPRESSION EXTENSION EXTERNAL EXTRACT
EACH EDGE ELSE EMPTY_P ENABLE_P ENCODING ENCRYPTED END_P ENFORCED ENUM_P
ERROR_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
@ -759,7 +790,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE MERGE_ACTION METHOD
MINUTE_P MINVALUE MODE MONTH_P MOVE
NAME_P NAMES NATIONAL NATURAL NCHAR NESTED NEW NEXT NFC NFD NFKC NFKD NO
NAME_P NAMES NATIONAL NATURAL NCHAR NESTED NEW NEXT NFC NFD NFKC NFKD NO NODE
NONE NORMALIZE NORMALIZED
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
NULLS_P NUMERIC
@ -771,12 +802,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
PARALLEL PARAMETER PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH
PERIOD PLACING PLAN 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 QUOTES
RANGE READ REAL REASSIGN RECURSIVE REF_P REFERENCES REFERENCING
REFRESH REINDEX RELATIVE_P RELEASE RENAME REPACK REPEATABLE REPLACE REPLICA
REFRESH REINDEX RELATIONSHIP RELATIVE_P RELEASE RENAME REPACK REPEATABLE REPLACE REPLICA
RESET RESPECT_P RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
ROUTINE ROUTINES ROW ROWS RULE
@ -796,7 +827,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 VIRTUAL VOLATILE
VERBOSE VERSION_P VERTEX VIEW VIEWS VIRTUAL VOLATILE
WAIT WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@ -894,7 +925,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%nonassoc UNBOUNDED NESTED /* 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 PATH
%left Op OPERATOR /* multi-character ops and user-defined operators */
%left Op OPERATOR RIGHT_ARROW '|' /* multi-character ops and user-defined operators */
%left '+' '-'
%left '*' '/' '%'
%left '^'
@ -1021,6 +1052,7 @@ stmt:
| AlterOperatorStmt
| AlterTypeStmt
| AlterPolicyStmt
| AlterPropGraphStmt
| AlterSeqStmt
| AlterSystemStmt
| AlterTableStmt
@ -1060,6 +1092,7 @@ stmt:
| AlterOpFamilyStmt
| CreatePolicyStmt
| CreatePLangStmt
| CreatePropGraphStmt
| CreateSchemaStmt
| CreateSeqStmt
| CreateStmt
@ -7202,6 +7235,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; }
@ -8090,6 +8124,15 @@ privilege_target:
n->objs = $2;
$$ = n;
}
| PROPERTY GRAPH qualified_name_list
{
PrivTarget *n = palloc_object(PrivTarget);
n->targtype = ACL_TARGET_OBJECT;
n->objtype = OBJECT_PROPGRAPH;
n->objs = $3;
$$ = n;
}
| SCHEMA name_list
{
PrivTarget *n = palloc_object(PrivTarget);
@ -9419,6 +9462,370 @@ 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
/*
* SQL standard also allows "PROPERTIES ARE ALL COLUMNS", but that
* would require making ARE a keyword, which seems a bit much for
* such a marginal use. Could be added later if needed.
*/
{
PropGraphProperties *pr = makeNode(PropGraphProperties);
pr->all = true;
pr->location = @1;
$$ = (Node *) pr;
}
| PROPERTIES '(' labeled_expr_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 '(' labeled_expr_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
@ -9715,6 +10122,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);
@ -10340,6 +10757,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);
@ -10683,6 +11120,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);
@ -13958,6 +14404,17 @@ table_ref: relation_expr opt_alias_clause
n->alias = $3;
$$ = (Node *) n;
}
| GRAPH_TABLE '(' qualified_name MATCH graph_pattern COLUMNS '(' labeled_expr_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);
@ -15309,6 +15766,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 RIGHT_ARROW a_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "->", $1, $3, @2); }
| a_expr '|' 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); }
@ -15789,6 +16250,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 RIGHT_ARROW b_expr
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "->", $1, $3, @2); }
| b_expr '|' 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
@ -16984,6 +17449,8 @@ MathOp: '+' { $$ = "+"; }
| LESS_EQUALS { $$ = "<="; }
| GREATER_EQUALS { $$ = ">="; }
| NOT_EQUALS { $$ = "<>"; }
| RIGHT_ARROW { $$ = "->"; }
| '|' { $$ = "|"; }
;
qual_Op: Op
@ -17564,6 +18031,214 @@ 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;
$$ = (Node *) gep;
}
;
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 ]- */
| '<' '-' '[' opt_colid opt_is_label_expression where_clause ']' '-'
{
GraphElementPattern *gep = makeNode(GraphElementPattern);
gep->kind = EDGE_PATTERN_LEFT;
gep->variable = $4;
gep->labelexpr = $5;
gep->whereClause = $6;
gep->location = @1;
$$ = (Node *) gep;
}
/* full edge pointing right: -[ xxx ]-> */
| '-' '[' opt_colid opt_is_label_expression where_clause ']' '-' '>'
{
GraphElementPattern *gep = makeNode(GraphElementPattern);
gep->kind = EDGE_PATTERN_RIGHT;
gep->variable = $3;
gep->labelexpr = $4;
gep->whereClause = $5;
gep->location = @1;
$$ = (Node *) gep;
}
| '-' '[' opt_colid opt_is_label_expression where_clause ']' RIGHT_ARROW
{
GraphElementPattern *gep = makeNode(GraphElementPattern);
gep->kind = EDGE_PATTERN_RIGHT;
gep->variable = $3;
gep->labelexpr = $4;
gep->whereClause = $5;
gep->location = @1;
$$ = (Node *) gep;
}
/* full edge any direction: -[ xxx ]- */
| '-' '[' opt_colid opt_is_label_expression where_clause ']' '-'
{
GraphElementPattern *gep = makeNode(GraphElementPattern);
gep->kind = EDGE_PATTERN_ANY;
gep->variable = $3;
gep->labelexpr = $4;
gep->whereClause = $5;
gep->location = @1;
$$ = (Node *) gep;
}
/* abbreviated edge patterns */
| '<' '-'
{
GraphElementPattern *gep = makeNode(GraphElementPattern);
gep->kind = EDGE_PATTERN_LEFT;
gep->location = @1;
$$ = (Node *) gep;
}
| '-' '>'
{
GraphElementPattern *gep = makeNode(GraphElementPattern);
gep->kind = EDGE_PATTERN_RIGHT;
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; }
| /*EMPTY*/ { $$ = NULL; }
;
/*
* graph pattern quantifier
*/
opt_graph_pattern_quantifier:
'{' Iconst '}' { $$ = list_make2_int($2, $2); }
| '{' ',' Iconst '}' { $$ = list_make2_int(0, $3); }
| '{' Iconst ',' Iconst '}' { $$ = 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
@ -18085,6 +18760,7 @@ unreserved_keyword:
| DELIMITERS
| DEPENDS
| DEPTH
| DESTINATION
| DETACH
| DICTIONARY
| DISABLE_P
@ -18094,6 +18770,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
| EDGE
| EMPTY_P
| ENABLE_P
| ENCODING
@ -18124,6 +18801,7 @@ unreserved_keyword:
| GENERATED
| GLOBAL
| GRANTED
| GRAPH
| GROUPS
| HANDLER
| HEADER_P
@ -18190,6 +18868,7 @@ unreserved_keyword:
| NFKC
| NFKD
| NO
| NODE
| NORMALIZED
| NOTHING
| NOTIFY
@ -18234,6 +18913,8 @@ unreserved_keyword:
| PROCEDURE
| PROCEDURES
| PROGRAM
| PROPERTIES
| PROPERTY
| PUBLICATION
| QUOTE
| QUOTES
@ -18245,6 +18926,7 @@ unreserved_keyword:
| REFERENCING
| REFRESH
| REINDEX
| RELATIONSHIP
| RELATIVE_P
| RELEASE
| RENAME
@ -18337,6 +19019,7 @@ unreserved_keyword:
| VALUE_P
| VARYING
| VERSION_P
| VERTEX
| VIEW
| VIEWS
| VIRTUAL
@ -18377,6 +19060,7 @@ col_name_keyword:
| EXISTS
| EXTRACT
| FLOAT_P
| GRAPH_TABLE
| GREATEST
| GROUPING
| INOUT
@ -18668,6 +19352,7 @@ bare_label_keyword:
| DEPENDS
| DEPTH
| DESC
| DESTINATION
| DETACH
| DICTIONARY
| DISABLE_P
@ -18679,6 +19364,7 @@ bare_label_keyword:
| DOUBLE_P
| DROP
| EACH
| EDGE
| ELSE
| EMPTY_P
| ENABLE_P
@ -18717,6 +19403,8 @@ bare_label_keyword:
| GENERATED
| GLOBAL
| GRANTED
| GRAPH
| GRAPH_TABLE
| GREATEST
| GROUPING
| GROUPS
@ -18813,6 +19501,7 @@ bare_label_keyword:
| NFKC
| NFKD
| NO
| NODE
| NONE
| NORMALIZE
| NORMALIZED
@ -18870,6 +19559,8 @@ bare_label_keyword:
| PROCEDURE
| PROCEDURES
| PROGRAM
| PROPERTIES
| PROPERTY
| PUBLICATION
| QUOTE
| QUOTES
@ -18883,6 +19574,7 @@ bare_label_keyword:
| REFERENCING
| REFRESH
| REINDEX
| RELATIONSHIP
| RELATIVE_P
| RELEASE
| RENAME
@ -18999,6 +19691,7 @@ bare_label_keyword:
| VARIADIC
| VERBOSE
| VERSION_P
| VERTEX
| VIEW
| VIEWS
| VIRTUAL

View file

@ -10,6 +10,7 @@ backend_sources += files(
'parse_enr.c',
'parse_expr.c',
'parse_func.c',
'parse_graphtable.c',
'parse_jsontable.c',
'parse_merge.c',
'parse_node.c',

View file

@ -585,6 +585,14 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
errkind = true;
break;
case EXPR_KIND_PROPGRAPH_PROPERTY:
if (isAgg)
err = _("aggregate functions are not allowed in property definition expressions");
else
err = _("grouping operations are not allowed in property definition expressions");
break;
/*
* There is intentionally no default: case here, so that the
* compiler will warn if we add a new ParseExprKind without
@ -1024,6 +1032,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_CYCLE_MARK:
errkind = true;
break;
case EXPR_KIND_PROPGRAPH_PROPERTY:
err = _("window functions are not allowed in property definition expressions");
break;
/*
* There is intentionally no default: case here, so that the

View file

@ -17,6 +17,7 @@
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/relation.h"
#include "access/table.h"
#include "access/tsmapi.h"
#include "catalog/catalog.h"
@ -35,6 +36,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"
@ -65,6 +67,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,
@ -898,6 +902,126 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
tf, rtf->alias, is_lateral, true);
}
/*
* Similar to parserOpenTable() but for property graphs.
*/
static Relation
parserOpenPropGraph(ParseState *pstate, const RangeVar *relation, LOCKMODE lockmode)
{
Relation rel;
ParseCallbackState pcbstate;
setup_parser_errposition_callback(&pcbstate, pstate, relation->location);
rel = relation_openrv(relation, lockmode);
/*
* In parserOpenTable(), the relkind check is done inside table_openrv*.
* We do it here since we don't have anything like propgraph_open.
*/
if (rel->rd_rel->relkind != RELKIND_PROPGRAPH)
ereport(ERROR,
errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a property graph",
RelationGetRelationName(rel)));
cancel_parser_errposition_callback(&pcbstate);
return rel;
}
/*
* transformRangeGraphTable -- transform a GRAPH_TABLE clause
*/
static ParseNamespaceItem *
transformRangeGraphTable(ParseState *pstate, RangeGraphTable *rgt)
{
Relation rel;
Oid graphid;
GraphTableParseState *gpstate = palloc0_object(GraphTableParseState);
Node *gp;
List *columns = NIL;
List *colnames = NIL;
ListCell *lc;
int resno = 0;
bool saved_hasSublinks;
rel = parserOpenPropGraph(pstate, rgt->graph_name, AccessShareLock);
graphid = RelationGetRelid(rel);
gpstate->graphid = graphid;
/*
* The syntax does not allow nested GRAPH_TABLE and this function
* prohibits subquery within GRAPH_TABLE. There should be only one
* GRAPH_TABLE being transformed at a time.
*/
Assert(!pstate->p_graph_table_pstate);
pstate->p_graph_table_pstate = gpstate;
Assert(!pstate->p_lateral_active);
pstate->p_lateral_active = true;
saved_hasSublinks = pstate->p_hasSubLinks;
pstate->p_hasSubLinks = false;
gp = transformGraphPattern(pstate, rgt->graph_pattern);
/*
* Construct a targetlist representing the COLUMNS specified in the
* GRAPH_TABLE. This uses previously constructed list of element pattern
* variables in the GraphTableParseState.
*/
foreach(lc, rgt->columns)
{
ResTarget *rt = lfirst_node(ResTarget, lc);
Node *colexpr;
TargetEntry *te;
char *colname;
colexpr = transformExpr(pstate, rt->val, EXPR_KIND_SELECT_TARGET);
if (rt->name)
colname = rt->name;
else
{
if (IsA(colexpr, GraphPropertyRef))
colname = get_propgraph_property_name(castNode(GraphPropertyRef, colexpr)->propid);
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);
pstate->p_graph_table_pstate = NULL;
pstate->p_lateral_active = false;
/*
* If we support subqueries within GRAPH_TABLE, those need to be
* propagated to the queries resulting from rewriting graph table RTE. We
* don't do that right now, hence prohibit it for now.
*/
if (pstate->p_hasSubLinks)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("subqueries within GRAPH_TABLE reference are not supported")));
pstate->p_hasSubLinks = saved_hasSublinks;
return addRangeTableEntryForGraphTable(pstate, graphid, castNode(GraphPattern, gp), columns, colnames, rgt->alias, false, true);
}
/*
* transformRangeTableSample --- transform a TABLESAMPLE clause
*
@ -1121,6 +1245,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) */

View file

@ -546,6 +546,7 @@ assign_collations_walker(Node *node, assign_collations_context *context)
case T_CaseTestExpr:
case T_SetToDefault:
case T_CurrentOfExpr:
case T_GraphPropertyRef:
/*
* General case for childless expression nodes. These should

View file

@ -29,6 +29,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"
@ -577,6 +578,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
case EXPR_KIND_COPY_WHERE:
case EXPR_KIND_GENERATED_COLUMN:
case EXPR_KIND_CYCLE_MARK:
case EXPR_KIND_PROPGRAPH_PROPERTY:
/* okay */
break;
@ -824,6 +826,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
break;
}
/* Try it as a graph table property reference. */
if (node == NULL)
node = transformGraphTablePropertyRef(pstate, cref);
/*
* Now give the PostParseColumnRefHook, if any, a chance. We pass the
* translation-so-far so that it can throw an error if it wishes in the
@ -1871,6 +1877,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_GENERATED_COLUMN:
err = _("cannot use subquery in column generation expression");
break;
case EXPR_KIND_PROPGRAPH_PROPERTY:
err = _("cannot use subquery in property definition expression");
break;
/*
* There is intentionally no default: case here, so that the
@ -3230,6 +3239,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "GENERATED AS";
case EXPR_KIND_CYCLE_MARK:
return "CYCLE";
case EXPR_KIND_PROPGRAPH_PROPERTY:
return "property definition expression";
/*
* There is intentionally no default: case here, so that the

View file

@ -2783,6 +2783,9 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
case EXPR_KIND_CYCLE_MARK:
errkind = true;
break;
case EXPR_KIND_PROPGRAPH_PROPERTY:
err = _("set-returning functions are not allowed in property definition expressions");
break;
/*
* There is intentionally no default: case here, so that the

View file

@ -0,0 +1,309 @@
/*-------------------------------------------------------------------------
*
* parse_graphtable.c
* parsing of GRAPH_TABLE
*
* Portions Copyright (c) 1996-2026, 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_label.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/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
/*
* Return human-readable name of the type of graph element pattern in
* GRAPH_TABLE clause, usually for error message purpose.
*/
static const char *
get_gep_kind_name(GraphElementPatternKind gepkind)
{
switch (gepkind)
{
case VERTEX_PATTERN:
return "vertex";
case EDGE_PATTERN_LEFT:
return "edge pointing left";
case EDGE_PATTERN_RIGHT:
return "edge pointing right";
case EDGE_PATTERN_ANY:
return "edge pointing any direction";
case PAREN_EXPR:
return "nested path pattern";
}
/*
* When a GraphElementPattern is constructed by the parser, it will set a
* value from the GraphElementPatternKind enum. But we may get here if the
* GraphElementPatternKind value stored in a catalog is corrupted.
*/
return "unknown";
}
/*
* Transform a property reference.
*
* A property reference is parsed as a ColumnRef of the form:
* <variable>.<property>. If <variable> is one of the variables bound to an
* element pattern in the graph pattern and <property> can be resolved as a
* property of the property graph, then we return a GraphPropertyRef node
* representing the property reference. If the <variable> exists in the graph
* pattern but <property> does not exist in the property graph, we raise an
* error. However, if <variable> does not exist in the graph pattern, we return
* NULL to let the caller handle it as some other kind of ColumnRef. The
* variables bound to the element patterns in the graph pattern are expected to
* be collected in the GraphTableParseState.
*/
Node *
transformGraphTablePropertyRef(ParseState *pstate, ColumnRef *cref)
{
GraphTableParseState *gpstate = pstate->p_graph_table_pstate;
if (!gpstate)
return NULL;
if (list_length(cref->fields) == 2)
{
Node *field1 = linitial(cref->fields);
Node *field2 = lsecond(cref->fields);
char *elvarname;
char *propname;
if (IsA(field1, A_Star) || IsA(field2, A_Star))
{
if (pstate->p_expr_kind == EXPR_KIND_SELECT_TARGET)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("\"*\" is not supported here"),
parser_errposition(pstate, cref->location));
else
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"*\" not allowed here"),
parser_errposition(pstate, cref->location));
}
elvarname = strVal(field1);
propname = strVal(field2);
if (list_member(gpstate->variables, field1))
{
GraphPropertyRef *gpr = makeNode(GraphPropertyRef);
HeapTuple pgptup;
Form_pg_propgraph_property pgpform;
pgptup = SearchSysCache2(PROPGRAPHPROPNAME, ObjectIdGetDatum(gpstate->graphid), CStringGetDatum(propname));
if (!HeapTupleIsValid(pgptup))
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("property \"%s\" does not exist", propname));
pgpform = (Form_pg_propgraph_property) GETSTRUCT(pgptup);
gpr->location = cref->location;
gpr->elvarname = elvarname;
gpr->propid = pgpform->oid;
gpr->typeId = pgpform->pgptypid;
gpr->typmod = pgpform->pgptypmod;
gpr->collation = pgpform->pgpcollation;
ReleaseSysCache(pgptup);
return (Node *) gpr;
}
}
return NULL;
}
/*
* Transform a label expression.
*
* A label expression is parsed as either a ColumnRef with a single field or a
* label expression like label disjunction. The single field in the ColumnRef is
* treated as a label name and transformed to a GraphLabelRef node. The label
* expression is recursively transformed into an expression tree containg
* GraphLabelRef nodes corresponding to the names of the labels appearing in the
* expression. If any label name cannot be resolved to a label in the property
* graph, an error is raised.
*/
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;
Oid labelid;
GraphLabelRef *lref;
Assert(list_length(cref->fields) == 1);
labelname = strVal(linitial(cref->fields));
labelid = GetSysCacheOid2(PROPGRAPHLABELNAME, Anum_pg_propgraph_label_oid, ObjectIdGetDatum(gpstate->graphid), CStringGetDatum(labelname));
if (!labelid)
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("label \"%s\" does not exist in property graph \"%s\"", labelname, get_rel_name(gpstate->graphid)));
lref = makeNode(GraphLabelRef);
lref->labelid = labelid;
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, "unsupported label expression node: %d", (int) nodeTag(labelexpr));
result = NULL; /* keep compiler quiet */
break;
}
return result;
}
/*
* Transform a GraphElementPattern.
*
* Transform the label expression and the where clause in the element pattern
* given by GraphElementPattern. The variable name in the GraphElementPattern is
* added to the list of variables in the GraphTableParseState which is used to
* resolve property references in this element pattern or elsewhere in the
* GRAPH_TABLE.
*/
static Node *
transformGraphElementPattern(ParseState *pstate, GraphElementPattern *gep)
{
GraphTableParseState *gpstate = pstate->p_graph_table_pstate;
if (gep->kind != VERTEX_PATTERN && !IS_EDGE_PATTERN(gep->kind))
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported element pattern kind: \"%s\"", get_gep_kind_name(gep->kind)));
if (gep->quantifier)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("element pattern quantifier is not supported")));
if (gep->variable)
gpstate->variables = lappend(gpstate->variables, makeString(pstrdup(gep->variable)));
gep->labelexpr = transformLabelExpr(gpstate, gep->labelexpr);
gep->whereClause = transformExpr(pstate, gep->whereClause, EXPR_KIND_WHERE);
assign_expr_collations(pstate, gep->whereClause);
return (Node *) gep;
}
/*
* Transform a path term (list of GraphElementPattern's).
*/
static Node *
transformPathTerm(ParseState *pstate, List *path_term)
{
List *result = NIL;
foreach_node(GraphElementPattern, gep, path_term)
result = lappend(result,
transformGraphElementPattern(pstate, gep));
return (Node *) result;
}
/*
* Transform a path pattern list (list of path terms).
*/
static Node *
transformPathPatternList(ParseState *pstate, List *path_pattern)
{
List *result = NIL;
/* Grammar doesn't allow empty path pattern list */
Assert(list_length(path_pattern) > 0);
/*
* We do not support multiple path patterns in one GRAPH_TABLE clause
* right now. But we may do so in future.
*/
if (list_length(path_pattern) != 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("multiple path patterns in one GRAPH_TABLE clause not supported")));
foreach_node(List, path_term, path_pattern)
result = lappend(result, transformPathTerm(pstate, path_term));
return (Node *) result;
}
/*
* Transform a GraphPattern.
*
* A GraphPattern consists of a list of one or more path patterns and an
* optional where clause. Transform them. We use the previously constructure
* list of variables in the GraphTableParseState to resolve property references
* in the WHERE clause.
*/
Node *
transformGraphPattern(ParseState *pstate, GraphPattern *graph_pattern)
{
List *path_pattern_list = castNode(List,
transformPathPatternList(pstate, graph_pattern->path_pattern_list));
graph_pattern->path_pattern_list = path_pattern_list;
graph_pattern->whereClause = transformExpr(pstate, graph_pattern->whereClause, EXPR_KIND_WHERE);
assign_expr_collations(pstate, graph_pattern->whereClause);
return (Node *) graph_pattern;
}

View file

@ -2131,6 +2131,99 @@ 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;
rte->rellockmode = AccessShareLock;
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.
@ -3029,6 +3122,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);
@ -3413,10 +3507,11 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
case RTE_VALUES:
case RTE_CTE:
case RTE_GROUP:
case RTE_GRAPH_TABLE:
/*
* Subselect, Table Functions, Values, CTE, GROUP RTEs never have
* dropped columns
* Subselect, Table Functions, Values, CTE, GROUP RTEs, Property
* graph references never have dropped columns
*/
result = false;
break;

View file

@ -359,6 +359,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)
@ -1584,6 +1588,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
case RTE_RELATION:
case RTE_VALUES:
case RTE_NAMEDTUPLESTORE:
case RTE_GRAPH_TABLE:
case RTE_RESULT:
/*

View file

@ -348,6 +348,8 @@ less_equals "<="
greater_equals ">="
less_greater "<>"
not_equals "!="
/* Note there is no need for left_arrow, since "<-" is not a single operator. */
right_arrow "->"
/*
* "self" is the set of chars that should be returned as single-character
@ -359,7 +361,7 @@ not_equals "!="
* If you change either set, adjust the character lists appearing in the
* rule for "operator"!
*/
self [,()\[\].;\:\+\-\*\/\%\^\<\>\=]
self [,()\[\].;\:\|\+\-\*\/\%\^\<\>\=]
op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
operator {op_chars}+
@ -853,6 +855,11 @@ other .
return NOT_EQUALS;
}
{right_arrow} {
SET_YYLLOC();
return RIGHT_ARROW;
}
{self} {
SET_YYLLOC();
return yytext[0];
@ -930,7 +937,7 @@ other .
* that the "self" rule would have.
*/
if (nchars == 1 &&
strchr(",()[].;:+-*/%^<>=", yytext[0]))
strchr(",()[].;:|+-*/%^<>=", yytext[0]))
return yytext[0];
/*
* Likewise, if what we have left is two chars, and
@ -950,6 +957,8 @@ other .
return NOT_EQUALS;
if (yytext[0] == '!' && yytext[1] == '=')
return NOT_EQUALS;
if (yytext[0] == '-' && yytext[1] == '>')
return RIGHT_ARROW;
}
}

View file

@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = \
rewriteDefine.o \
rewriteGraphTable.o \
rewriteHandler.o \
rewriteManip.o \
rewriteRemove.o \

View file

@ -2,6 +2,7 @@
backend_sources += files(
'rewriteDefine.c',
'rewriteGraphTable.c',
'rewriteHandler.c',
'rewriteManip.c',
'rewriteRemove.c',

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,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"
@ -173,6 +174,7 @@ AcquireRewriteLocks(Query *parsetree,
switch (rte->rtekind)
{
case RTE_RELATION:
case RTE_GRAPH_TABLE:
/*
* Grab the appropriate lock type for the relation, and do not
@ -2045,6 +2047,16 @@ 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);
}
/*
* 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

View file

@ -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"
@ -149,6 +150,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:
@ -179,6 +181,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:
@ -1739,6 +1742,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;
@ -2008,6 +2019,7 @@ ExecDropStmt(DropStmt *stmt, bool isTopLevel)
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_FOREIGN_TABLE:
case OBJECT_PROPGRAPH:
RemoveRelations(stmt);
break;
default:
@ -2290,6 +2302,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;
@ -2566,6 +2581,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;
@ -2950,6 +2968,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;
@ -3651,6 +3677,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;

View file

@ -890,6 +890,10 @@ acldefault(ObjectType objtype, Oid ownerId)
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_PARAMETER_ACL;
break;
case OBJECT_PROPGRAPH:
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_PROPGRAPH;
break;
default:
elog(ERROR, "unrecognized object type: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */

View file

@ -34,6 +34,11 @@
#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_element_label.h"
#include "catalog/pg_propgraph_label.h"
#include "catalog/pg_propgraph_label_property.h"
#include "catalog/pg_propgraph_property.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@ -361,6 +366,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 ellabelid, 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,
@ -1601,6 +1609,325 @@ 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),
quote_identifier(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, false, 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, false, buf);
appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
decompile_column_index_array(srcref, pgeform2->pgerelid, false, buf);
appendStringInfoString(buf, ")");
}
else
appendStringInfo(buf, " %s ", quote_identifier(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, false, buf);
appendStringInfo(buf, ") REFERENCES %s (", quote_identifier(NameStr(pgeform2->pgealias)));
decompile_column_index_array(destref, pgeform2->pgerelid, false, buf);
appendStringInfoString(buf, ")");
}
else
appendStringInfo(buf, " %s", quote_identifier(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 appended 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(PropgraphElementLabelRelationId, AccessShareLock);
ScanKeyInit(&scankey[0],
Anum_pg_propgraph_element_label_pgelelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(elid));
count = 0;
scan = systable_beginscan(pglrel, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, scankey);
while ((tup = systable_getnext(scan)))
{
count++;
}
systable_endscan(scan);
scan = systable_beginscan(pglrel, PropgraphElementLabelElementLabelIndexId, true, NULL, 1, scankey);
while ((tup = systable_getnext(scan)))
{
Form_pg_propgraph_element_label pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
const char *labelname;
labelname = get_propgraph_label_name(pgelform->pgellabelid);
if (strcmp(labelname, 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(labelname));
make_propgraphdef_properties(buf, pgelform->oid, elrelid);
}
systable_endscan(scan);
table_close(pglrel, AccessShareLock);
}
/*
* Helper function for make_propgraphdef_properties(): Sort (propname, expr)
* pairs by name.
*/
static int
propdata_by_name_cmp(const ListCell *a, const ListCell *b)
{
List *la = lfirst_node(List, a);
List *lb = lfirst_node(List, b);
char *pna = strVal(linitial(la));
char *pnb = strVal(linitial(lb));
return strcmp(pna, pnb);
}
/*
* Generates element table properties clause (PROPERTIES (...) or NO
* PROPERTIES). Pass in label OID and element table OID. Result is appended
* to buf.
*/
static void
make_propgraphdef_properties(StringInfo buf, Oid ellabelid, Oid elrelid)
{
Relation plprel;
ScanKeyData scankey[1];
SysScanDesc scan;
HeapTuple tup;
List *outlist = NIL;
plprel = table_open(PropgraphLabelPropertyRelationId, AccessShareLock);
ScanKeyInit(&scankey[0],
Anum_pg_propgraph_label_property_plpellabelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(ellabelid));
/*
* We want to output the properties in a deterministic order. So we first
* read all the data, then sort, then print it.
*/
scan = systable_beginscan(plprel, PropgraphLabelPropertyLabelPropIndexId, true, NULL, 1, scankey);
while ((tup = systable_getnext(scan)))
{
Form_pg_propgraph_label_property plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
Datum exprDatum;
bool isnull;
char *tmp;
Node *expr;
char *propname;
exprDatum = heap_getattr(tup, Anum_pg_propgraph_label_property_plpexpr, RelationGetDescr(plprel), &isnull);
Assert(!isnull);
tmp = TextDatumGetCString(exprDatum);
expr = stringToNode(tmp);
pfree(tmp);
propname = get_propgraph_property_name(plpform->plppropid);
outlist = lappend(outlist, list_make2(makeString(propname), expr));
}
systable_endscan(scan);
table_close(plprel, AccessShareLock);
list_sort(outlist, propdata_by_name_cmp);
if (outlist)
{
List *context;
ListCell *lc;
bool first = true;
context = deparse_context_for(get_relation_name(elrelid), elrelid);
appendStringInfo(buf, " PROPERTIES (");
foreach(lc, outlist)
{
List *data = lfirst_node(List, lc);
char *propname = strVal(linitial(data));
Node *expr = lsecond(data);
if (first)
first = false;
else
appendStringInfo(buf, ", ");
if (IsA(expr, Var) && strcmp(propname, get_attname(elrelid, castNode(Var, expr)->varattno, false)) == 0)
appendStringInfo(buf, "%s", quote_identifier(propname));
else
appendStringInfo(buf, "%s AS %s",
deparse_expression_pretty(expr, context, false, false, 0, 0),
quote_identifier(propname));
}
appendStringInfo(buf, ")");
}
else
appendStringInfo(buf, " NO PROPERTIES");
}
/*
* pg_get_statisticsobjdef
* Get the definition of an extended statistics object
@ -7610,6 +7937,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(get_propgraph_label_name(lref->labelid)));
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, quote_identifier(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.
*
@ -8220,6 +8712,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:
/*
@ -10664,6 +11157,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(get_propgraph_property_name(gpr->propid)));
break;
}
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
break;
@ -12546,6 +13047,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, '(');

View file

@ -34,6 +34,8 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_propgraph_label.h"
#include "catalog/pg_propgraph_property.h"
#include "catalog/pg_publication.h"
#include "catalog/pg_range.h"
#include "catalog/pg_statistic.h"
@ -3934,3 +3936,39 @@ get_subscription_name(Oid subid, bool missing_ok)
return subname;
}
char *
get_propgraph_label_name(Oid labeloid)
{
HeapTuple tuple;
char *labelname;
tuple = SearchSysCache1(PROPGRAPHLABELOID, labeloid);
if (!tuple)
{
elog(ERROR, "cache lookup failed for label %u", labeloid);
return NULL;
}
labelname = pstrdup(NameStr(((Form_pg_propgraph_label) GETSTRUCT(tuple))->pgllabel));
ReleaseSysCache(tuple);
return labelname;
}
char *
get_propgraph_property_name(Oid propoid)
{
HeapTuple tuple;
char *propname;
tuple = SearchSysCache1(PROPGRAPHPROPOID, propoid);
if (!tuple)
{
elog(ERROR, "cache lookup failed for property %u", propoid);
return NULL;
}
propname = pstrdup(NameStr(((Form_pg_propgraph_property) GETSTRUCT(tuple))->pgpname));
ReleaseSysCache(tuple);
return propname;
}

View file

@ -2014,7 +2014,11 @@ ScanQueryForLocks(Query *parsetree, bool acquire)
break;
case RTE_SUBQUERY:
/* If this was a view, must lock/unlock the view */
/*
* If this was a view or a property graph, must lock/unlock
* it.
*/
if (OidIsValid(rte->relid))
{
if (acquire)

View file

@ -497,7 +497,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 */

View file

@ -510,6 +510,9 @@ do { \
/* UPDATE */
CONVERT_PRIV('w', "UPDATE");
}
else if (strcmp(type, "PROPERTY GRAPH") == 0 ||
strcmp(type, "PROPERTY GRAPHS") == 0)
CONVERT_PRIV('r', "SELECT");
else if (strcmp(type, "FUNCTION") == 0 ||
strcmp(type, "FUNCTIONS") == 0)
CONVERT_PRIV('X', "EXECUTE");

View file

@ -3853,6 +3853,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 ||

View file

@ -1851,10 +1851,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,
@ -3034,6 +3034,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;
@ -7484,7 +7487,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);
@ -16987,8 +16991,20 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
{
const char *objtype =
(tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
const char *objtype;
switch (tbinfo->relkind)
{
case RELKIND_SEQUENCE:
objtype = "SEQUENCE";
break;
case RELKIND_PROPGRAPH:
objtype = "PROPERTY GRAPH";
break;
default:
objtype = "TABLE";
break;
}
tableAclDumpId =
dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
@ -17234,8 +17250,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);
@ -17261,6 +17275,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);
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;
@ -17336,8 +17391,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);
@ -18081,6 +18134,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,
@ -20414,6 +20469,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");

View file

@ -3131,6 +3131,18 @@ 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;',
@ -4508,6 +4520,22 @@ my %tests = (
},
},
'GRANT SELECT ON PROPERTY GRAPH propgraph' => {
create_order => 21,
create_sql =>
'GRANT SELECT ON PROPERTY GRAPH dump_test.propgraph TO regress_dump_test_role;',
regexp => qr/^
\QGRANT ALL ON PROPERTY GRAPH dump_test.propgraph TO regress_dump_test_role;\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
unlike => {
exclude_dump_test_schema => 1,
no_privs => 1,
only_dump_measurement => 1,
},
},
'GRANT EXECUTE ON FUNCTION pg_sleep() TO regress_dump_test_role' => {
create_order => 16,
create_sql => 'GRANT EXECUTE ON FUNCTION pg_sleep(float8)

View file

@ -1056,7 +1056,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':
{
@ -1190,6 +1190,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':

View file

@ -24,6 +24,7 @@
#include "catalog/pg_constraint_d.h"
#include "catalog/pg_default_acl_d.h"
#include "catalog/pg_proc_d.h"
#include "catalog/pg_propgraph_element_d.h"
#include "catalog/pg_publication_d.h"
#include "catalog/pg_statistic_ext_d.h"
#include "catalog/pg_subscription_d.h"
@ -1068,6 +1069,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"
" ",
@ -1078,6 +1080,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"));
@ -1169,6 +1172,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)
@ -1909,6 +1913,78 @@ describeOneTableDetails(const char *schemaname,
goto error_return; /* not an error, just return early */
}
/*
* If it's a property graph, deal with it here separately.
*/
if (tableinfo.relkind == RELKIND_PROPGRAPH)
{
printQueryOpt myopt = pset.popt;
char *footers[3] = {NULL, NULL, NULL};
printfPQExpBuffer(&buf,
"SELECT e.pgealias AS \"%s\","
"\n pg_catalog.quote_ident(n.nspname) || '.' ||"
"\n pg_catalog.quote_ident(c.relname) AS \"%s\","
"\n case e.pgekind when " CppAsString2(PGEKIND_VERTEX) " then 'vertex'"
"\n when " CppAsString2(PGEKIND_EDGE) " then 'edge' end AS \"%s\","
"\n s.pgealias as \"%s\","
"\n d.pgealias as \"%s\""
"\n FROM pg_propgraph_element e"
"\n INNER JOIN pg_class c ON c.oid = e.pgerelid"
"\n INNER JOIN pg_namespace n ON c.relnamespace = n.oid"
"\n LEFT JOIN pg_propgraph_element s ON e.pgesrcvertexid = s.oid"
"\n LEFT JOIN pg_propgraph_element d ON e.pgedestvertexid = d.oid"
"\n WHERE e.pgepgid = '%s'"
"\n ORDER BY e.pgealias",
gettext_noop("Element Alias"),
gettext_noop("Element Table"),
gettext_noop("Element Kind"),
gettext_noop("Source Vertex Alias"),
gettext_noop("Destination Vertex Alias"),
oid);
res = PSQLexec(buf.data);
if (!res)
goto error_return;
printfPQExpBuffer(&title, _("Property Graph \"%s.%s\""),
schemaname, relationname);
/* Add property graph definition in verbose mode */
if (verbose)
{
PGresult *result;
printfPQExpBuffer(&buf,
"SELECT pg_catalog.pg_get_propgraphdef('%s'::pg_catalog.oid);",
oid);
result = PSQLexec(buf.data);
if (result)
{
if (PQntuples(result) > 0)
{
footers[0] = pg_strdup(_("Property graph definition:"));
footers[1] = pg_strdup(PQgetvalue(result, 0, 0));
}
PQclear(result);
}
}
myopt.footers = footers;
myopt.topt.default_footer = false;
myopt.title = title.data;
myopt.translate_header = true;
printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
free(footers[0]);
free(footers[1]);
retval = true;
goto error_return; /* not an error, just return early */
}
/* Identify whether we should print collation, nullable, default vals */
if (tableinfo.relkind == RELKIND_RELATION ||
tableinfo.relkind == RELKIND_VIEW ||
@ -4093,6 +4169,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
@ -4104,6 +4181,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;
int ntypes;
PQExpBufferData buf;
@ -4114,10 +4192,10 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
/* Count the number of explicitly-requested relation types */
ntypes = showTables + showIndexes + showViews + showMatViews +
showSeq + showForeign;
/* If none, we default to \dtvmsE (but see also command.c) */
showSeq + showForeign + showPropGraphs;
/* If none, we default to \dtvmsEG (but see also command.c) */
if (ntypes == 0)
showTables = showViews = showMatViews = showSeq = showForeign = true;
showTables = showViews = showMatViews = showSeq = showForeign = showPropGraphs = true;
initPQExpBuffer(&buf);
@ -4134,6 +4212,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"),
@ -4147,6 +4226,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;
@ -4234,6 +4314,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");
@ -4289,6 +4371,9 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
else if (showForeign)
pg_log_error("Did not find any foreign tables named \"%s\".",
pattern);
else if (showPropGraphs)
pg_log_error("Did not find any property graphs named \"%s\".",
pattern);
else /* should not get here */
pg_log_error_internal("Did not find any ??? named \"%s\".",
pattern);
@ -4309,6 +4394,8 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
pg_log_error("Did not find any sequences.");
else if (showForeign)
pg_log_error("Did not find any foreign tables.");
else if (showPropGraphs)
pg_log_error("Did not find any property graphs.");
else /* should not get here */
pg_log_error_internal("Did not find any ??? relations.");
}
@ -4323,6 +4410,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSys
(showMatViews) ? _("List of materialized views") :
(showSeq) ? _("List of sequences") :
(showForeign) ? _("List of foreign tables") :
(showPropGraphs) ? _("List of property graphs") :
"List of ???"; /* should not get here */
myopt.translate_header = true;
myopt.translate_columns = translate_columns;

View file

@ -219,8 +219,8 @@ slashUsage(unsigned short int pager)
HELP0("Informational\n");
HELP0(" (options: S = show system objects, x = expanded mode, + = additional detail)\n");
HELP0(" \\d[Sx+] list tables, views, and sequences\n");
HELP0(" \\d[S+] NAME describe table, view, sequence, or index\n");
HELP0(" \\d[Sx+] list tables, views, sequences, and property graphs\n");
HELP0(" \\d[S+] NAME describe table, view, sequence, index, or property graph\n");
HELP0(" \\da[Sx] [PATTERN] list aggregates\n");
HELP0(" \\dA[x+] [PATTERN] list access methods\n");
HELP0(" \\dAc[x+] [AMPTRN [TYPEPTRN]] list operator classes\n");
@ -246,6 +246,7 @@ slashUsage(unsigned short int pager)
HELP0(" \\dFp[x+] [PATTERN] list text search parsers\n");
HELP0(" \\dFt[x+] [PATTERN] list text search templates\n");
HELP0(" \\dg[Sx+] [PATTERN] list roles\n");
HELP0(" \\dG[Sx+] [PATTERN] list property graphs\n");
HELP0(" \\di[Sx+] [PATTERN] list indexes\n");
HELP0(" \\dl[x+] list large objects, same as \\lo_list\n");
HELP0(" \\dL[Sx+] [PATTERN] list procedural languages\n");

View file

@ -815,6 +815,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 = {
@ -1336,6 +1344,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},
@ -2739,6 +2748,20 @@ match_previous_words(int pattern_id,
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("ADD", "ALTER", "DROP", "OWNER TO", "RENAME TO", "SET SCHEMA");
else if (Matches("ALTER", "PROPERTY", "GRAPH", MatchAny, "ADD|ALTER|DROP"))
COMPLETE_WITH("VERTEX", "EDGE");
else if (Matches("ALTER", "PROPERTY", "GRAPH", MatchAny, "ADD|DROP", "VERTEX|EDGE"))
COMPLETE_WITH("TABLES");
else if (HeadMatches("ALTER", "PROPERTY", "GRAPH", MatchAny, "ADD") && TailMatches("EDGE"))
COMPLETE_WITH("TABLES");
else if (Matches("ALTER", "PROPERTY", "GRAPH", MatchAny, "ALTER", "VERTEX|EDGE"))
COMPLETE_WITH("TABLE");
/* ALTER RULE <name>, add ON */
else if (Matches("ALTER", "RULE", MatchAny))
COMPLETE_WITH("ON");
@ -3275,7 +3298,7 @@ match_previous_words(int pattern_id,
"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",
@ -3313,6 +3336,8 @@ match_previous_words(int pattern_id,
}
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"))
@ -3672,6 +3697,25 @@ match_previous_words(int pattern_id,
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", "(*)"))
COMPLETE_WITH("EDGE");
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))
@ -4403,6 +4447,12 @@ match_previous_words(int pattern_id,
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");
@ -4647,6 +4697,7 @@ match_previous_words(int pattern_id,
"LARGE OBJECT",
"PARAMETER",
"PROCEDURE",
"PROPERTY GRAPH",
"ROUTINE",
"SCHEMA",
"SEQUENCE",
@ -4805,6 +4856,14 @@ match_previous_words(int pattern_id,
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");
@ -5170,8 +5229,10 @@ match_previous_words(int pattern_id,
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");
@ -5652,6 +5713,8 @@ match_previous_words(int pattern_id,
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"))

View file

@ -293,6 +293,8 @@ less_equals "<="
greater_equals ">="
less_greater "<>"
not_equals "!="
/* Note there is no need for left_arrow, since "<-" is not a single operator. */
right_arrow "->"
/*
* "self" is the set of chars that should be returned as single-character
@ -304,7 +306,7 @@ not_equals "!="
* If you change either set, adjust the character lists appearing in the
* rule for "operator"!
*/
self [,()\[\].;\:\+\-\*\/\%\^\<\>\=]
self [,()\[\].;\:\|\+\-\*\/\%\^\<\>\=]
op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
operator {op_chars}+
@ -652,6 +654,10 @@ other .
ECHO;
}
{right_arrow} {
ECHO;
}
/*
* These rules are specific to psql --- they implement parenthesis
* counting and detection of command-ending semicolon. These must

View file

@ -81,7 +81,12 @@ 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_element_label.h \
pg_propgraph_label.h \
pg_propgraph_label_property.h \
pg_propgraph_property.h
GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h)

View file

@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202603161
#define CATALOG_VERSION_NO 202603162
#endif

View file

@ -69,6 +69,11 @@ catalog_headers = [
'pg_publication_rel.h',
'pg_subscription.h',
'pg_subscription_rel.h',
'pg_propgraph_element.h',
'pg_propgraph_element_label.h',
'pg_propgraph_label.h',
'pg_propgraph_label_property.h',
'pg_propgraph_property.h',
]
# The .dat files we need can just be listed alphabetically.

View file

@ -178,6 +178,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 */

View file

@ -3965,6 +3965,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' },

View file

@ -0,0 +1,118 @@
/*-------------------------------------------------------------------------
*
* pg_propgraph_element.h
* definition of the "property graph elements" system catalog (pg_propgraph_element)
*
* Portions Copyright (c) 1996-2026, 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
* ----------------
*/
BEGIN_CATALOG_STRUCT
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: Oids of the equality operators for comparing source keys
*/
Oid pgesrceqop[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];
/*
* for edges: Oids of the equality operators for comparing destination
* keys
*/
Oid pgedesteqop[1];
#endif
} FormData_pg_propgraph_element;
END_CATALOG_STRUCT
/* ----------------
* 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, 8315, 8316);
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 */

View file

@ -0,0 +1,55 @@
/*-------------------------------------------------------------------------
*
* pg_propgraph_element_label.h
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/catalog/pg_propgraph_element_label.h
*
* NOTES
* The Catalog.pm module reads this file and derives schema
* information.
*
*-------------------------------------------------------------------------
*/
#ifndef PG_PROPGRAPH_ELEMENT_LABEL_H
#define PG_PROPGRAPH_ELEMENT_LABEL_H
#include "catalog/genbki.h"
#include "catalog/pg_propgraph_element_label_d.h"
/* ----------------
* pg_propgraph_element_label definition. cpp turns this into
* typedef struct FormData_pg_propgraph_element_label
* ----------------
*/
BEGIN_CATALOG_STRUCT
CATALOG(pg_propgraph_element_label,8305,PropgraphElementLabelRelationId)
{
Oid oid;
/* OID of the label */
Oid pgellabelid BKI_LOOKUP(pg_propgraph_label);
/* OID of the property graph element */
Oid pgelelid BKI_LOOKUP(pg_propgraph_element);
} FormData_pg_propgraph_element_label;
END_CATALOG_STRUCT
/* ----------------
* Form_pg_propgraph_element_label corresponds to a pointer to a tuple with
* the format of pg_propgraph_element_label relation.
* ----------------
*/
typedef FormData_pg_propgraph_element_label *Form_pg_propgraph_element_label;
DECLARE_UNIQUE_INDEX_PKEY(pg_propgraph_element_label_oid_index, 8312, PropgraphElementLabelObjectIndexId, pg_propgraph_element_label, btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_propgraph_element_label_element_label_index, 8313, PropgraphElementLabelElementLabelIndexId, pg_propgraph_element_label, btree(pgelelid oid_ops, pgellabelid oid_ops));
DECLARE_INDEX(pg_propgraph_element_label_label_index, 8317, PropgraphElementLabelLabelIndexId, pg_propgraph_element_label, btree(pgellabelid oid_ops));
MAKE_SYSCACHE(PROPGRAPHELEMENTLABELELEMENTLABEL, pg_propgraph_element_label_element_label_index, 128);
#endif /* PG_PROPGRAPH_ELEMENT_LABEL_H */

View file

@ -0,0 +1,55 @@
/*-------------------------------------------------------------------------
*
* pg_propgraph_label.h
*
* Portions Copyright (c) 1996-2026, 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
* ----------------
*/
BEGIN_CATALOG_STRUCT
CATALOG(pg_propgraph_label,8303,PropgraphLabelRelationId)
{
Oid oid;
/* OID of the property graph relation */
Oid pglpgid BKI_LOOKUP(pg_class);
/* label name */
NameData pgllabel;
} FormData_pg_propgraph_label;
END_CATALOG_STRUCT
/* ----------------
* 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_graph_name_index, 8314, PropgraphLabelGraphNameIndexId, pg_propgraph_label, btree(pglpgid oid_ops, pgllabel name_ops));
MAKE_SYSCACHE(PROPGRAPHLABELOID, pg_propgraph_label_oid_index, 128);
MAKE_SYSCACHE(PROPGRAPHLABELNAME, pg_propgraph_label_graph_name_index, 128);
#endif /* PG_PROPGRAPH_LABEL_H */

View file

@ -0,0 +1,63 @@
/*-------------------------------------------------------------------------
*
* pg_propgraph_label_property.h
*
* Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/catalog/pg_propgraph_label_property.h
*
* NOTES
* The Catalog.pm module reads this file and derives schema
* information.
*
*-------------------------------------------------------------------------
*/
#ifndef PG_PROPGRAPH_LABEL_PROPERTY_H
#define PG_PROPGRAPH_LABEL_PROPERTY_H
#include "catalog/genbki.h"
#include "catalog/pg_propgraph_label_property_d.h"
/* ----------------
* pg_propgraph_label_property definition. cpp turns this into
* typedef struct FormData_pg_propgraph_label_property
* ----------------
*/
BEGIN_CATALOG_STRUCT
CATALOG(pg_propgraph_label_property,8318,PropgraphLabelPropertyRelationId)
{
Oid oid;
/* OID of the property */
Oid plppropid BKI_LOOKUP(pg_propgraph_property);
/* OID of the element label */
Oid plpellabelid BKI_LOOKUP(pg_propgraph_element_label);
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/* property expression */
pg_node_tree plpexpr BKI_FORCE_NOT_NULL;
#endif
} FormData_pg_propgraph_label_property;
END_CATALOG_STRUCT
/* ----------------
* Form_pg_propgraph_label_property corresponds to a pointer to a tuple with
* the format of pg_propgraph_label_property relation.
* ----------------
*/
typedef FormData_pg_propgraph_label_property *Form_pg_propgraph_label_property;
DECLARE_TOAST(pg_propgraph_label_property, 8319, 8320);
DECLARE_UNIQUE_INDEX_PKEY(pg_propgraph_label_property_oid_index, 8328, PropgraphLabelPropertyObjectIndexId, pg_propgraph_label_property, btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_propgraph_label_property_label_prop_index, 8329, PropgraphLabelPropertyLabelPropIndexId, pg_propgraph_label_property, btree(plpellabelid oid_ops, plppropid oid_ops));
MAKE_SYSCACHE(PROPGRAPHLABELPROP, pg_propgraph_label_property_label_prop_index, 128);
#endif /* PG_PROPGRAPH_LABEL_PROPERTY_H */

View file

@ -0,0 +1,64 @@
/*-------------------------------------------------------------------------
*
* pg_propgraph_property.h
*
* Portions Copyright (c) 1996-2026, 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
* ----------------
*/
BEGIN_CATALOG_STRUCT
CATALOG(pg_propgraph_property,8306,PropgraphPropertyRelationId)
{
Oid oid;
/* OID of the property graph relation */
Oid pgppgid BKI_LOOKUP(pg_class);
/* property name */
NameData pgpname;
/* data type of the property */
Oid pgptypid BKI_LOOKUP_OPT(pg_type);
/* typemod of the property */
int32 pgptypmod;
/* collation of the property */
Oid pgpcollation BKI_LOOKUP_OPT(pg_collation);
} FormData_pg_propgraph_property;
END_CATALOG_STRUCT
/* ----------------
* 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_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(pgppgid oid_ops, pgpname name_ops));
MAKE_SYSCACHE(PROPGRAPHPROPOID, pg_propgraph_property_oid_index, 128);
MAKE_SYSCACHE(PROPGRAPHPROPNAME, pg_propgraph_property_name_index, 128);
#endif /* PG_PROPGRAPH_PROPERTY_H */

View file

@ -0,0 +1,23 @@
/*-------------------------------------------------------------------------
*
* propgraphcmds.h
* prototypes for propgraphcmds.c.
*
* Portions Copyright (c) 1996-2026, 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 */

View file

@ -710,6 +710,19 @@ typedef struct RangeTableFuncCol
ParseLoc 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 */
ParseLoc location; /* token location, or -1 if unknown */
} RangeGraphTable;
/*
* RangeTableSample - TABLESAMPLE appearing in a raw FROM clause
*
@ -1003,6 +1016,42 @@ 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;
#define IS_EDGE_PATTERN(kind) ((kind) == EDGE_PATTERN_ANY || \
(kind) == EDGE_PATTERN_RIGHT || \
(kind) == EDGE_PATTERN_LEFT)
typedef struct GraphElementPattern
{
NodeTag type;
GraphElementPatternKind kind;
const char *variable;
Node *labelexpr;
List *subexpr;
Node *whereClause;
List *quantifier;
ParseLoc location;
} GraphElementPattern;
/****************************************************************************
* Nodes for a Query tree
****************************************************************************/
@ -1075,6 +1124,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 */
@ -1241,6 +1291,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):
*/
@ -2381,6 +2437,7 @@ typedef enum ObjectType
OBJECT_PARAMETER_ACL,
OBJECT_POLICY,
OBJECT_PROCEDURE,
OBJECT_PROPGRAPH,
OBJECT_PUBLICATION,
OBJECT_PUBLICATION_NAMESPACE,
OBJECT_PUBLICATION_REL,
@ -4186,6 +4243,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;
ParseLoc 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;
ParseLoc location;
} PropGraphEdge;
typedef struct PropGraphLabelAndProperties
{
NodeTag type;
const char *label;
struct PropGraphProperties *properties;
ParseLoc location;
} PropGraphLabelAndProperties;
typedef struct PropGraphProperties
{
NodeTag type;
List *properties;
bool all;
ParseLoc 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
* ----------------------

View file

@ -2178,6 +2178,30 @@ typedef struct ReturningExpr
Expr *retexpr; /* expression to be returned */
} ReturningExpr;
/*
* GraphLabelRef - label reference in label expression inside GRAPH_TABLE clause
*/
typedef struct GraphLabelRef
{
NodeTag type;
Oid labelid;
ParseLoc location;
} GraphLabelRef;
/*
* GraphPropertyRef - property reference inside GRAPH_TABLE clause
*/
typedef struct GraphPropertyRef
{
Expr xpr;
const char *elvarname;
Oid propid;
Oid typeId;
int32 typmod;
Oid collation;
ParseLoc location;
} GraphPropertyRef;
/*--------------------
* TargetEntry -
* a target entry (used in query target lists)

View file

@ -64,5 +64,8 @@ extern List *BuildOnConflictExcludedTargetlist(Relation targetrel,
Index exclRelIndex);
extern SortGroupClause *makeSortGroupClauseForSetOp(Oid rescoltype, bool require_hash);
extern void constructSetOpTargetlist(ParseState *pstate, SetOperationStmt *op,
const List *ltargetlist, const List *rtargetlist,
List **targetlist, const char *context, bool recursive);
#endif /* ANALYZE_H */

View file

@ -136,6 +136,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)
@ -147,6 +148,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("empty", EMPTY_P, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
@ -191,6 +193,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)
@ -297,6 +301,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)
@ -361,6 +366,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("quotes", QUOTES, UNRESERVED_KEYWORD, BARE_LABEL)
@ -374,6 +381,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)
@ -496,6 +504,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("virtual", VIRTUAL, UNRESERVED_KEYWORD, BARE_LABEL)

View file

@ -0,0 +1,24 @@
/*-------------------------------------------------------------------------
*
* parse_graphtable.h
* parsing of GRAPH_TABLE
*
*
* Portions Copyright (c) 1996-2026, 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/pg_list.h"
#include "parser/parse_node.h"
extern Node *transformGraphTablePropertyRef(ParseState *pstate, ColumnRef *cref);
extern Node *transformGraphPattern(ParseState *pstate, GraphPattern *graph_pattern);
#endif /* PARSE_GRAPHTABLE_H */

View file

@ -82,6 +82,7 @@ typedef enum ParseExprKind
EXPR_KIND_COPY_WHERE, /* WHERE condition in COPY FROM */
EXPR_KIND_GENERATED_COLUMN, /* generation expression for a column */
EXPR_KIND_CYCLE_MARK, /* cycle mark value */
EXPR_KIND_PROPGRAPH_PROPERTY, /* derived property expression */
} ParseExprKind;
@ -95,6 +96,21 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
Oid targetTypeId, int32 targetTypeMod,
int location);
/*
* Namespace for the GRAPH_TABLE reference being transformed.
*
* Labels, properties and variables used in the GRAPH_TABLE form the namespace.
* The names of the labels and properties used in GRAPH_TABLE are looked up using
* the OID of the property graph. Variables are collected in a list as graph
* patterns are transformed. This namespace is used to resolve label and property
* references in the GRAPH_TABLE.
*/
typedef struct GraphTableParseState
{
Oid graphid; /* OID of the graph being referenced */
List *variables; /* list of element pattern variables in
* GRAPH_TABLE */
} GraphTableParseState;
/*
* State information used during parse analysis
@ -174,6 +190,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
* p_resolve_unknowns: resolve unknown-type SELECT output columns as type TEXT
* (this is true by default).
*
* p_graph_table_pstate: Namespace for the GRAPH_TABLE reference being
* transformed, if any.
*
* p_hasAggs, p_hasWindowFuncs, etc: true if we've found any of the indicated
* constructs in the query.
*
@ -216,6 +235,8 @@ struct ParseState
* type text */
QueryEnvironment *p_queryEnv; /* curr env, incl refs to enclosing env */
GraphTableParseState *p_graph_table_pstate; /* Current graph table
* namespace, if any */
/* Flags telling about things found in the query: */
bool p_hasAggs;

View file

@ -82,6 +82,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,

View file

@ -0,0 +1,21 @@
/*-------------------------------------------------------------------------
*
* rewriteGraphTable.h
* Support for rewriting GRAPH_TABLE clauses.
*
*
* Portions Copyright (c) 1996-2026, 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 */

View file

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

View file

@ -166,6 +166,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)

View file

@ -213,6 +213,9 @@ extern char *get_publication_name(Oid pubid, bool missing_ok);
extern Oid get_subscription_oid(const char *subname, bool missing_ok);
extern char *get_subscription_name(Oid subid, bool missing_ok);
extern char *get_propgraph_label_name(Oid labeloid);
extern char *get_propgraph_property_name(Oid propoid);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
#define type_is_array_domain(typid) (get_base_element_type(typid) != InvalidOid)

View file

@ -335,6 +335,8 @@ less_equals "<="
greater_equals ">="
less_greater "<>"
not_equals "!="
/* Note there is no need for left_arrow, since "<-" is not a single operator. */
right_arrow "->"
/*
* "self" is the set of chars that should be returned as single-character
@ -346,7 +348,7 @@ not_equals "!="
* If you change either set, adjust the character lists appearing in the
* rule for "operator"!
*/
self [,()\[\].;\:\+\-\*\/\%\^\<\>\=]
self [,()\[\].;\:\|\+\-\*\/\%\^\<\>\=]
op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
operator {op_chars}+
@ -854,6 +856,10 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
return NOT_EQUALS;
}
{right_arrow} {
return RIGHT_ARROW;
}
{informix_special} {
/* are we simulating Informix? */
if (INFORMIX_MODE)
@ -947,7 +953,7 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
* that the "self" rule would have.
*/
if (nchars == 1 &&
strchr(",()[].;:+-*/%^<>=", yytext[0]))
strchr(",()[].;:|+-*/%^<>=", yytext[0]))
return yytext[0];
/*
@ -968,6 +974,8 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
return NOT_EQUALS;
if (yytext[0] == '!' && yytext[1] == '=')
return NOT_EQUALS;
if (yytext[0] == '-' && yytext[1] == '>')
return RIGHT_ARROW;
}
}

View file

@ -53,6 +53,7 @@ test: sql/quote
test: sql/show
test: sql/sqljson
test: sql/sqljson_jsontable
test: sql/sqlpgq
test: sql/insupd
test: sql/parser
test: sql/prepareas

Some files were not shown because too many files have changed in this diff Show more