GQL Essentials Data Modification
PDF ↗

Data Modification

Chapter 14Data Modification

This chapter formalizes how GQL data-modification statements transform the underlying property-graph store, treating updates as structured deltas that map one graph state to another. We give a declarative description of the insertion and deletion of graph elements and the in-place modification of labels and properties, and explain how these operations compose with pattern-based reads, identifier allocation, and constraint checking. The model makes implementation-defined details such as reference value types explicit, but is otherwise independent of any particular execution strategy.

Semantic interfaces used in this chapter. Let GTypeDesc denote the domain of graph-type descriptors. A graph entry has the abstract form

GraphEntry = ⟨G,GT?,I G⟩,

where G is its graph, GT? is either a reference to a descriptor in GTypeDesc or none, and IG is the dictionary of constraints defined directly on the graph. The current working graph site has a declared graph-reference type and is occupied by a graph reference value. That reference identifies the graph denoted here by Gcur; its corresponding graph entry or graph descriptor supplies the optional constraining graph-type reference and constraint dictionary. Write GEGcur for that entry or descriptor. When the graph is written G, GE denotes the corresponding active entry or descriptor. Let 𝔸𝕀𝔻 denote the domain of session authorization identifiers and let u ∈ 𝔸𝕀𝔻 denote the identifier under which a statement is executed. Finally, let 𝕊𝕋𝔸𝕋𝕌𝕊 denote the domain of five-character GQLSTATUS codes. To raise a code κ ∈ 𝕊𝕋𝔸𝕋𝕌𝕊 is to produce a failed outcome, which has no statement result. Let omitted denote an omitted statement result, and write success(R) for a successful outcome whose result is R, abstracting over its status objects; this is the projection of the Ok(R,ζ,ζ¯) outcome defined in Chapter 21. The later chapters on types, constraints, catalogs, authorization, and diagnostics refine these interfaces.

Implementation configuration. We make the implementation-defined choices used below explicit by an implementation configuration K. Formally, let

K = ⟨RefTyV ,RefTyE,LabMinV ,LabMaxV ,LabMinE,LabMaxE, PropMaxV ,PropMaxE⟩

where:

  • RefTyV ,RefTyE are implementation-defined reference value types for nodes and edges;
  • LabMinV ,LabMaxV ∈ ℕ ∪{∞} and LabMinE,LabMaxE ∈ ℕ ∪{∞} are the supported minimum and maximum cardinalities of node and edge label sets, respectively;
  • PropMaxV ,PropMaxE ∈ ℕ ∪{∞} are the supported maximum numbers of node and edge properties.

For the current working graph Gcur, the constraint dictionary IG in GEGcur contains the constraints defined directly for that graph. After a data-modifying operation, those constraints are dynamically validated against the modified graph. The subsequent graph-type conformance check, when a constraining graph type is present, also accounts for the constraints of that graph type. Constraints registered for unrelated graph or graph-type subjects are not consulted.

Whenever we write “implementation-defined” in this chapter, the corresponding choice is assumed to be represented in K.

Syntactic frame for data modification. At the concrete-syntax level, a linear data-modification statement is a pipeline consisting of an optional graph-selection header followed by simple data-accessing steps, at least one of which is data-modifying, and an optional primitive result statement at the end. The following highly simplified BNF captures this spine; detailed productions for each kind of query and DML primitive are given in later sections.

BNF
linear-dm-stmt ::= [ "USE" graph-expr ] dm-body [ result-step ]
dm-body       ::= { data-access-step } dm-primitive
                  { data-access-step }
data-access-step ::= query-step | dm-primitive
dm-primitive  ::= insert-stmt
            | set-stmt
            | remove-stmt
            | delete-stmt
            | "CALL" dm-procedure  /* data-modifying procedure */
query-step    ::= ... /* simple query statement */
result-step   ::= ... /* FINISH, RETURN, or SELECT; final only */
graph-expr    ::= ... /* graph reference / expression */
dm-procedure  ::= ... /* procedure name + arguments */

Semantically, the query steps determine a working table of bindings, while the data-modification primitives contribute a staged write-set (delta) relative to a chosen graph. The remainder of this chapter explains how the primitives in dm-primitive contribute node/edge insertion, deletion, and property/label updates, and how the resulting delta is validated and applied.

The simple data-modification statements require optional feature GD01; label additions and removals require GD02. A delete item containing a procedure body requires GD03; one simply containing a value expression other than a binding-variable reference requires GD04. Insertion of an undirected edge requires GH02.

14.1 INSERT and CREATE

An INSERT statement inserts new nodes and edges into the current working graph. Its body is an insert graph pattern composed of insert path patterns that (i) may declare element variables for newly created nodes/edges and (ii) may specify label and property sets for those elements. The statement has an empty declared type and returns an omitted result; it may, however, augment the working table with columns for declared element variables (see below).

Concrete syntax for INSERT. At the level of concrete syntax, INSERT uses a compact, path-shaped notation that mirrors the pattern language of MATCH. New nodes and edges are described by node and edge patterns, which may optionally introduce element variables, label lists, and property maps. The following simplified BNF sketch outlines the core structure of the INSERT pattern language.

BNF
insert-stmt   ::= "INSERT" insert-pattern
insert-pattern ::= insert-path { "," insert-path }
insert-path    ::= node-pattern { edge-pattern node-pattern }
node-pattern  ::= "(" [ var ] [ ( ":" | "IS" ) label-set ] 
                                      [ property-map ] ")"
edge-pattern  ::= "-["  [ var ] [ ( ":" | "IS" ) label-set ] 
                                      [ property-map ] "]->"
                | "<-[" [ var ] [ ( ":" | "IS" ) label-set ] 
                                      [ property-map ] "]-"
                | "~["  [ var ] [ ( ":" | "IS" ) label-set ]
                                      [ property-map ] "]~"
label-set     ::= label { "&" label }
property-map  ::= "{" ... "}"  /* key: value pairs, 
                                as in expressions */
var           ::= identifier
label         ::= identifier

Intuitively, each insert-path contributes one or more insert node/edge patterns to the abstract insert graph pattern IGP: an isolated node-pattern describes a node insertion, while each adjacent edge-pattern node-pattern extends the path with an edge and node. The semantic clauses below reinterpret these syntactic patterns as scheduled insertions of nodes and edges, equipped with label and property sets derived from the specified label lists and property maps. The undirected form requires optional feature GH02.

Typing and new columns. An insert element pattern is bound if the name of its declared element variable already occurs as a column of the incoming working table type or as a field of the incoming working record type. Its declared type is inherited from that column or field. Such a pattern reuses the corresponding incoming node reference and does not introduce a new column.

For every other insert element pattern in IGP that declares an element variable EV by name EVN, INSERT contributes a column named EVN whose type is an implementation-defined reference value type covering all nodes (resp. edges) that INSERT can bind to EV. If that type is closed, then each property key/value in the pattern is store-assigned as if to a transient site of the corresponding property type. Columns whose names are system-generated in IGP (introduced when an element variable name is omitted) are removed from the outgoing working table. The incoming record type is preserved; the outgoing table type equals the union of the incoming columns and the new EVN columns, minus the system-generated ones. The current working graph site must be defined.

Operational semantics. Let TABLE be the current working table, let Gcur be the graph referenced by the current working graph site, and let IDNSET be the set of system-generated variable names introduced by IGP. Execution of INSERTIGP transforms these objects according to

⟨Gcur,TABLE⟩INSERTIGP == = = = = = = ⇒ ⟨Gcur′,TABLE′,success(omitted)⟩,

provided that no exception is raised. Here Gcur′ is obtained from Gcur by performing all node and edge insertions scheduled by the procedure below, and TABLE′ is obtained by extending each input record with reference values for the newly inserted graph elements declared by non-bound insert element patterns, and then removing the columns whose names are system-generated.

Initially, let NEW_TABLE be an empty binding table whose columns are the union of the columns of TABLE and the columns contributed by the element variables declared by non-bound insert element patterns in IGP. For each record R ∈TABLE, process IGP in a fresh child execution context amended with R. Construct a record CR containing the reference values of the graph elements created for R, by processing each insert path pattern immediately contained in IGP as follows:

  1. Inserting node pattern. For every insert node pattern INP that is the defining pattern for an element variable EV , has the name EV N, and is not a bound insert element pattern:

    1. Determine the node label set NLNS, taking NLNS = ∅ if no label set is specified. Enforce the implementation-defined minimum and maximum node-label-set cardinalities. If |NLNS| is below the supported minimum, raise 22G0N (number of node labels below supported minimum); if it exceeds the supported maximum, raise 22G0P (number of node labels exceeds supported maximum).
    2. Compute the node property set NPS from the property key/value list, taking NPS = ∅ if no such list is specified. Each property value expression is evaluated in the child execution context and store-assigned to the corresponding new property. If the number of properties exceeds the implementation-defined maximum, raise 22G0S (number of node properties exceeds supported maximum).
    3. Schedule the insertion into Gcur of a new node NN with a new global object identifier, label set NLNS, and property set NPS. Let NR be the node reference value for NN, and include in CR the field named EV N with value NR. The same reference value is used by other insert element patterns that refer to this defining node pattern.
  2. Inserting edge pattern. For every insert edge pattern IEP that declares an element variable EV with name EV N:

    1. Determine the edge label set ELNS, taking ELNS = ∅ if no label set is specified, and enforce the implementation-defined minimum and maximum edge-label-set cardinalities. If |ELNS| is below the supported minimum, raise 22G0Q (number of edge labels below supported minimum); if it exceeds the supported maximum, raise 22G0R (number of edge labels exceeds supported maximum).

      Compute the edge property set EPS analogously, evaluating each property value expression in the child execution context and store-assigning its result to the corresponding new property. If the number of properties exceeds the implementation-defined maximum, raise 22G0T (number of edge properties exceeds supported maximum).

    2. Determine the left and right endpoint nodes from the nearest surrounding insert node patterns. If an endpoint pattern is a bound insert element pattern, use the node denoted by its bound element variable; otherwise, use the node reference value computed for its defining insert node pattern. If either endpoint node is deleted, raise G1002 (endpoint node is deleted). If either endpoint node is not in the current working graph, raise G1003 (endpoint node not in current working graph).

      Determine whether the new edge is directed or undirected from the syntactic form of IEP. For a directed edge, use the arrow orientation to determine which endpoint is the source and which is the destination.

    3. Schedule the insertion into Gcur of a new edge NE with a new global object identifier, label set ELNS, property set EPS, and the determined endpoints, direction, and orientation. Let ER be the edge reference value for NE, and include in CR the field named EV N with value ER.

After all immediately contained insert path patterns have been processed for R, construct a record NRR by amending R with CR, and append NRR to NEW_TABLE.

After all records of TABLE have been processed, perform all scheduled node insertions, followed by all scheduled edge insertions. Let Gcur′ be the resulting provisional graph. Dynamically validate every direct constraint C ∈ ran ⁡ (IG) on Gcur′; an enforcement failure raises 23000 (integrity constraint violation). If Gcur has a constraining graph type GT and Gcur′ is not of GT, raise G2000 (graph type violation). Consequently, a successful INSERT has passed the required direct-constraint validation and graph-type conformance check, including the constraints of that graph type.

Finally, let TABLE′ be a copy of NEW_TABLE with all columns whose names belong to IDNSET removed, and set TABLE′ as the current working table. The execution outcome is successful with an omitted result; thus, INSERT modifies the graph and propagates the extended working table, but does not itself return a result value. See Section 4.3 for a complete insertion of library nodes, edges, labels, and properties.

Identifiers and allocation. Every created graph element (node or edge) receives a new global object identifier; copies likewise obtain fresh identifiers. Reference values reify globally resolved references to such objects. Element variable names that the syntax supplies automatically are system-generated regular identifiers and are not retained as output columns.

CREATE GRAPH (catalog). A CREATE GRAPH statement creates a new graph using an open ANY graph form or constraining it by an existing graph type reference, an inline graph type specification, or a graph type LIKE another graph. When a source graph is provided, all nodes and edges are copied into the new graph and each copy is assigned a new global object identifier. The created graph descriptor records the constraining graph type (if any). A violation of a constraint defined directly on the graph raises 23000; a graph-type violation raises G2000.

14.2 DELETE

Concrete syntax for DELETE. DELETE statements operate on a list of expressions whose declared types are graph-element reference value types. At runtime, valid node and edge references are collected for deletion, while the null value and invalidated references are ignored without accessing their referents. An optional prefix keyword controls whether incident edges are automatically removed or must already be absent. The following simplified BNF captures the core concrete form:

BNF
delete-stmt ::= [ "DETACH" | "NODETACH" ] "DELETE"
                delete-arg { "," delete-arg }
delete-arg  ::= expr  /* must evaluate to a node or 
                                  edge reference */
expr        ::= ...   /* value expression */

Intuitively, a delete-stmt with NODETACH (or without any prefix, for which GQL makes NODETACH implicit) is interpreted using the plain delete semantics below, which require that every incident edge of a deleted node is also designated for deletion. A statement prefixed by DETACH is interpreted using the detach delete semantics, which first removes incident edges and then removes the node itself. In both cases, the deletion of edges referenced directly by delete-arg follows the edge-deletion rules outlined next.

Incident edges. For x ∈ V , let inc(x) = {e ∈ E∣src(e) = x ∨dst(e) = x}.

Plain DELETE. A plain DELETE, for which NODETACH is implicit, first collects the sets V del ⊆ V and Edel ⊆ E of nodes and edges designated for deletion. For every v ∈ V del, deletion is permitted only if

inc(v) ⊆ Edel;

otherwise, the statement signals a dependent-object error (G1001, edges still exist). Every e ∈ Edel is deleted unconditionally, without affecting its endpoint nodes. If the condition holds for all v ∈ V del, all designated nodes and edges are deleted atomically, together with their labels and properties.

Example (plain deletion of an edge).

GQL
USE libraryGraph
MATCH (l:Loan)-[i:INCLUDES]->(b:Book)
WHERE b.isbn = '978-83-987...'
DELETE i
The statement deletes only the selected

INCLUDES

edge; its endpoint nodes

l

and

b

remain in the graph.

DETACH DELETE. For a node v ∈ V , DETACH DELETE v adds every edge e ∈inc(v) to the set of elements to be deleted and then deletes v. It does not recursively delete nodes adjacent to v. Additional nodes must be designated explicitly by including them as separate items in the comma-separated delete-item list, as in DETACH DELETE v1, v2. See Section 4.3 for a concrete use of DETACH DELETE in the library graph.

Constraint validation. The sets of nodes and edges to be deleted, including the incident edges added by DETACH, are collected before any deletion is performed. The resulting copy G′ is then checked as one atomic result. Its direct constraints are dynamically validated, and, if the graph has a constraining graph type, G′ is checked for conformance to that type. Deleting elements cannot by itself create a violation of the key or unique constraints defined by GQL. A graph-type violation raises G2000; this check is retained for implementation-defined extensions and future graph-type constraints even though the constraints currently defined by GQL do not make it reachable by deletion alone.

14.3 Read-then-Update Composition

A data-modifying statement commonly combines a read phase with an update phase. The read query Q is evaluated first against the current graph state G, under the authorization context of principal u, producing a binding table

BQu := ⟦Q⟧ G,u.

Each record in BQu identifies graph elements or supplies values that may be used by the subsequent update U. Thus, the result of Q does not itself modify the graph; rather, it determines the bindings with respect to which U is evaluated.

For an update part U, graph G, binding table B, and authorization identifier u, write ΔU(G,B,u) for the coalesced update delta produced by evaluating U for all records of B, as defined concretely in Section 14.4. The partial expression G ⊕Δ denotes application of a delta Δ to G. Let GE = ⟨G,GT?,I G⟩ be the active graph entry. The full validation predicate is

okGE(G,Δ)⇔G ⊕Δ is defined, satisfies every direct constraint in  ran ⁡ (IG), and is of its constraining graph type (if any).

The update phase computes a graph delta

ΔU,Qu := Δ U(G,BQu,u),

which collects the insertions, deletions, label changes, and property changes requested by U for the bindings produced by Q. Conceptually, U is evaluated for the records of BQu, and the individual changes are combined into a single delta. If BQu is empty, an update whose effects depend entirely on those bindings normally produces the empty delta and leaves the graph unchanged.

The denotation of the composed read-then-update operation is

⟦U∘Q⟧G,u = { G ⊕ΔU,Qu,if principal u is authorized to execute both Q and U, and okGE(G,ΔU,Qu), error, otherwise.

Here, G ⊕ΔU,Qu denotes the graph obtained by applying the complete delta to G. The authorization condition covers both access to the data inspected by Q and permission to perform the modifications requested by U. The predicate okGE(G,ΔU,Qu) states that the delta is applicable to G, that the resulting graph satisfies every direct constraint, and that it conforms to its constraining graph type, including the constraints of that graph type.

Accordingly, the composition is atomic at the level of this denotation: either the complete authorized and valid delta is applied, producing the new graph state, or the operation yields an error and no resulting graph state is defined by the statement.

14.4 Write-Set Coalescing and Conflicts

Concrete syntax for SET. A SET statement updates properties and labels of elements named by variables that are bound by preceding query steps (for example, by MATCH). Each item in the SET list either assigns a single property, replaces the entire property map, or adds a label to the element’s label set. The following simplified BNF describes the core concrete form:

BNF
set-stmt ::= "SET" set-item { "," set-item }

set-item ::= var "." prop "=" expr
    | var "=" "{" ... "}" /* replace all properties of var */
    | var ( ":" | "IS" ) label /* add label to var */

var   ::= identifier /* binding variable from MATCH, etc. */
prop  ::= identifier /* property name */
label ::= identifier
expr  ::= ...     /* value expression */

Informally, a set-item of the first form assigns a value to a single property of the element denoted by var, the second form replaces all existing properties of that element by the given map, and the third form adds a label to the element’s label set (using either the shorthand var:Label or the more verbose var IS Label). In the semantic model below, these surface forms are reinterpreted as combinations of primitive property-setting, property-clearing, and label-addition atoms that constitute an update template. See Section 4.3 for a concrete read-then-SET operation on a library edge property.

Update context. For a graph pattern P, predicate ϕ, and concrete write part U, an update pipeline is represented at the meta-level as

MATCH PWHERE ϕ ⊳ U

where ⊳ is meta-notation, not a GQL keyword; examples of U include SET, REMOVE, and DELETE. The read part yields a bag M = ⟦PWHERE ϕ⟧G of mappings over variables (node-, edge-, and path-variables). Updates are then staged per μ ∈M and coalesced before application to the transaction-visible working state.

Targets. Evaluation of a target binding-variable reference under μ yields either a graph-element reference or the null value. A null target contributes no modification. A target reference that is used for an update is therefore either a node-id v ∈ V or an edge-id e ∈ E; accessing the referent of an invalidated reference raises 22G11. We write Tgt(μ) ⊆ V ∪ E for the valid targets referenced by U under μ.

Update atoms. For v ∈ V , e ∈ E, k ∈ 𝕂, ℓ ∈ 𝕃, and a value expression expr, the primitive update atoms are listed below. Here ρV +,ρ E+,λ V +,λ E+ denote the provisional post-update maps obtained from ρV ,ρE,λV ,λE by the indicated assignment; every unmentioned map entry is unchanged.

SET_NODE_PROP(v,k,expr) : ρV +(v,k) ←⟦expr⟧ μ SET_EDGE_PROP(e,k,expr) : ρE+(e,k) ←⟦expr⟧ μ CLEAR_NODE_PROPS(v) : ρV +(v,⋅) ←∅ CLEAR_EDGE_PROPS(e) : ρE+(e,⋅) ←∅ REMOVE_NODE_PROP(v,k) : ρV +(v,k) ←absent REMOVE_EDGE_PROP(e,k) : ρE+(e,k) ←absent ADD_NODE_LABEL(v,ℓ) : λV +(v) ← λ V (v) ∪{ℓ} ADD_EDGE_LABEL(e,ℓ) : λE+(e) ← λ E(e) ∪{ℓ} REMOVE_NODE_LABEL(v,ℓ) : λV +(v) ← λ V (v) ∖{ℓ} REMOVE_EDGE_LABEL(e,ℓ) : λE+(e) ← λ E(e) ∖{ℓ}.

The two property-setting rows abbreviate the standard store-assignment rules, including their nullability and conversion checks. A surface item var = {...} contributes the appropriate CLEAR atom followed by one property-setting atom for each property pair in textual order. Endpoint reattachment is not an in-place update; changing endpoints is modeled as deletion followed by recreation.

Notational shorthands (ground update operations). The primitive atoms above mix two concerns: (i) the target (a concrete node/edge id) and (ii) an r-value expression that must be evaluated under a mapping μ. For examples and for write-set reasoning, it is convenient to use ground operations where the r-value has already been evaluated to a concrete value.

Let v ∈ V be a node identifier, a ∈ E an edge identifier, k ∈ 𝕂 a property key, ℓ ∈ 𝕃 a label, q a value expression, and w ∈ 𝕍 a concrete value. Let groundμ(⋅) be the function that maps update atoms to ground operations by evaluating r-values under μ. We use the following shorthands:

groundμ(SET_NODE_PROP(v,k,q)) ≜ setPropV(v,k,⟦q⟧μ)    (set vertex
property)                                                              groundμ(SET_EDGE_PROP(a,k,q)) ≜ setPropE(a,k,⟦q⟧μ)    (set edge

property)                                                               groundμ(CLEAR_NODE_PROPS(v)) ≜ clearPropsV(v)    (clear all vertex
properties)                                                         groundμ(CLEAR_EDGE_PROPS(a)) ≜ clearPropsE(a)    (clear all edge
properties)                                                           groundμ(REMOVE_NODE_PROP(v,k)) ≜ delPropV(v,k)    (delete vertex

property)                                                           groundμ(REMOVE_EDGE_PROP(a,k)) ≜ delPropE(a,k)    (delete edge
property)                                                             groundμ(ADD_NODE_LABEL(v,ℓ)) ≜ addLabelV(v,ℓ)    (add vertex
label)                                                             groundμ(ADD_EDGE_LABEL(a,ℓ)) ≜ addLabelE(a,ℓ)    (add edge
label)                                                               groundμ(REMOVE_NODE_LABEL(v,ℓ)) ≜ delLabelV(v,ℓ)    (remove  vertex

label)                                                          groundμ(REMOVE_EDGE_LABEL(a,ℓ)) ≜ delLabelE(a,ℓ)    (remove  edge
label).                                                           

Thus, for example, setPropV(v,k,w) abbreviates the ground effect “set the vertex-property (v,k) to the value w”.

Templates and determinism. An update template U is a finite, textually ordered list of atoms whose l-values are expressions over μ. For a fixed μ, evaluation produces a per-binding write-set WSμ(U), represented as a list of ground operations on concrete element identifiers. Concretely, WSμ(U) is obtained by applying groundμ to the atoms of U, evaluating all r-values under μ, while preserving their textual order. A set item whose target is null contributes the empty list. A set-all- properties item contributes a clear operation followed by its evaluated property assignments; an attempted access through an invalidated target raises 22G11 rather than contributing a write.

Standard options. For a single SET statement that would make multiple assignments to the same (id,k), even if the assigned values are equal, the GQL standard specifies an implementation-defined choice between two behaviors:

  • signal data exception 22G0M, or
  • make an implementation-dependent choice of one assignment.

In this book we adopt a canonical partial coalescer ⊞ that realizes option (a) while preserving the order of records and set items required by the standard. Property-assignment operations are pairwise compatible only when they target distinct (id,k) pairs. Label additions commute, and repeated additions of the same label to the same element are idempotent and may coalesce to one effective addition. Clear-all-properties operations are retained at their ordered positions. Whenever two property assignments target the same (id,k) in one statement, ⊞ is undefined and the statement signals 22G0M. Different μ-instances are folded in incoming-table order, subject to these rules.

Staged delta and application. Identifying the binding table B with its bag M of mappings, the staged delta introduced in Section 14.4 is

ΔU(G,B,u) := ⊞ ⁡μ∈MWSμ(U),

where the indexed ⊞ folds the partial coalescer and is defined only when the combined write-set is pairwise-compatible as above. Within this section, ΔU abbreviates ΔU(G,B,u). Applying the ordered ΔU to G yields G′ by clearing property sets for set-all-properties items, performing their property assignments in textual order, overwriting individual properties, adding labels, and leaving all other components unchanged. After each non-null set item, exceeding the supported maximum for a node label set, node property set, edge label set, or edge property set raises 22G0P, 22G0S, 22G0R, or 22G0T, respectively. The direct constraints on G are dynamically validated against G′. If G has a constraining graph type GT and G′ is not of GT, the statement raises G2000; this conformance check includes the constraints of GT.

Read stability. All l-values and r-values in U are evaluated over the pre-update working state that produced M. Thus, intra-statement reads do not observe effects of the same statement’s writes; only the final ΔU is materialized atomically. This statement-level evaluation rule does not by itself select the transaction isolation extension discussed in Chapter 19.

Examples of coalescing and conflict detection. The following examples illustrate how the canonical coalescer handles repeated updates generated by bag-valued pattern matching. The first example considers multiple identical and idempotent label additions, which can be safely reduced to a single effective update. The second considers incompatible property writes to the same target and property key, which cannot be coalesced and therefore result in an intra-statement conflict.

(A) Idempotent label addition under bag matches. Suppose the read pattern P matches the same vertex i with multiplicity 5. Formally, the match result is a bag M that contains five mappings μ1,… ⁡,μ5 such that the l-value of the label update refers to i under each mapping (e.g., ⟦v⟧μj = i for j = 1,… ⁡, 5). Let the write part be a single label-add atom ADD_NODE_LABEL(i,ℓ).

Then, for each j = 1,… ⁡, 5, evaluation of the template yields the same per-binding write-set:

WSμj(U) =[ADD_NODE_LABEL(i,ℓ)].

Hence, the staged delta is obtained by folding these five identical contributions:

ΔU = ⊞ ⁡μ∈MWSμ(U) = WSμ1(U) ⊞⋯ ⊞WSμ5(U).

Because label addition is idempotent and duplicates are pairwise compatible under the canonical coalescer, the five identical atoms coalesce into a single effective update:

ΔU = {ADD_NODE_LABEL(i,ℓ)}.

Therefore, although P matches i five times, the statement is equivalent, with respect to the final store state, to a single addition of ℓ to λV (i).

(B) Conflicting property writes. Consider a mapping μ ∈M and two syntactically distinct l-values, for example x.k and y.k, that satisfy the static non-duplication rule but whose variable references alias at runtime: μ(x) = μ(y) = i. Assume that their expressions produce two different ground writes to the same pair (i,k):

setPropV(i,k, 1) ∈WSμ(U)andsetPropV(i,k, 2) ∈WSμ(U),with 1≠2.

Equivalently, this arises from the atoms

SET_NODE_PROP(i,k,e1)andSET_NODE_PROP(i,k,e2),

such that ⟦e1⟧μ = 1 and ⟦e2⟧μ = 2.

Under the standard-permitted option (a) adopted by the canonical model here, the coalescer ⊞ is defined only for pairwise-compatible operations. Since the two ground writes target the same pair (i,k) but prescribe different values, their union is not compatible. Consequently, ⊞ is undefined, the indexed ⊞ rejects the combined write-set, and the statement signals 22G0M, irrespective of the textual ordering of the two writes.

Concrete syntax for REMOVE. A REMOVE statement removes properties and labels from elements named by variables that are bound by preceding query steps (typically by MATCH). The following simplified BNF mirrors the style of SET and isolates the core concrete form of the statement:

BNF
remove-stmt ::= "REMOVE" remove-item { "," remove-item }

remove-item ::= var "." prop  
              | var ( ":" | "IS" ) label 

var   ::= identifier  /* binding variable from MATCH, etc. */
prop  ::= identifier  /* property name */
label ::= identifier  /* label name */

Each remove-item uses a binding variable whose declared type is a graph-element reference (node or edge). The first form var.prop schedules the removal of a single property (if present) from the element denoted by var. The second form var:Label or var IS Label schedules removal of a label (if present) from the element’s label set. The statement has empty declared type and does not change the working table: incoming and outgoing working record/table types coincide, and the result is omitted.

Remove templates and atoms. We reuse the update context and the notion of an update target introduced for SET. Consider an update pipeline

MATCH PWHERE ϕREMOVE I1,… ⁡,Im,

whose read part yields the bag

M = ⟦PWHERE ϕ⟧G

of mappings over node, edge, and path variables. Each occurrence of a mapping in M is processed separately; hence duplicate mappings may generate duplicate removal atoms.

The construction of the update proceeds in three stages. First, for every mapping occurrence μ ∈M and every remove item Ij, the binding-variable reference contained in Ij is evaluated under μ:

tμ,j := ⟦varj⟧μ,G.

This target-resolution stage determines the node or edge to which the item refers but does not yet modify the graph. In particular, all targets are resolved with respect to the bindings produced by the read part before the corresponding removal effects are applied.

Second, the resolved items are inspected in textual order. Each item contributes either one concrete removal atom or no atom. Writing 𝜖 for the empty list, let

AG(μ,Ij) ∈ {𝜖, [REMOVE_NODE_PROP(v,k)],[REMOVE_EDGE_PROP(e,k)], [REMOVE_NODE_LABEL(v,ℓ)],[REMOVE_EDGE_LABEL(e,ℓ)] }

be the contribution of Ij. The per-binding write-set is the textual-order concatenation

WSμ(Urem) = AG(μ,I1) ++ ⋯ ++ AG(μ,Im),

where ++ denotes list concatenation.

Finally, the per-binding write-sets may be folded over all occurrences to give an extensional summary of the removals completed by a successful statement:

Δrem = ⊞ ⁡μ∈MWSμ(Urem).

This delta notation is not a replacement for the operational order required by REMOVE. Records are processed in current-working-table order and, for each record, remove items are performed in textual order. The applicable label-cardinality check is made immediately after each label item. After every remove item, the graph’s direct constraints are dynamically validated and its conformance to the constraining graph type is checked. An exception stops that ordered execution. Repeated removals remain harmless once the property or label is absent.

The contribution AG(μ,Ij) is determined as follows.

  • Property removal var.prop. Let

    t = tμ,j

    be the previously resolved target and let k be the property name.

    If t is the null value, then the item has no effect:

    AG(μ,Ij) = 𝜖.

    If t is an invalidated reference, inspecting its property set raises 22G11. Otherwise:

    • if t ∈ V and k occurs in the node-property set of t, then

      AG(μ,Ij) = [REMOVE_NODE_PROP(t,k)];
    • if t ∈ E and k occurs in the edge-property set of t, then

      AG(μ,Ij) = [REMOVE_EDGE_PROP(t,k)];
    • if k is not present in the property set of t, then

      AG(μ,Ij) = 𝜖.
  • Label removal var:Label or var IS Label. Let

    t = tμ,j

    be the previously resolved target and let ℓ be the label name.

    If t is the null value, then the item has no effect:

    AG(μ,Ij) = 𝜖.

    If t is an invalidated reference, inspecting its label set raises 22G11. Otherwise:

    • if t ∈ V and ℓ ∈ λV (t), then

      AG(μ,Ij) = [REMOVE_NODE_LABEL(t,ℓ)];
    • if t ∈ E and ℓ ∈ λE(t), then

      AG(μ,Ij) = [REMOVE_EDGE_LABEL(t,ℓ)];
    • if ℓ is not contained in the label set of t, then

      AG(μ,Ij) = 𝜖.

Thus, WSμ(Urem) records the removal effects generated by one occurrence of a match, whereas Δrem collects the effects generated by the complete match bag. Applying Δrem removes the indicated properties and labels without deleting the affected nodes or edges themselves.

Extensional coalescing for REMOVE. For describing the final graph of a successful execution, write-sets generated for different binding occurrences can be summarized using the canonical coalescer ⊞:

Δrem := ⊞ ⁡μ∈MWSμ(Urem).

Because Urem contains only removal atoms, all contributions are pairwise compatible:

  • Removing the same property or label from the same element multiple times is idempotent; duplicate atoms may therefore be coalesced into a single removal.
  • Removing different properties or labels, whether from the same or different graph elements, commutes; their order does not affect the final graph state.

Consequently, ⊞ is defined when combining raw write-sets that contain only removal atoms, and there is no analog of the multiple assignments to the same property conflict (status 22G0M) that may arise for SET. This extensional commutativity does not license reordering the operational steps: intermediate label-cardinality and graph-type checks remain observable. A successful REMOVE statement has a uniquely determined summary Δrem. This delta may be empty when every remove item resolves to null, or when none of the indicated properties or labels is present; an invalidated reference instead raises 22G11 when its referent is accessed.

Application and label-set cardinalities. Applying Δrem to G produces a graph G′ in which:

  • Every REMOVE_NODE_PROP and REMOVE_EDGE_PROP atom deletes the indicated property from the respective property set, if present.
  • Every REMOVE_NODE_LABEL and REMOVE_EDGE_LABEL atom removes the designated label from the respective label set, if present.

Immediately after each label-removal item, the node-label set λV (v) (resp. edge-label set λE(e)) must still satisfy the implementation-defined minimum label-set cardinality. If the resulting cardinality for a node falls below the supported minimum, the statement signals data exception 22G0N; if it falls below the supported minimum for an edge, it signals 22G0Q. These label-set cardinality checks are data-exception rules of the operation. After each remove item, the graph’s direct constraints are dynamically validated. If the containing graph has a constraining graph type but the graph is not of that type, the operation raises G2000; this conformance check includes the constraints of that graph type.

Read stability. A REMOVE statement inherits the same statement-level read stability as SET. All variable references used as l-values in remove-items are evaluated over the incoming working-table records. The removals are then performed in the record and item order described above; Δrem is only an extensional summary of a successful final effect. Transaction rollback rules govern the effects of a failed procedure.

14.5 Update Model for Property Graphs

The mutable graph state is the complete property-graph tuple introduced in Chapter 6:

G = ⟨V,E,λV ,ρV ,λE,ρE,src,dst,dir⟩.

Primitive mutations (expressible using GQL data-modification statements) are modeled as deltas over all of these components:

insNode(v, Λ,P), delNode(v), insEdge(e, Λ,P,u,v,d), delEdge(e), setPropV(v,k,w),setPropE(e,k,w), clearPropsV(v),clearPropsE(e), delPropV(v,k),delPropE(e,k), addLabelV(v,ℓ),addLabelE(e,ℓ), delLabelV(v,ℓ),delLabelE(e,ℓ),

where Λ and P are the new element’s label and property sets, u,v ∈ V are endpoints, and d records whether an edge is directed and, when directed, its orientation. Application of a well-formed delta updates the corresponding components together; deleting a node is defined only when every incident edge is also deleted. A statement U therefore denotes a partial transformation G↦G ⊕ΔU. Failure of dynamic validation for a direct constraint in ran ⁡ (IG) raises 23000. Independently, if the result of a primitive data-modifying statement is not of its constraining graph type, its mandatory conformance check raises G2000.

14.6 Dynamic Constraint Validation

Some DML forms are set-based: effects are computed from the whole input multiset to avoid statement-order anomalies (e.g., key and unique-constraint satisfaction), then applied atomically. After a primitive data-modifying operation, every constraint defined directly on the modified graph is dynamically validated. The graph is then checked for conformance to its constraining graph type, when present; that check includes the constraints of the graph type. Constraints registered for unrelated catalog subjects are not consulted.

GQL Essentials · Dominik Tomaszuk

Search chapters, definitions and examples.