1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/*
* Copyright (c) 2025-2026 Anton Kundenko <singaraiona@gmail.com>
* All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* datalog.h — Datalog evaluation engine for Rayforce
*
* Compiles Datalog rules into ray_graph_t operation DAGs and evaluates
* them to fixpoint using Rayforce's vectorized columnar execution engine.
* Supports semi-naive evaluation, stratified negation, and multi-rule heads.
*/
/* == */
/* == */
/* == */
/* == */
/* == */
/* == */
typedef enum dl_expr_kind_t;
typedef struct dl_expr dl_expr_t;
/* Variable index sentinel: constant value, not a variable */
/* Predicate name of the built-in 3-arity entity/attribute/value relation
* that ray_query_fn registers from the `db` argument (and that the triple-
* pattern `(?e :attr ?v)` sugar compiles atoms against). This is the only
* relation whose column 2 (the v column) is ever DATOM-tagged (below) --
* the strict_tag gates in dl_col_eq_row's callers, and the two
* dl_rule_add_atom(rule, DL_EAV_PRED, 3) triple-pattern call sites, all key
* off this one name so the coupling between "this predicate" and "this
* column may carry a DATOM tag" is explicit and in one place. */
/* ===== DATOM value tag scheme (v column of a datoms table) =====
* assert-fact stores a symbol/string value in an otherwise-plain I64
* cell with one of these tags OR'd into the top bits so it can never
* collide with a bare integer equal to the same intern id. dl_col_eq_row
* strips the tag (via the const_type of the body literal) to compare;
* user-visible reads (pull, scan-eav, query projection) strip it via
* dl_datom_untag so callers keep seeing plain intern ids.
*
* Value-range limit: only the top 3 bits (61-63) are reserved for the tag
* and its sign guard (see dl_datom_untag below), so a *positive* I64 value
* of 2^61 or greater stored in the datoms v column -- or appearing in any
* I64 query result column that flows through dl_untag_i64_col -- has its
* top bits indistinguishable from a tag and is masked down to its low 61
* bits on output. This is a pre-existing limit of reusing the top bits for
* tagging, not something this fix introduces further constraints on; it
* simply means the v column (and any I64 result derived from it) is not a
* safe place to store the full 63-bit positive integer range. */
/* A negative plain integer has every high bit set by sign extension, so it
* spuriously matches DL_DATOM_TAG_MASK; guard with v >= 0 since a genuine
* tagged cell is always non-negative (built from a small, non-negative
* intern id OR'd with a tag that never sets the sign bit) -- without this,
* untagging would corrupt ordinary negative I64 result values (e.g. from
* datalog arithmetic) by masking off their sign-extended high bits. */
static inline int64_t
/* Maximum arity for any relation */
/* Maximum number of body literals per rule */
/* Maximum number of rules in a program */
/* Maximum number of relations */
/* Maximum strata */
/* Program flags */
/* == */
typedef struct dl_body_t;
/* == */
typedef struct dl_rule_t;
/* == */
typedef struct dl_rel_t;
/* == */
typedef struct dl_program_t;
/* == */
/* Create a new empty Datalog program */
dl_program_t* ;
/* Free a Datalog program and release all owned tables */
void ;
/** Append rules registered via the Rayfall (rule ...) special form into a program. */
void ;
/* Register an EDB (extensional) relation backed by an existing table.
* Column names are auto-generated as "c0", "c1", ... unless the table
* already has named columns. */
int ;
/* Add a rule to the program. The rule struct is copied. */
int ;
/* Compute stratification (topological sort of negation dependency graph).
* Returns 0 on success, -1 if program has unstratifiable negation cycle. */
int ;
/* Evaluate the program to fixpoint using semi-naive evaluation.
* Returns 0 on success, -1 on error. */
int ;
/* Query the result of a derived relation after evaluation.
* Returns the backing table (caller does NOT own it). */
ray_t* ;
/* Retrieve the provenance column from a derived relation.
* Only valid when DL_FLAG_PROVENANCE is set. Returns the I64 column
* of rule indices, or NULL if provenance not enabled/available. */
ray_t* ;
/* Retrieve deep provenance source offsets for a derived relation.
* Returns an I64 vector of length nrows+1 in CSR format: offsets[i] is the
* start index in the source-data vector for derived row i.
* Only valid when DL_FLAG_PROVENANCE is set. Returns NULL if unavailable. */
ray_t* ;
/* Retrieve deep provenance source data for a derived relation.
* Returns a flat I64 vector of packed source references. Each entry encodes
* (relation_index << 32) | row_index, identifying which EDB or IDB relation
* and row contributed to deriving a given output tuple. Row indices are
* truncated to 32 bits (max ~4 billion rows per relation).
*
* For rules with body-only variables (variables appearing in body atoms but
* not in the head), source entries include all body rows consistent with
* head-visible bindings. Cross-body join constraints are not re-enforced
* during source lookup, so entries may be a superset of the true derivation.
*
* Only valid when DL_FLAG_PROVENANCE is set. Returns NULL if unavailable. */
ray_t* ;
/* == */
/* Initialize a rule with the given head predicate and arity */
void ;
/* Set a head argument to a variable */
void ;
/* Set a head argument to an I64 constant — backward-compatible
* signature. Equivalent to dl_rule_head_const_typed(rule, pos, val,
* RAY_I64). Prefer the typed variant for new code. */
void ;
/* Set a head argument to a typed constant.
* type must be RAY_I64, RAY_SYM, or RAY_F64.
* For RAY_F64 callers should pass a double reinterpreted via memcpy/union
* into val's int64 slot; dl_rule_head_const_f64 is the safe wrapper. */
void ;
/* Convenience wrapper: set a head argument to a RAY_F64 constant. */
void ;
/* Add a positive body atom. Returns body literal index. */
int ;
/* Set a body atom argument to a variable */
void ;
/* Set a body atom argument to a constant. Defaults the type tag to RAY_I64;
* use dl_body_set_const_typed when the literal's source ray type matters
* (RAY_STR / RAY_SYM body literals must record their type so the row-
* equality helper can compare them against DATOM-encoded I64 columns). */
void ;
/* Set a body atom argument to a typed constant. `ray_type` is the source
* ray type of the literal (RAY_I64 / RAY_SYM / RAY_STR / RAY_F64), used at
* filter time to decide whether a tagged-payload compare is needed when
* the column is RAY_I64 with DATOM-encoded entries. */
void ;
/* Add a negated body atom. Returns body literal index. */
int ;
/* Add a comparison. Returns body literal index. */
int ;
/* Add a comparison with a constant RHS. Returns body literal index. */
int ;
/* Add an assignment: target_var = expr. Returns body literal index. */
int ;
/* Add a builtin predicate. Returns body literal index.
* Arguments are set via dl_body_set_var (same as atoms). */
int ;
/* Add a comparison with expression trees on both sides.
* E.g., "X + Y < Z * 2" -> cmp_op=DL_CMP_LT, lhs=binop(+,X,Y), rhs=binop(*,Z,2).
* Returns body literal index. */
int ;
/* Add an interval bind: decompose two consecutive columns at the fact variable's
* position into start_var and end_var. Returns body literal index. */
int ;
/* pred_arity is advisory; evaluator re-resolves against program EDB/IDB at compile time. */
/* Add an aggregate body literal: (op ?target pred col)
* - op: DL_AGG_COUNT (col is ignored), DL_AGG_SUM/MIN/MAX/AVG
* - target_var: variable that receives the aggregate result
* - pred: predicate to aggregate over
* - pred_arity: arity of that predicate
* - value_col: which column to aggregate (ignored for COUNT)
* Returns body literal index. */
int ;
/* Attach group-by keys to an aggregate body literal previously added via
* dl_rule_add_agg. body_idx is that builder's return value.
* key_vars and key_cols have n_keys entries (<= DL_AGG_MAX_KEYS).
* Returns 0 on success, -1 if n_keys is out of range. */
int ;
/* == */
/* Create a constant expression */
dl_expr_t* ;
/* Create a float constant expression */
dl_expr_t* ;
/* Create a variable reference expression */
dl_expr_t* ;
/* Create a binary operation expression (OP_ADD, OP_SUB, OP_MUL, OP_DIV) */
dl_expr_t* ;
/* ===== Expression tree ownership =====
*
* A dl_rule_t owns the expression trees its body[] points to
* (assign_expr, cmp_lhs_expr, cmp_rhs_expr). dl_add_rule deep-clones the
* rule it is given, so the caller retains ownership of the trees it built
* and must free them itself (dl_rule_free_exprs). dl_program_free frees
* the expression trees owned by every rule in the program. The rules in
* g_dl_rules[] own their trees until ray_dl_reset_rules frees them. */
/* Recursively free an expression tree. NULL-safe. */
void ;
/* Deep-copy an expression tree. NULL -> NULL. Returns NULL on OOM. */
dl_expr_t* ;
/* Free and NULL every expression tree owned by rule->body[]. */
void ;
/* Replace every expression in rule->body[] with a deep clone of itself.
* Returns 0 on success, -1 on OOM (rule is left expr-free on failure). */
int ;
/* == */
/* Find relation by name. Returns index or -1. */
int ;
/* Ensure an IDB relation exists for the given head predicate.
* Creates it with the correct arity if it doesn't exist yet. */
int ;
/* Which body position, if any, reads the semi-naive delta instead of
* the full relation. NULL delta = evaluate against full relations. */
typedef struct dl_delta_t;
/* Compile one rule into a ray_graph_t for one fixpoint iteration.
* delta: which body position (if any) reads the semi-naive delta table
* instead of the full relation; NULL means evaluate against full relations.
* rule_idx: index of this rule in prog->rules (used for provenance).
* Returns the output node in g that produces new head tuples. */
ray_op_t* ;
/* RAYFORCE_DATALOG_H */