Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,11 @@ add_library(core
src/query_execution.cpp
src/join.cpp
src/row.cpp
src/shard.cpp
src/storage.cpp
src/metadata.cpp
src/file_utils.cpp
src/snapshot.cpp
src/snapshot_manager.cpp
src/edge_store.cpp
src/table_info.cpp
src/utils.cpp
Expand Down
18 changes: 17 additions & 1 deletion antlr/TundraQL.g4
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
grammar TundraQL;

// Entry point for parsing a full command
statement: createSchemaStatement | createNodeStatement | createEdgeStatement | matchStatement | deleteStatement | commitStatement | showStatement EOF;
statement: createSchemaStatement | createNodeStatement | createEdgeStatement | matchStatement | deleteStatement | updateStatement | commitStatement | showStatement EOF;

// --- Schema Definition ---
createSchemaStatement: K_CREATE K_SCHEMA IDENTIFIER LPAREN schemaFieldList RPAREN SEMI;
Expand Down Expand Up @@ -47,6 +47,20 @@ edgeDeleteTarget:
| K_EDGE IDENTIFIER K_TO nodeSelector // DELETE EDGE edge_type TO node;
| K_EDGE IDENTIFIER K_FROM nodeSelector K_TO nodeSelector; // DELETE EDGE edge_type FROM node TO node;

// --- Update Statement ---
// UPDATE User(0) SET name = "Bob", age = 31;
// UPDATE (u:User) SET u.age = 31 WHERE u.name = "Alice";
// UPDATE MATCH (u:User)-[:WORKS_AT]->(c:Company) SET u.employed = true, c.size = 1 WHERE c.name = "Acme";
updateStatement: K_UPDATE updateTarget K_SET setClause (K_WHERE whereClause)? SEMI;

updateTarget:
nodeLocator // UPDATE User(0) SET ...;
| K_MATCH patternList // UPDATE MATCH (u:User)-[:WORKS_AT]->(c:Company) SET ...;
| nodePattern; // UPDATE (u:User) SET ... WHERE ...;

setClause: setAssignment (COMMA setAssignment)*;
setAssignment: IDENTIFIER (DOT IDENTIFIER)? EQ value;

// --- Commit Statement ---
commitStatement: K_COMMIT SEMI;

Expand Down Expand Up @@ -106,6 +120,8 @@ K_RIGHT: 'RIGHT';
K_FULL: 'FULL';
K_AND: 'AND';
K_OR: 'OR';
K_UPDATE: 'UPDATE';
K_SET: 'SET';
K_COMMIT: 'COMMIT';
K_UNIQUE: 'UNIQUE';
K_SHOW: 'SHOW';
Expand Down
125 changes: 124 additions & 1 deletion docs/tundraql.html
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,15 @@ <h4>Statements</h4>
<a href="#node">CREATE NODE</a>
<a href="#edge">CREATE EDGE</a>
<a href="#match">MATCH</a>
<a href="#update">UPDATE</a>
<a href="#delete">DELETE</a>
<a href="#commit">COMMIT</a>
<a href="#show">SHOW</a>
<div class="sep"></div>
<h4>Clauses</h4>
<a href="#where">WHERE</a>
<a href="#select">SELECT</a>
<a href="#set">SET</a>
<a href="#joins">JOIN Types</a>
<div class="sep"></div>
<h4>Reference</h4>
Expand Down Expand Up @@ -300,6 +302,85 @@ <h2 id="delete">DELETE</h2>
<span class="kw">DELETE EDGE</span> <span class="edge-type">works_at</span> <span class="kw">TO</span> <span class="alias">Company</span>(<span class="num">0</span>);</code></pre>
</div>

<!-- ========== UPDATE ========== -->
<h2 id="update">UPDATE</h2>
<p>Modifies field values on existing nodes. Supports three forms: <strong>by ID</strong> (direct), <strong>by pattern</strong> (single schema + optional <code>WHERE</code>), and <strong>by MATCH</strong> (traversals / joins + optional <code>WHERE</code>).</p>

<h3>Form 1 — Update by ID</h3>
<p>Targets a single node using <code>Schema(id)</code>. Field names are bare (no alias prefix).</p>

<div class="syntax-block">
<div class="label">Syntax <span class="tag">DML</span></div>
<pre><code><span class="kw">UPDATE</span> <span class="alias">Schema</span>(<span class="num">id</span>) <span class="kw">SET</span> <span class="alias">field</span> = <span class="str">value</span> [, <span class="alias">field</span> = <span class="str">value</span> ...] ;</code></pre>
</div>

<div class="syntax-block">
<div class="label">Examples</div>
<pre><code><span class="cmt">// Update a single field</span>
<span class="kw">UPDATE</span> <span class="alias">User</span>(<span class="num">0</span>) <span class="kw">SET</span> <span class="alias">age</span> = <span class="num">31</span>;

<span class="cmt">// Update multiple fields at once (creates one version)</span>
<span class="kw">UPDATE</span> <span class="alias">User</span>(<span class="num">0</span>) <span class="kw">SET</span> <span class="alias">name</span> = <span class="str">"Alice B."</span>, <span class="alias">age</span> = <span class="num">31</span>;</code></pre>
</div>

<h3>Form 2 — Update by Pattern</h3>
<p>Uses a node pattern <code>(alias:Schema)</code> with an optional <code>WHERE</code> clause to match nodes. Field names must be alias-qualified (<code>alias.field</code>).</p>

<div class="syntax-block">
<div class="label">Syntax <span class="tag">DML</span></div>
<pre><code><span class="kw">UPDATE</span> (<span class="alias">alias</span>:<span class="type">Schema</span>) <span class="kw">SET</span> <span class="alias">alias</span>.<span class="alias">field</span> = <span class="str">value</span> [, ...]
[<span class="kw">WHERE</span> <span class="alias">alias</span>.<span class="alias">field</span> <span class="op">op</span> <span class="str">value</span>] ;</code></pre>
</div>

<div class="syntax-block">
<div class="label">Examples</div>
<pre><code><span class="cmt">// Update all users named Alice</span>
<span class="kw">UPDATE</span> (<span class="alias">u</span>:<span class="type">User</span>) <span class="kw">SET</span> <span class="alias">u</span>.<span class="alias">age</span> = <span class="num">31</span>
<span class="kw">WHERE</span> <span class="alias">u</span>.<span class="alias">name</span> <span class="op">=</span> <span class="str">"Alice"</span>;

<span class="cmt">// Update with compound condition</span>
<span class="kw">UPDATE</span> (<span class="alias">u</span>:<span class="type">User</span>) <span class="kw">SET</span> <span class="alias">u</span>.<span class="alias">name</span> = <span class="str">"Senior"</span>
<span class="kw">WHERE</span> <span class="alias">u</span>.<span class="alias">age</span> <span class="op">&gt;</span> <span class="num">30</span> <span class="kw">AND</span> <span class="alias">u</span>.<span class="alias">age</span> <span class="op">&lt;</span> <span class="num">50</span>;

<span class="cmt">// Update all nodes of a schema (no WHERE)</span>
<span class="kw">UPDATE</span> (<span class="alias">u</span>:<span class="type">User</span>) <span class="kw">SET</span> <span class="alias">u</span>.<span class="alias">age</span> = <span class="num">0</span>;</code></pre>
</div>

<h3>Form 3 — Update by MATCH (traversals / joins)</h3>
<p>Uses a full <code>MATCH</code> pattern with traversals to find nodes across multiple schemas, then applies <code>SET</code> assignments. Field names must be alias-qualified. Multiple schemas can be updated in a single statement.</p>

<div class="syntax-block">
<div class="label">Syntax <span class="tag">DML</span></div>
<pre><code><span class="kw">UPDATE MATCH</span> (<span class="alias">a</span>:<span class="type">Schema1</span>)-[:<span class="edge-type">EDGE_TYPE</span>]-&gt;(<span class="alias">b</span>:<span class="type">Schema2</span>)
<span class="kw">SET</span> <span class="alias">a</span>.<span class="alias">field</span> = <span class="str">value</span> [, <span class="alias">b</span>.<span class="alias">field</span> = <span class="str">value</span> ...]
[<span class="kw">WHERE</span> <span class="alias">alias</span>.<span class="alias">field</span> <span class="op">op</span> <span class="str">value</span>] ;</code></pre>
</div>

<div class="syntax-block">
<div class="label">Examples</div>
<pre><code><span class="cmt">// Update both user and company for a traversal</span>
<span class="kw">UPDATE MATCH</span> (<span class="alias">u</span>:<span class="type">User</span>)-[:<span class="edge-type">WORKS_AT</span>]-&gt;(<span class="alias">c</span>:<span class="type">Company</span>)
<span class="kw">SET</span> <span class="alias">u</span>.<span class="alias">employed</span> = <span class="num">true</span>, <span class="alias">c</span>.<span class="alias">size</span> = <span class="num">1</span>
<span class="kw">WHERE</span> <span class="alias">c</span>.<span class="alias">name</span> <span class="op">=</span> <span class="str">"Acme Corp"</span>;

<span class="cmt">// Update only one side of the relationship</span>
<span class="kw">UPDATE MATCH</span> (<span class="alias">u</span>:<span class="type">User</span>)-[:<span class="edge-type">WORKS_AT</span>]-&gt;(<span class="alias">c</span>:<span class="type">Company</span>)
<span class="kw">SET</span> <span class="alias">u</span>.<span class="alias">status</span> = <span class="str">"employed"</span>
<span class="kw">WHERE</span> <span class="alias">c</span>.<span class="alias">name</span> <span class="op">=</span> <span class="str">"Google"</span>;

<span class="cmt">// Same-schema traversal (e.g. friends)</span>
<span class="kw">UPDATE MATCH</span> (<span class="alias">a</span>:<span class="type">User</span>)-[:<span class="edge-type">FRIEND</span>]-&gt;(<span class="alias">b</span>:<span class="type">User</span>)
<span class="kw">SET</span> <span class="alias">a</span>.<span class="alias">has_friend</span> = <span class="num">true</span>, <span class="alias">b</span>.<span class="alias">has_friend</span> = <span class="num">true</span>;</code></pre>
</div>

<div class="info-box">
<strong>Versioning:</strong> When multiple fields are updated in a single <code>SET</code> clause, TundraDB creates <strong>one version</strong> for the entire batch — not one per field.
</div>

<div class="warn-box">
<strong>Pattern/MATCH forms require alias prefix:</strong> <code>SET age = 31</code> is only valid in the by-ID form. In pattern and MATCH forms you must write <code>SET u.age = 31</code>.
</div>

<!-- ========== COMMIT ========== -->
<h2 id="commit">COMMIT</h2>
<p>Persists the current database state to disk (Parquet files + JSON metadata).</p>
Expand Down Expand Up @@ -360,6 +441,37 @@ <h2 id="select">SELECT Clause</h2>
<span class="kw">SELECT</span> <span class="alias">u</span>.<span class="alias">name</span> <span class="kw">AS</span> <span class="alias">employee</span>, <span class="alias">c</span>.<span class="alias">name</span> <span class="kw">AS</span> <span class="alias">company</span>, <span class="alias">u</span>.<span class="alias">age</span>;</code></pre>
</div>

<!-- ========== SET ========== -->
<h2 id="set">SET Clause</h2>
<p>Specifies field assignments in an <code>UPDATE</code> statement. Comma-separated list of <code>field = value</code> pairs.</p>

<div class="syntax-block">
<div class="label">Syntax</div>
<pre><code><span class="kw">SET</span> <span class="alias">field</span> = <span class="str">value</span> [, <span class="alias">field</span> = <span class="str">value</span> ...]</code></pre>
</div>

<table class="type-table">
<tr>
<th>Update Form</th>
<th>Field Name Format</th>
<th>Example</th>
</tr>
<tr>
<td>By ID</td>
<td>Bare name</td>
<td><code>SET name = "Alice", age = 31</code></td>
</tr>
<tr>
<td>By Pattern</td>
<td>Alias-qualified</td>
<td><code>SET u.name = "Alice", u.age = 31</code></td>
</tr>
</table>

<div class="info-box">
<strong>Batch semantics:</strong> All assignments in a single <code>SET</code> clause are applied atomically — one version is created per node, regardless of how many fields are changed.
</div>

<!-- ========== JOIN TYPES ========== -->
<h2 id="joins">JOIN Types</h2>
<p>Specified inside the edge pattern <code>-[:EDGE_TYPE <em>JOIN</em>]-></code>. Controls how unmatched nodes are handled.</p>
Expand Down Expand Up @@ -481,7 +593,18 @@ <h2 id="patterns">Pattern Syntax Reference</h2>
<span class="kw">WHERE</span> <span class="alias">u</span>.<span class="alias">name</span> <span class="op">=</span> <span class="str">"Alice"</span> <span class="kw">AND</span> <span class="alias">c</span>.<span class="alias">name</span> <span class="op">=</span> <span class="str">"Google"</span>
<span class="kw">SELECT</span> <span class="alias">u</span>.<span class="alias">name</span> <span class="kw">AS</span> <span class="alias">user</span>, <span class="alias">f</span>.<span class="alias">name</span> <span class="kw">AS</span> <span class="alias">friend</span>, <span class="alias">c</span>.<span class="alias">name</span> <span class="kw">AS</span> <span class="alias">company</span>;

<span class="cmt">// 5. Persist</span>
<span class="cmt">// 5. Update: Alice turned 31</span>
<span class="kw">UPDATE</span> <span class="alias">User</span>(<span class="num">0</span>) <span class="kw">SET</span> <span class="alias">age</span> = <span class="num">31</span>;

<span class="cmt">// 6. Bulk update: set all users older than 30 to "Senior"</span>
<span class="kw">UPDATE</span> (<span class="alias">u</span>:<span class="type">User</span>) <span class="kw">SET</span> <span class="alias">u</span>.<span class="alias">name</span> = <span class="str">"Senior"</span> <span class="kw">WHERE</span> <span class="alias">u</span>.<span class="alias">age</span> <span class="op">&gt;</span> <span class="num">30</span>;

<span class="cmt">// 7. Update with MATCH (traversal) — set employed flag for Google employees</span>
<span class="kw">UPDATE MATCH</span> (<span class="alias">u</span>:<span class="type">User</span>)-[:<span class="edge-type">works_at</span>]-&gt;(<span class="alias">c</span>:<span class="type">Company</span>)
<span class="kw">SET</span> <span class="alias">u</span>.<span class="alias">name</span> = <span class="str">"Employed"</span>
<span class="kw">WHERE</span> <span class="alias">c</span>.<span class="alias">name</span> <span class="op">=</span> <span class="str">"Google"</span>;

<span class="cmt">// 8. Persist</span>
<span class="kw">COMMIT</span>;</code></pre>
</div>

Expand Down
Loading