C Stable API

Runtime EDB

Build complete request fact sets with the stable opaque input buffer.

The stable facade accepts runtime facts either as one borrowed array passed to maelys_datalog_session_solve(), or in an opaque, reusable maelys_datalog_input_edb_t buffer passed to maelys_datalog_session_solve_edb(). These are two input paths for a complete request snapshot, not incremental updates to the previous solve. See the EDB concept for the distinction between request facts and policy facts.

Types

Defined on this page

TypePurpose
maelys_datalog_input_edb_tOpaque, reusable request-fact buffer.
maelys_datalog_fact_tOne predicate and its typed terms, used in a batch.

Used from other pages

These types are defined on the linked pages; this page uses them in the roles below.

TypeUse on this pageDefined in
maelys_datalog_diagnostic_tOptional output argument receiving insertion details.Errors

Functions

Start with the common operations. Capacity and memory management are useful when the application needs explicit limits or supplies its own storage; inspection functions observe an existing buffer. All are declared in <maelys/datalog.h>.

Common operations

FunctionPurpose
maelys_datalog_input_edb_create()Allocate a default-capacity EDB.
maelys_datalog_input_edb_add_fact()Append one typed fact.
maelys_datalog_input_edb_add_facts()Append one atomic batch.
maelys_datalog_input_edb_clear()Start a fresh request in the same buffer.
maelys_datalog_input_edb_free()End the handle lifetime.

Capacity and memory management

FunctionPurpose
maelys_datalog_input_edb_create_with_capacity()Allocate an EDB with explicit, fixed fact and text capacities.
maelys_datalog_input_edb_storage_requirements() / maelys_datalog_input_edb_init()Place the EDB handle in caller-owned aligned memory.

Inspection functions

FunctionPurpose
maelys_datalog_input_edb_count()Count stored raw entries.
maelys_datalog_input_edb_text_usage()Inspect committed string-storage occupancy.
maelys_datalog_input_edb_view()Borrow committed raw facts until mutation.

Function-like macro builders

These C11 conveniences are distinct from the public EDB functions below. A macro with () accepts arguments; it is not an exported function.

Function-like macroPurpose
MAELYS_DATALOG_SYMBOL(value)Marks a borrowed C string as a symbol-valued term.
MAELYS_DATALOG_BOOL(value)Requests boolean rather than integer conversion for a C expression.
MAELYS_DATALOG_FACT(...)Builds one borrowed checked descriptor for an atomic batch.
MAELYS_DATALOG_ADD_FACT(edb, diagnostic, ...)Adds one typed fact through the input EDB API.
MAELYS_DATALOG_ADD_FACTS(edb, diagnostic, ...)Adds one batch through the input EDB API.

Input EDB handle

C · opaque
maelys_datalog_input_edb_t
typedef struct maelys_datalog_input_edb maelys_datalog_input_edb_t;
Opaque, reusable buffer for the complete input facts of one request. Create or initialize it, add facts, solve, then clear it before another request.

Input fact type

C · struct
maelys_datalog_fact_t
typedef struct {
    const char *predicate;
    size_t arity;
    maelys_datalog_value_t terms[MAELYS_DATALOG_PUBLIC_MAX_TERMS];
} maelys_datalog_fact_t;
One input fact: a predicate name, its arity and up to four typed terms.
Fields
predicateconst char *
Registered predicate that receives the fact.
aritysize_t
Number of populated terms; must match the predicate declaration.
termsmaelys_datalog_value_t[4]
Typed values copied into the input buffer when the fact is added.

Common use

The quickstart follows this lifecycle: create an input buffer, add request facts, solve, then release the buffer. For later requests, clear and refill the same buffer. The default buffer has fixed capacities and never grows automatically.

Create an input buffer

C · function
Create a default input EDB
maelys_datalog_status_t
maelys_datalog_input_edb_create(
maelys_datalog_input_edb_t ** out_edb
);
Creates an empty input buffer using the installed profile’s default capacities. The application fills it with facts before solving a request.
Arguments
out_edbmaelys_datalog_input_edb_t **
Receives the new opaque input handle on success.
Return value
maelys_datalog_status_t

Returns OK with a new buffer; release it with input_edb_free.

Add input facts

Add one fact through the typed call below, or submit a whole request batch when partial insertion would be unsafe. Successful insertion copies predicate and symbol strings, so the caller can release its input strings afterward. Domain membership is checked at solve time.

C · function
Add one input fact
maelys_datalog_status_t
maelys_datalog_input_edb_add_fact(
maelys_datalog_input_edb_t * edb,
const char * predicate,
const maelys_datalog_value_t * terms,
size_t arity,
maelys_datalog_diagnostic_t * out_diagnostic
);
Appends one typed fact to the input buffer and copies its text values into the buffer’s storage. This call does not evaluate the policy.
Arguments
edbmaelys_datalog_input_edb_t *
Input buffer to inspect, update or release.
predicateconst char *
Predicate name of the input fact.
termsconst maelys_datalog_value_t *
Array of typed ground values.
aritysize_t
Number of terms in the fact.
out_diagnosticmaelys_datalog_diagnostic_t *
Optional details when insertion fails.
Return value
maelys_datalog_status_t

Returns OK after copying the fact; on failure that fact is not inserted.

C · functionSince v0.4.0
Add a batch of input facts
maelys_datalog_status_t
maelys_datalog_input_edb_add_facts(
maelys_datalog_input_edb_t * edb,
const maelys_datalog_fact_t * facts,
size_t fact_count,
maelys_datalog_diagnostic_t * out_diagnostic
);
Appends a complete batch of typed facts in one operation. The batch is atomic: if an entry cannot be stored, none of this batch is added.
Arguments
edbmaelys_datalog_input_edb_t *
Opaque input buffer to update.
factsconst maelys_datalog_fact_t *
Array of facts to add as one atomic batch.
fact_countsize_t
Number of facts in the array.
out_diagnosticmaelys_datalog_diagnostic_t *
Optional validation diagnostic when the batch is rejected.
Return value
maelys_datalog_status_t

This call validates the complete batch before storing it; a failed batch adds no facts.

CODE
maelys_datalog_status_t status = maelys_datalog_input_edb_add_facts(
    edb, facts, fact_count, &diagnostic);

Without the macros: typed arrays

Use the typed API for C++, FFI, large batches or an array populated dynamically. Assume edb is initialized, diagnostic is initialized and rc is a status. The examples below are two alternative ways to add input:

CODE
const maelys_datalog_value_t terms[] = {
    {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "alice"},
    {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "roadmap.pdf"},
};
rc = maelys_datalog_input_edb_add_fact(
    edb, "owns", terms, 2u, &diagnostic);
if (rc != MAELYS_DATALOG_STATUS_OK) goto cleanup;
CODE
const maelys_datalog_fact_t facts[] = {
    {.predicate = "user", .arity = 1u, .terms = {
        {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "alice"},
    }},
    {.predicate = "owns", .arity = 2u, .terms = {
        {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "alice"},
        {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "roadmap.pdf"},
    }},
};
rc = maelys_datalog_input_edb_add_facts(
    edb, facts, sizeof(facts) / sizeof(facts[0]), &diagnostic);
if (rc != MAELYS_DATALOG_STATUS_OK) goto cleanup;

Both paths use the same native validation and copying rules. The typed batch function accepts a zero-length batch; the variadic macro requires at least one FACT. A FACT descriptor is not a maelys_datalog_fact_t initializer. These helpers only build input: call maelys_datalog_session_solve_edb() after successful insertion to evaluate the request.

Reuse and release

input_edb_clear() starts another request in the same buffer; it is not a secure erase. At the end of the handle's lifetime, call input_edb_free().

C · function
Clear input entries
maelys_datalog_status_t
maelys_datalog_input_edb_clear(
maelys_datalog_input_edb_t * edb
);
Removes the input entries so the same buffer can hold another request. Clearing input does not release a live result or reset its session.
Arguments
edbmaelys_datalog_input_edb_t *
Input buffer to inspect, update or release.
Return value
maelys_datalog_status_t

Returns OK after resetting the buffer for another complete request; this is not secure erasure.

C · function
Release an input EDB
maelys_datalog_status_t
maelys_datalog_input_edb_free(
maelys_datalog_input_edb_t * edb
);
Releases the input buffer. Results already produced from it remain independent of that input storage.
Arguments
edbmaelys_datalog_input_edb_t *
Input buffer to inspect, update or release.
Return value
maelys_datalog_status_t

Releases the handle; caller-owned storage supplied to init remains the caller's responsibility.

Example — document access

For the policy from the quickstart, build the same seven-fact request: three users, two ownership facts, one delegation and one blocked user. The domain declares blocked/1 as EDB, so the last fact is request data, not policy source. Both tabs submit one atomic batch to an already initialized EDB.

CODEDocument-access request facts

request-facts.c

#include <maelys/datalog.h>

maelys_datalog_status_t add_document_request(
    maelys_datalog_input_edb_t *edb, maelys_datalog_diagnostic_t *diagnostic) {
    const maelys_datalog_fact_t facts[] = {
        {.predicate = "user", .arity = 1u, .terms = {{.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "alice"}}},
        {.predicate = "user", .arity = 1u, .terms = {{.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "bob"}}},
        {.predicate = "user", .arity = 1u, .terms = {{.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "mallory"}}},
        {.predicate = "owns", .arity = 2u, .terms = {
            {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "alice"},
            {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "roadmap.pdf"}}},
        {.predicate = "delegated", .arity = 2u, .terms = {
            {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "bob"},
            {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "roadmap.pdf"}}},
        {.predicate = "owns", .arity = 2u, .terms = {
            {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "mallory"},
            {.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "roadmap.pdf"}}},
        {.predicate = "blocked", .arity = 1u, .terms = {{.kind = MAELYS_DATALOG_VALUE_SYMBOL, .as.symbol = "mallory"}}},
    };
    return maelys_datalog_input_edb_add_facts(
        edb, facts, sizeof(facts) / sizeof(facts[0]), diagnostic);
}

The batch is atomic: none of its seven facts is inserted if validation fails. A later solve reads exactly this input snapshot; it does not inherit facts from a previous request.

Capacity and memory

Use these alternatives to default creation when the application needs explicit fact and text limits or must provide the buffer's memory itself.

Explicit capacities

create_with_capacity() allocates a buffer with the requested fixed limits. Like the default buffer, it never grows automatically.

C · function
Create a bounded input EDB
maelys_datalog_status_t
maelys_datalog_input_edb_create_with_capacity(
size_t fact_capacity,
size_t text_capacity,
maelys_datalog_input_edb_t ** out_edb
);
Creates an empty input buffer with explicit fact and text capacities, fixed for its lifetime.
Arguments
fact_capacitysize_t
Maximum stored raw fact entries.
text_capacitysize_t
Maximum interned predicate and symbol bytes.
out_edbmaelys_datalog_input_edb_t **
Receives the new opaque input handle on success.
Return value
maelys_datalog_status_t

Returns OK with the requested fixed capacities; the buffer never grows.

Caller-owned storage

Call storage_requirements() to obtain the required size and alignment, then pass suitably aligned memory to init().

C · function
Measure caller storage
maelys_datalog_status_t
maelys_datalog_input_edb_storage_requirements(
size_t fact_capacity,
size_t text_capacity,
size_t * out_bytes,
size_t * out_alignment
);
Measures the size and alignment needed to initialize an input buffer in application-provided memory. It does not allocate or initialize that memory.
Arguments
fact_capacitysize_t
Maximum stored raw fact entries.
text_capacitysize_t
Maximum interned predicate and symbol bytes.
out_bytessize_t *
Receives required caller-storage bytes.
out_alignmentsize_t *
Receives required storage alignment.
Return value
maelys_datalog_status_t

Returns bytes and alignment for the requested capacities; no EDB is created.

C · function
Initialize in caller storage
maelys_datalog_status_t
maelys_datalog_input_edb_init(
void * storage,
size_t storage_bytes,
size_t fact_capacity,
size_t text_capacity,
maelys_datalog_input_edb_t ** out_edb
);
Initializes an empty input buffer inside memory supplied by the application, using the requested fact and text capacities.
Arguments
storagevoid *
Caller-owned aligned memory for the handle.
storage_bytessize_t
Available bytes in caller storage.
fact_capacitysize_t
Maximum stored raw fact entries.
text_capacitysize_t
Maximum interned predicate and symbol bytes.
out_edbmaelys_datalog_input_edb_t **
Receives the new opaque input handle on success.
Return value
maelys_datalog_status_t

Returns OK with an EDB handle borrowing the supplied aligned memory.

Both creation paths use the common insertion and solve operations. Finish every initialized handle with input_edb_free(); this call does not free caller-owned storage, which remains the application's responsibility.

Inspection

These accessors inspect one already populated input buffer without solving. They are useful for admission diagnostics and for native adapters such as event windows. They do not change the EDB or allocate memory.

Count the stored entries before inspecting their text occupancy or content. input_edb_count() includes duplicates; it does not count deduplicated facts.

C · function
Count input entries
maelys_datalog_status_t
maelys_datalog_input_edb_count(
const maelys_datalog_input_edb_t * edb,
size_t * out_count
);
Reads the number of entries currently stored in the input buffer, before any solving or result enumeration.
Arguments
edbconst maelys_datalog_input_edb_t *
Input buffer to inspect, update or release.
out_countsize_t *
Receives the number of stored raw entries.
Return value
maelys_datalog_status_t

Returns the number of stored raw entries, including duplicates.

When an input approaches its fixed text budget, inspect only the bytes already committed to this EDB; this is not a prediction of solve memory.

C · functionSince v0.8.0
Observe interned text occupancy
maelys_datalog_status_t
maelys_datalog_input_edb_text_usage(
const maelys_datalog_input_edb_t * edb,
size_t * out_used,
size_t * out_capacity
);
Reads how much of the input buffer’s text storage is occupied by interned strings.
Arguments
edbconst maelys_datalog_input_edb_t *
Input buffer to inspect.
out_usedsize_t *
Interned predicate and symbol bytes used, including terminating NULs.
out_capacitysize_t *
Configured text capacity of this buffer.
Return value
maelys_datalog_status_t

Returns OK and leaves both outputs unchanged on error. This measures committed input text only, not engine or candidate scratch memory.

Use the view to audit the order and content accepted into the buffer before solve. It is borrowed, not a copy.

C · functionSince v0.8.0
Borrow ordered raw input facts
maelys_datalog_status_t
maelys_datalog_input_edb_view(
const maelys_datalog_input_edb_t * edb,
const maelys_datalog_fact_t ** out_facts,
size_t * out_count
);
Borrows a view of the stored input facts in insertion order. The view refers to the input buffer rather than copying its contents.
Arguments
edbconst maelys_datalog_input_edb_t *
Input buffer to inspect.
out_factsconst maelys_datalog_fact_t **
Borrowed array of raw entries, before native deduplication.
out_countsize_t *
Number of entries in the borrowed view.
Return value
maelys_datalog_status_t

Returns OK and leaves outputs unchanged on error. The view lasts until the next successful mutation or free of this EDB.

Do not modify the borrowed array or its strings. Do not pass the view back into a mutating operation on the same buffer; copying to a different buffer is allowed. clear() resets text usage to zero, while a rejected append preserves the reported usage. Free text alone does not guarantee that the next solve fits every other engine limit.

Macro builder reference

C · macro builder
MAELYS_DATALOG_SYMBOL()
MAELYS_DATALOG_SYMBOL(value)

Marks a borrowed C string as a symbol-valued term.

Arguments
valueconst char *
Borrowed NUL-terminated symbol string, valid until the consuming call returns.
Effect

Builds a symbol-value initializer. It neither copies the string nor inserts a fact.

Result

A maelys_datalog_value_t initializer; the consuming call validates and copies when appropriate.

Example
maelys_datalog_value_t user = MAELYS_DATALOG_SYMBOL("alice");
C · macro builder
MAELYS_DATALOG_BOOL()
MAELYS_DATALOG_BOOL(value)

Requests boolean rather than integer conversion for a C expression.

Arguments
valuescalar C expression convertible to _Bool
A value or expression to interpret as a boolean, rather than as an integer.
Effect

Casts the expression to _Bool so the C11 typed builders select BOOLEAN. It inserts no fact.

Result

A _Bool expression. It inserts no fact.

Example
maelys_datalog_status_t rc = MAELYS_DATALOG_ADD_FACT(
    edb, diagnostic, "enabled", MAELYS_DATALOG_BOOL(1));
C · macro builder
MAELYS_DATALOG_FACT()
MAELYS_DATALOG_FACT(...)

Builds one borrowed checked descriptor for an atomic batch.

Arguments
...const char * predicate, then 0–4 supported C term expressions
Predicate name followed by strings, supported integer expressions, or explicit boolean expressions.
Effect

Builds one borrowed, checked fact descriptor for a later atomic batch. It inserts no fact.

Result

A temporary fact descriptor; it inserts nothing until ADD_FACTS is called.

Example
maelys_datalog_status_t rc = MAELYS_DATALOG_ADD_FACTS(
    edb, diagnostic,
    MAELYS_DATALOG_FACT("owns", "alice", "roadmap.pdf"));
C · macro builder
MAELYS_DATALOG_ADD_FACT()
MAELYS_DATALOG_ADD_FACT(edb, diagnostic, ...)

Adds one typed fact through the input EDB API.

Arguments
edbmaelys_datalog_input_edb_t *
Initialized input EDB handle.
diagnosticmaelys_datalog_diagnostic_t *
Initialized diagnostic pointer, or NULL when diagnostics are not needed.
...const char * predicate, then 0–4 supported C term expressions
Predicate name followed by supported typed terms.
Effect

Converts and appends one fact. Separate successful calls remain inserted if a later call fails.

Result

A public status; one failed insertion leaves that fact absent but earlier successful calls remain.

Example
maelys_datalog_status_t rc = MAELYS_DATALOG_ADD_FACT(
    edb, diagnostic, "owns", "alice", "roadmap.pdf");
C · macro builder
MAELYS_DATALOG_ADD_FACTS()
MAELYS_DATALOG_ADD_FACTS(edb, diagnostic, ...)

Adds one batch through the input EDB API.

Arguments
edbmaelys_datalog_input_edb_t *
Initialized input EDB handle.
diagnosticmaelys_datalog_diagnostic_t *
Initialized diagnostic pointer, or NULL when diagnostics are not needed.
...one or more MAELYS_DATALOG_FACT(...) descriptors
A nonempty comma-separated batch of fact descriptors.
Effect

Converts and submits the descriptors in one atomic batch: failure inserts none of its facts.

Result

A public status; a failed batch inserts none of its facts.

Example
maelys_datalog_status_t rc = MAELYS_DATALOG_ADD_FACTS(
    edb, diagnostic,
    MAELYS_DATALOG_FACT("user", "alice"),
    MAELYS_DATALOG_FACT("owns", "alice", "roadmap.pdf"));

Using the C11 builders

The optional macros are declared in <maelys/datalog_builders.h>, also included by <maelys/datalog.h>. They are source conveniences, not a new ABI.

Assume edb is a successfully created or caller-initialized input buffer, diagnostic is a maelys_datalog_diagnostic_t, and rc is a status. One fact can be added directly:

CODE
rc = MAELYS_DATALOG_ADD_FACT(
    edb, &diagnostic, "owns", "alice", "roadmap.pdf");
if (rc != MAELYS_DATALOG_STATUS_OK) goto cleanup;

For a small batch written in the program, use one call:

CODE
rc = MAELYS_DATALOG_ADD_FACTS(
    edb, &diagnostic,
    MAELYS_DATALOG_FACT("user", "alice"),
    MAELYS_DATALOG_FACT("owns", "alice", "roadmap.pdf"),
    MAELYS_DATALOG_FACT("blocked", "mallory")
);
if (rc != MAELYS_DATALOG_STATUS_OK) goto cleanup;
OperationWhat it doesOn failure
FACT(predicate, ...)Constructs a checked descriptor; inserts nothing.Range errors are retained for the enclosing batch call. Unsupported types fail compilation.
ADD_FACT(edb, diagnostic, predicate, ...)Appends one fact.That fact is not added; earlier successful calls remain.
ADD_FACTS(edb, diagnostic, FACT(...), ...)Counts the facts and calls the native batch function once.No fact from this batch is added; existing facts and storage remain unchanged.

The table abbreviates the common MAELYS_DATALOG_ prefix. These macros support C11, zero to four terms per fact, and at least one fact per batch. They accept the same values:

C valueDatalog term
char * or const char *Symbol, copied into the EDB on successful insertion.
Standard signed or unsigned integerInteger, checked against INT64_MIN..INT64_MAX.
_Bool or MAELYS_DATALOG_BOOL(expression)Boolean. Plain C11 true and comparisons are int: wrap them to request boolean semantics.

For example, MAELYS_DATALOG_FACT("enabled", "alice", MAELYS_DATALOG_BOOL(1)) builds a symbol and a boolean; MAELYS_DATALOG_FACT("quota", "alice", 1024) builds a symbol and an integer. Their predicates still need to be declared in the domain before solving. Floats, arbitrary pointers and structs are rejected at compilation.

Each argument is evaluated once, but evaluation order is unspecified: do not put mutually dependent side effects in different arguments. FACT borrows strings until submission; ADD_FACTS copies them into native storage before returning successfully. It uses automatic temporary arrays proportional to the written batch, no extra malloc, and no heap fallback. This says nothing about an allocating EDB constructor or Python allocations.

Range failures and native shape/storage failures are atomic. Diagnostic fact and term indices are zero-based within the submitted batch. Domain membership, declared arities and policy-specific limits are checked at solve time, not by these macros.