You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Refactor DSL to use nested JSON graph construction
Fixes#42
DSL functions now build stateless nested JSON structures instead of
writing to df.nodes during construction. This eliminates orphaned nodes,
enables transaction-safe graph building, and simplifies df.explain().
Key changes:
- Durofut now embeds children as Box<Durofut> instead of ID references
- Durofut no longer includes a node_id field; IDs are generated when
writing to the database, making Durofut a pure data structure without
database concerns.
- df.start() recursively inserts all nodes in a single transaction
- df.explain() parses nested JSON directly without temp tables
- Add E2E test 26_graph_reuse.sql to verify graph storage and reuse
Copy file name to clipboardExpand all lines: USER_GUIDE.md
+22-9Lines changed: 22 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -138,6 +138,19 @@ Functions are persisted to disk. If PostgreSQL crashes:
138
138
- In-progress steps resume from the last checkpoint
139
139
- Pending steps execute when the server restarts
140
140
141
+
### Graph Construction
142
+
143
+
DSL functions build graph structures **in memory** without touching the database. Only when you call `df.start()` are the nodes written to the database:
144
+
145
+
```sql
146
+
-- This creates a JSON string representing the graph.
A `Durofut` represents a node in the function graph. It's serialized as JSON and passed between DSL functions.
189
+
A `Durofut` represents an abstract function graph, sub-graph or leaf node. It's serialized as JSON and passed between DSL functions.
190
190
191
191
```rust
192
192
// src/types.rs
193
193
pubstructDurofut {
194
-
pubnode_id:String, // 8-char hex ID (e.g., "a1b2c3d4")
195
-
pubnode_type:String, // SQL, THEN, IF, JOIN, LOOP, etc.
196
-
publeft_node:Option<String>, // Left child node ID
197
-
pubright_node:Option<String>,// Right child node ID
198
-
pubquery:Option<String>, // SQL query or config JSON
199
-
pubresult_name:Option<String>, // Named result (from |=> operator)
194
+
pubnode_type:String, // SQL, THEN, IF, JOIN, LOOP, etc.
195
+
publeft_node:Option<Box<Durofut>>, // Embedded left child
196
+
pubright_node:Option<Box<Durofut>>, // Embedded right child
197
+
pubquery:Option<String>, // SQL query or config JSON
198
+
pubresult_name:Option<String>, // Named result (from |=> operator)
200
199
}
201
200
```
202
201
203
202
When serialized to JSON:
204
203
```json
205
204
{
206
-
"node_id": "a1b2c3d4",
207
-
"node_type": "SQL",
208
-
"query": "SELECT 1"
205
+
"node_type": "THEN",
206
+
"left_node": {
207
+
"node_type": "SQL",
208
+
"query": "SELECT 1"
209
+
},
210
+
"right_node": {
211
+
"node_type": "SQL",
212
+
"query": "SELECT 2"
213
+
}
209
214
}
210
215
```
211
216
212
217
#### FunctionNode (Database Representation)
213
218
214
-
Nodes are persisted in `df.nodes`:
219
+
`df.start(<function>)` adds a new row to `df.instances`, then iterates the nodes in the function graph bottom up, persisting each one to the `df.nodes` table along with the instance ID, a new ID for the node, and the IDs of its child nodes, if any.
215
220
216
221
```sql
217
222
CREATETABLEdf.nodes (
218
223
id VARCHAR(8) PRIMARY KEY,
219
-
instance_id VARCHAR(8), -- Set by df.start() during linking
224
+
instance_id VARCHAR(8), -- Set by df.start()
220
225
node_type TEXTNOT NULL, -- SQL, THEN, IF, JOIN, LOOP, etc.
221
226
query TEXT, -- SQL query or config JSON
222
227
result_name TEXT, -- Named result for $variable substitution
223
-
left_node VARCHAR(8), -- Left child
224
-
right_node VARCHAR(8), -- Right child
228
+
left_node VARCHAR(8), -- Left child ID
229
+
right_node VARCHAR(8), -- Right child ID
225
230
status TEXT DEFAULT 'pending',
226
231
result JSONB,
227
232
created_at TIMESTAMPTZ DEFAULT now()
@@ -230,24 +235,20 @@ CREATE TABLE df.nodes (
230
235
231
236
### DSL Functions
232
237
233
-
Each DSL function (`df.sql`, `df.sleep`, `df.join`, etc.) creates a node and returns its JSON representation.
238
+
Each DSL function (`df.sql`, `df.sleep`, `df.join`, etc.) creates a Durofut and returns its JSON representation. All graph construction is stateless.
234
239
235
240
#### Example: `df.sql()`
236
241
237
242
```rust
238
243
// src/dsl.rs
239
244
#[pg_extern(schema ="df")]
240
245
pubfnsql(query:&str) ->String {
241
-
letdurofut=Durofut {
242
-
node_id:short_id(), // Generate 8-char hex ID
246
+
Durofut {
243
247
node_type:"SQL".to_string(),
244
-
left_node:None,
245
-
right_node:None,
246
-
query:Some(query.to_string()), // Store the SQL query
0 commit comments