Queries and mutations

The example stores the people in a database and queries their names. It collects query pages, updates Ada's age, and reads the changed age.

The optional adaptive query planner learns from runs and compares alternative plans for stored queries. Enable it through the database's plugins open option.

Example

The example compares these results:

  • a committed mutation
  • a repeated mutation
  • a query that matches no entity

Give Ada her birthday and read her age

birthday_text holds this document:

edn82 B
{:query {:find  [?e]
         :where [[?e "name" "Ada"]]}
 :patch {:?e {:age 37}}}

query_text holds this document:

edn66 B
{:find  [?age]
 :where [
   [?e "name" "Ada"]
   [?e "age" ?age]]}
c1.5 kB
stardust_document *birthday = NULL;
stardust_document *definition = NULL;
stardust_db_mutation *mutation = NULL;
stardust_result *committed = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_result *page = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(birthday_text, STARDUST_EDN, &birthday);
if (status == STARDUST_OK) status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source change = {.kind = STARDUST_SOURCE_DOCUMENT, .document = birthday};
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_mutation_open(db, &change, &mutation);
if (status == STARDUST_OK) status = stardust_db_mutation_run(mutation, NULL, NULL, NULL, &committed);
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, NULL, NULL, &rows);
if (status == STARDUST_OK) status = stardust_db_rows_next(rows, &page);
if (status == STARDUST_OK) status = stardust_result_take_document(page, &result);
stardust_result_release(page);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_result_release(committed);
stardust_db_mutation_release(mutation);
stardust_document_release(definition);
stardust_document_release(birthday);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 20 B
{:value [{:age 37}]}

Query pages

Each query page is an owned document.

  • You can keep a page after you release the rows handle, query, or database that produced it.
  • Release each page when you finish with its values. The document ownership rule applies.

Cached joins

The database can keep completed native fact joins as owned binding rows before aggregation. When you run the same query and plan again, the cache can supply those rows at the same database snapshot. Aggregates and output expressions still run for each call.

The cache uses the database working-memory budget and gives memory back when queries need it. The cache discards rows from joins that do not complete. After a write, new queries use a new snapshot. An active reader keeps the rows from its original snapshot.

Parameterized, temporal and overlay queries use their normal execution paths. Queries with predicates, derivations, member clauses or transaction terms also use those paths. Background planner trials and sampled foreground measurements execute joins directly.

Exact indexes

A query or mutation definition that compares a field with a value makes the database build that field's exact index. The comparison is a fact clause with a constant or a parameter value, or a =, <, <=, >, >= or hasPrefix predicate on a fact clause's value. With the index, a hasPrefix predicate reads only the texts that start with the prefix, and no value of another type.

  • Storing the definition starts the build. An inline query or mutation that compares a field the same way also starts one.
  • The database builds the index in the background, including the values committed before the definition. The call that started it does not wait.
  • Queries scan the field until its index is ready. The results are the same; only the time differs.
  • Use indexes_sync to wait until every index that earlier definitions and inline runs ask for is ready, for example before you measure a query.
  • An index that no definition uses any more stays until the database next opens.

Operations

OperationReturnsSummary
stardust_db_read_entityStatus. document through outputdatabase.read_entity(entity, max_depth, max_entities) atomically returns an owned {document links revisions truncated} document, expanding links up to max_depth hops.
stardust_db_query_openStatus. query through outputOpen an owned query from a path or an inline definition.
stardust_db_mutation_openStatus. mutation through outputOpen an owned mutation from a path or an inline definition.
stardust_db_query_runStatus. rows through outputRun the query and return an owned row cursor.
stardust_db_query_releasevoidRelease the query handle.
stardust_db_rows_nextStatus. document through outputRead the next page as an owned document.
pagesUnavailablerows.pages() iterates the non-empty pages of the cursor.
all_pagesUnavailablerows.all_pages() reads every remaining page into one owned array document.
stardust_db_rows_releasevoidRelease the rows handle.
stardust_db_query_subscribeStatus. subscription through outputquery.subscribe(parameters, options) opens an owned live subscription.
stardust_db_subscription_nextStatus. event through outputWait for the next live event.
eventsUnavailablesubscription.events() delivers events in order until the subscription is cancelled, closes or fails.
stardust_db_subscription_cancelvoidsubscription.cancel() ends the subscription.
stardust_db_subscription_releasevoidRelease the subscription handle.
stardust_db_mutation_runStatus. commit through outputRun the mutation and return commit information.
stardust_db_mutation_releasevoidRelease the mutation handle.
stardust_db_merge_patchStatus. commit through outputdatabase.merge_patch(patch) applies an entity-keyed patch document in one transaction and returns commit information.
stardust_db_indexes_syncStatus. none through outputdatabase.indexes_sync(max_wait) waits until the database built every exact index that earlier definition commits and inline runs ask for.
stardust_db_bulk_beginStatus. none through outputdatabase.bulk_begin() starts a bulk build of data merge patches held by the database.
stardust_db_bulk_addStatus. none through outputdatabase.bulk_add(patch) copies and queues one merge-patch document in the bulk build.
stardust_db_bulk_finishStatus. document through outputdatabase.bulk_finish() publishes the bulk build with one commit, ends it and returns an owned summary document.
stardust_db_bulk_abortvoiddatabase.bulk_abort() drops the bulk build in progress without writing it.
stardust_db_subscription_set_notifystatusWake a host scheduler when a subscription may be ready.

stardust_db_read_entity

c133 B
uint32_t stardust_db_read_entity(stardust_db *db, int64_t entity, int64_t max_depth, int64_t max_entities, stardust_result **output);
c92 B
uint32_t stardust_result_take_document(stardust_result *result, stardust_document **output);
c96 B
uint32_t stardust_result_error(const stardust_result *result, stardust_diagnostic_view *output);
c54 B
void stardust_result_release(stardust_result *result);

Parameters

stardust_db_read_entity

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleRequiredKeep the handle alive until the call returns.
entityint64_tValueRequiredThe decimal user entity ID.
max_depthint64_tValueRequiredLink hops to expand along each path, 0 through 32. 0, the default, leaves every link a {#link id} reference.
max_entitiesint64_tValueRequiredThe most entity documents the result holds, counting the root and every expanded or absent target each time it is placed. 0 selects the default of 1000.
outputstardust_result **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_result_take_document

ParameterTypeOwnershipDefaultMeaning
resultstardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_document **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_result_error

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_result_release

ParameterTypeOwnershipDefaultMeaning
resultstardust_result *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Errors

StatusWhen
STARDUST_INVALID_ARGUMENTthe entity ID is outside the user entity range, max_depth is negative or above 32, or max_entities is negative
STARDUST_CLOSEDthe database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

revisions maps each placed entity's decimal ID to its last-touch transaction for #_tx.stardust/checkAndSet. Empty object and array fields are omitted recursively. See entity reads for result fields, JSON Pointer links and expansion rules.

Examples

Read Bob with his mentor expanded

c330 B
stardust_document *result = NULL;
stardust_result *outcome = NULL;
uint32_t status = stardust_db_read_entity(db, 2, 1, 0, &outcome);
if (status == STARDUST_OK) status = stardust_result_take_document(outcome, &result);
stardust_result_release(outcome);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 360 B
{:value {:document  {:age    41
                     :mentor {:age  36
                              :name "Ada"
                              :role "engineer"}
                     :name   "Bob"
                     :role   "engineer"}
         :links     {"/mentor" {:#link 1}}
         :revisions {:1 4
                     :2 4}
         :truncated false}}

Also calls: stardust_result_take_document, stardust_result_error, stardust_result_release

stardust_db_query_open

c111 B
uint32_t stardust_db_query_open(stardust_db *db, const stardust_db_source *source, stardust_db_query **output);
c100 B
uint32_t stardust_db_query_error(const stardust_db_query *handle, stardust_diagnostic_view *output);

Parameters

stardust_db_query_open

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleRequiredKeep the handle alive until the call returns.
sourceconst stardust_db_source *Borrowed inputRequiredAn inline native definition or a stored definition path.
outputstardust_db_query **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_db_query_error

ParameterTypeOwnershipDefaultMeaning
handleconst stardust_db_query *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

Errors

StatusWhen
STARDUST_NOT_FOUNDno definition is stored at the path
STARDUST_INVALID_DEFINITIONthe definition is invalid
STARDUST_CLOSEDthe database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted
STARDUST_INVALID_ARGUMENTa required input or output pointer is NULL, or an input owner is not ready

Notes

Inline definitions are copied. Stored paths select their definition at run time.

Query documents.

Examples

Open an inline query and read its first page

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c867 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_result *page = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, NULL, NULL, &rows);
if (status == STARDUST_OK) status = stardust_db_rows_next(rows, &page);
if (status == STARDUST_OK) status = stardust_result_take_document(page, &result);
stardust_result_release(page);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

Refuse an invalid query definition

definition_text holds this document:

edn12 B
{:find [?x]}
c495 B
stardust_document *definition = NULL;
uint32_t status = stardust_parse(definition_text, STARDUST_EDN, &definition);
if (status != STARDUST_OK) {stardust_document_release(definition); return status;}
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
stardust_db_query *failed = NULL;
status = stardust_db_query_open(db, &source, &failed);
stardust_document_release(definition);
stardust_diagnostic_view result = {0};
stardust_db_query_error(failed, &result);
Result / 202 B
{:value {:code    "unbound_variable"
         :message "Unbound_Variable"
         :path    nil
         :field   nil
         :entity  nil
         :offset  nil
         :status  "invalid_definition"}}

Report a missing stored query

c287 B
stardust_db_source source = {.kind = STARDUST_SOURCE_PATH, .path = {(const uint8_t *)"people/missing", 14}};
stardust_db_query *failed = NULL;
uint32_t status = stardust_db_query_open(db, &source, &failed);
stardust_diagnostic_view result = {0};
stardust_db_query_error(failed, &result);
Result / 209 B
{:value {:code    "not_found"
         :message "no definition at this path"
         :path    "people/missing"
         :field   nil
         :entity  nil
         :offset  nil
         :status  "not_found"}}

Also calls: stardust_db_query_error

stardust_db_mutation_open

c117 B
uint32_t stardust_db_mutation_open(stardust_db *db, const stardust_db_source *source, stardust_db_mutation **output);
c106 B
uint32_t stardust_db_mutation_error(const stardust_db_mutation *handle, stardust_diagnostic_view *output);

Parameters

stardust_db_mutation_open

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleRequiredKeep the handle alive until the call returns.
sourceconst stardust_db_source *Borrowed inputRequiredAn inline native definition or a stored definition path.
outputstardust_db_mutation **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_db_mutation_error

ParameterTypeOwnershipDefaultMeaning
handleconst stardust_db_mutation *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

Errors

StatusWhen
STARDUST_NOT_FOUNDno definition is stored at the path
STARDUST_INVALID_DEFINITIONthe definition is invalid
STARDUST_CLOSEDthe database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted
STARDUST_INVALID_ARGUMENTa required input or output pointer is NULL, or an input owner is not ready

Notes

Inline definitions are copied. Stored paths select their definition at run time.

Mutation documents.

Examples

database/mutation

birthday_text holds this document:

edn82 B
{:query {:find  [?e]
         :where [[?e "name" "Ada"]]}
 :patch {:?e {:age 37}}}
c614 B
stardust_document *definition = NULL;
stardust_db_mutation *mutation = NULL;
stardust_result *result = NULL;
uint32_t status = stardust_parse(birthday_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_mutation_open(db, &source, &mutation);
if (status == STARDUST_OK) status = stardust_db_mutation_run(mutation, NULL, NULL, NULL, &result);
stardust_db_mutation_release(mutation);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 153 B
{:value {:status       "committed"
         :transaction  5
         :facts        1
         :committed_at 17923367930930079
         :ids          {}}}

Also calls: stardust_db_mutation_error

stardust_db_query_run

c206 B
uint32_t stardust_db_query_run(stardust_db_query *query_handle, const stardust_document *parameters, const stardust_db_query_options *options, stardust_db_query_explain *explain, stardust_db_rows **output);
c96 B
uint32_t stardust_db_rows_error(const stardust_db_rows *rows, stardust_diagnostic_view *output);

Parameters

stardust_db_query_run

ParameterTypeOwnershipDefaultMeaning
query_handlestardust_db_query *Borrowed handleRequiredKeep the handle alive until the call returns.
parametersconst stardust_document *Borrowed handleNULLAn optional object of parameter values that merge-patches the definition's declared parameters. A parameter matched in the entity position of a fact clause takes an entity reference {#link id}, never the bare ID number: a commit returns IDs as numbers, so wrap one as {#link id}, or as the typed Link value where the binding has one, before passing it. A query run fails with entity_reference_expected when a bare number reaches that position. Projected and found entities also read back as {#link id}, so a typed field that receives one uses Link rather than a plain integer.
optionsconst stardust_db_query_options *Borrowed inputNULLThe operation's options record. See its field defaults.
explainstardust_db_query_explain *Caller-owned outputNULLReceives a record. Its pointer fields follow the record's ownership contract.
outputstardust_db_rows **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_db_rows_error

ParameterTypeOwnershipDefaultMeaning
rowsconst stardust_db_rows *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

Errors

StatusWhen
STARDUST_NOT_FOUNDthe stored query definition was deleted
STARDUST_INVALID_DEFINITIONthe loaded query definition is invalid
STARDUST_INVALID_ARGUMENTthe parameters or run options are invalid
STARDUST_EVALUATION_FAILEDthe query fails or exceeds max_cost
STARDUST_CLOSEDthe parent database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

Query documents.

Examples

Open an inline query and read its first page

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c867 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_result *page = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, NULL, NULL, &rows);
if (status == STARDUST_OK) status = stardust_db_rows_next(rows, &page);
if (status == STARDUST_OK) status = stardust_result_take_document(page, &result);
stardust_result_release(page);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

Capture the host duration boundary

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}

boundary_text holds this document:

edn19 B
9223372036854775807
c1.1 kB
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_document *boundary = NULL;
uint32_t boundary_root = 0;
int64_t ticks = 0;
uint32_t status = stardust_parse(boundary_text, STARDUST_EDN, &boundary);
if (status == STARDUST_OK) status = stardust_document_root(boundary, &boundary_root);
if (status == STARDUST_OK) status = stardust_document_i64(boundary, boundary_root, &ticks);
stardust_document_release(boundary);
if (status == STARDUST_OK) status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
stardust_db_query_options result = {.size=sizeof(result), .version=1, .page_size=2, .max_cost_ticks=ticks};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, &result, NULL, &rows);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) return status;
Result / 85 B
{:value {:page_size 2
         :max_cost  9223372036854775807
         :as_of     0}}

The C ABI takes complete 100-ns ticks

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c531 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
stardust_db_query_options result = {.size=sizeof(result), .version=1, .page_size=2, .max_cost_ticks=1};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) return status;
Result / 67 B
{:value {:page_size 2
         :max_cost  1
         :as_of     0}}

Explain query execution

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c670 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_db_query_explain explain = {0};
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, NULL, &explain, &rows);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) return status;
stardust_db_query_explain result = explain;
Result / 1.1 kB
{:value {:load_definition   {:cpu         1326
                             :wall        1370
                             :arena_bytes 1680}
         :parameters        {:cpu         7
                             :wall        9
                             :arena_bytes 0}
         :run               {:cpu         9090
                             :wall        9090
                             :arena_bytes 7424}
         :total             {:cpu         10449
                             :wall        10495
                             :arena_bytes 9104}
         :arena_capacity    4194304
         :work              6082
         :rows              3
         :compile           301
         :plan              459
         :execute           0
         :group             0
         :order             0
         :memory_bytes      5995
         :memory_refusals   0
         :budget_bytes      10489856512
         :budget_used_bytes 10194
         :budget_peak_bytes 14267
         :budget_refusals   0
         :retry             0
         :stages_completed  3
         :retries           0}}

A negative configured duration is not an absent field

c90 B
stardust_db_query_options result = {.size=sizeof(result), .version=1, .max_cost_ticks=-1};
Result / 68 B
{:value {:page_size 0
         :max_cost  -1
         :as_of     0}}

Run with typed query options

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c712 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
stardust_db_query_options result = {.size=sizeof(result), .version=1, .page_size=2, .max_cost_ticks=STARDUST_TICKS_PER_SECOND};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, &result, NULL, &rows);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) return status;
Result / 74 B
{:value {:page_size 2
         :max_cost  10000000
         :as_of     0}}

Also calls: stardust_db_rows_error

stardust_db_query_release

c64 B
void stardust_db_query_release(stardust_db_query *query_handle);

Consumes the receiver handle.

Parameters

ParameterTypeOwnershipDefaultMeaning
query_handlestardust_db_query *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Examples

Open an inline query and read its first page

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c867 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_result *page = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, NULL, NULL, &rows);
if (status == STARDUST_OK) status = stardust_db_rows_next(rows, &page);
if (status == STARDUST_OK) status = stardust_result_take_document(page, &result);
stardust_result_release(page);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

stardust_db_rows_next

c79 B
uint32_t stardust_db_rows_next(stardust_db_rows *rows, stardust_result **page);
c92 B
uint32_t stardust_result_take_document(stardust_result *result, stardust_document **output);
c96 B
uint32_t stardust_result_error(const stardust_result *result, stardust_diagnostic_view *output);
c54 B
void stardust_result_release(stardust_result *result);

Parameters

stardust_db_rows_next

ParameterTypeOwnershipDefaultMeaning
rowsstardust_db_rows *Borrowed handleRequiredKeep the handle alive until the call returns.
pagestardust_result **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_result_take_document

ParameterTypeOwnershipDefaultMeaning
resultstardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_document **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_result_error

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_result_release

ParameterTypeOwnershipDefaultMeaning
resultstardust_result *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Errors

StatusWhen
STARDUST_EVALUATION_FAILEDthe page does not render
STARDUST_CLOSEDthe parent database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted
STARDUST_INVALID_ARGUMENTa required input or output pointer is NULL, or an input owner is not ready

Notes

An empty array ends the cursor. Every page reads the snapshot selected by the run. Returned pages survive cursor and database closure.

Examples

Open an inline query and read its first page

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c867 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_result *page = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, NULL, NULL, &rows);
if (status == STARDUST_OK) status = stardust_db_rows_next(rows, &page);
if (status == STARDUST_OK) status = stardust_result_take_document(page, &result);
stardust_result_release(page);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

Also calls: stardust_result_take_document, stardust_result_error, stardust_result_release

pages

Call stardust_db_rows_next until a page root has no first child.

Also calls: stardust_db_rows_next, stardust_result_take_document, stardust_result_error, stardust_result_release, stardust_document_root, stardust_document_read_many, stardust_document_release

all_pages

Copy each page's rows into one stardust_document_builder_add_many array.

Also calls: stardust_db_rows_next, stardust_result_take_document, stardust_result_error, stardust_result_release, stardust_document_count, stardust_document_read_many, stardust_document_i64, stardust_document_f64, stardust_document_release, stardust_document_builder_create, stardust_document_builder_add_many, stardust_document_builder_seal, stardust_document_builder_release

stardust_db_rows_release

c54 B
void stardust_db_rows_release(stardust_db_rows *rows);

Consumes the receiver handle.

Parameters

ParameterTypeOwnershipDefaultMeaning
rowsstardust_db_rows *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Examples

Open an inline query and read its first page

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c867 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_rows *rows = NULL;
stardust_result *page = NULL;
stardust_document *result = NULL;
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_run(query, NULL, NULL, NULL, &rows);
if (status == STARDUST_OK) status = stardust_db_rows_next(rows, &page);
if (status == STARDUST_OK) status = stardust_result_take_document(page, &result);
stardust_result_release(page);
stardust_db_rows_release(rows);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

stardust_db_query_subscribe

c184 B
uint32_t stardust_db_query_subscribe(stardust_db_query *query_handle, const stardust_document *parameters, const stardust_db_query_options *options, stardust_db_subscription **output);
c111 B
uint32_t stardust_db_subscription_error(const stardust_db_subscription *sub, stardust_diagnostic_view *output);

Parameters

stardust_db_query_subscribe

ParameterTypeOwnershipDefaultMeaning
query_handlestardust_db_query *Borrowed handleRequiredKeep the handle alive until the call returns.
parametersconst stardust_document *Borrowed handleNULLAn optional object of parameter values that merge-patches the definition's declared parameters. A parameter matched in the entity position of a fact clause takes an entity reference {#link id}, never the bare ID number: a commit returns IDs as numbers, so wrap one as {#link id}, or as the typed Link value where the binding has one, before passing it. A query run fails with entity_reference_expected when a bare number reaches that position. Projected and found entities also read back as {#link id}, so a typed field that receives one uses Link rather than a plain integer.
optionsconst stardust_db_query_options *Borrowed inputNULLThe operation's options record. See its field defaults.
outputstardust_db_subscription **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_db_subscription_error

ParameterTypeOwnershipDefaultMeaning
subconst stardust_db_subscription *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

Errors

StatusWhen
STARDUST_INVALID_DEFINITIONthe query or its callable definitions are invalid
STARDUST_INVALID_ARGUMENTthe parameters or subscription options are invalid
STARDUST_CLOSEDthe parent database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

The subscription copies its definition and parameters. Later definition edits do not change it. The page_size option is ignored.

Blocking next works without registration. To wake your scheduler, see Readiness callbacks for registration, waiting and context lifetime.

Query documents.

Examples

query/subscribe

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c834 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_subscription *subscription = NULL;
stardust_db_live_event event = {0};
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_subscribe(query, NULL, NULL, &subscription);
if (status == STARDUST_OK) status = stardust_db_subscription_next(subscription, 0, &event);
stardust_db_subscription_release(subscription);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(event.result); return status;}
stardust_document *result = event.result;
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

Read the complete initial subscription event

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c831 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_subscription *subscription = NULL;
stardust_db_live_event event = {0};
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_subscribe(query, NULL, NULL, &subscription);
if (status == STARDUST_OK) status = stardust_db_subscription_next(subscription, 0, &event);
stardust_db_subscription_release(subscription);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(event.result); return status;}
stardust_db_live_event result = event;
Result / 138 B
{:value {:sequence    1
         :superseded  0
         :transaction 4
         :result      [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}}

Also calls: stardust_db_subscription_error

stardust_db_subscription_next

c125 B
uint32_t stardust_db_subscription_next(stardust_db_subscription *sub, int64_t max_wait_ticks, stardust_db_live_event *event);
c111 B
uint32_t stardust_db_subscription_error(const stardust_db_subscription *sub, stardust_diagnostic_view *output);
c55 B
void stardust_document_release(stardust_document *doc);

Parameters

stardust_db_subscription_next

ParameterTypeOwnershipDefaultMeaning
substardust_db_subscription *Borrowed handleRequiredKeep the handle alive until the call returns.
max_wait_ticksint64_tValueRequiredValue passed to the call.
eventstardust_db_live_event *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_db_subscription_error

ParameterTypeOwnershipDefaultMeaning
subconst stardust_db_subscription *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_document_release

ParameterTypeOwnershipDefaultMeaning
docstardust_document *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Errors

StatusWhen
STARDUST_INVALID_ARGUMENTmax_wait is negative
STARDUST_CANCELLEDthe subscription is cancelled or closed
STARDUST_EVALUATION_FAILEDa rerun of the query failed. Every later call reports this failure
STARDUST_CLOSEDthe parent database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

Each event replaces the entire result. superseded counts skipped computed results, not commits. Cancel, close and database close wake blocked waits. Windows rounds deadlines to milliseconds.

Examples

subscription/next

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c834 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_subscription *subscription = NULL;
stardust_db_live_event event = {0};
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_subscribe(query, NULL, NULL, &subscription);
if (status == STARDUST_OK) status = stardust_db_subscription_next(subscription, 0, &event);
stardust_db_subscription_release(subscription);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(event.result); return status;}
stardust_document *result = event.result;
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

Also calls: stardust_db_subscription_error, stardust_document_release

events

Call stardust_db_subscription_next repeatedly.

Also calls: stardust_db_subscription_next, stardust_db_subscription_error

stardust_db_subscription_cancel

c68 B
void stardust_db_subscription_cancel(stardust_db_subscription *sub);

Parameters

ParameterTypeOwnershipDefaultMeaning
substardust_db_subscription *Borrowed handleNULLKeep the handle alive until the call returns.

Notes

Repeated cancellation is safe. Close the subscription separately to release its handle.

Examples

subscription/cancel

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c881 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_subscription *subscription = NULL;
stardust_db_live_event event = {0};
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_subscribe(query, NULL, NULL, &subscription);
if (status == STARDUST_OK) status = stardust_db_subscription_next(subscription, 0, &event);
stardust_db_subscription_cancel(subscription);
stardust_db_subscription_release(subscription);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(event.result); return status;}
stardust_document *result = event.result;
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

stardust_db_subscription_release

c69 B
void stardust_db_subscription_release(stardust_db_subscription *sub);

Consumes the receiver handle.

Parameters

ParameterTypeOwnershipDefaultMeaning
substardust_db_subscription *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Examples

subscription/close

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c834 B
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_subscription *subscription = NULL;
stardust_db_live_event event = {0};
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_subscribe(query, NULL, NULL, &subscription);
if (status == STARDUST_OK) status = stardust_db_subscription_next(subscription, 0, &event);
stardust_db_subscription_release(subscription);
stardust_db_query_release(query);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_document_release(event.result); return status;}
stardust_document *result = event.result;
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

stardust_db_mutation_run

c210 B
uint32_t stardust_db_mutation_run(stardust_db_mutation *mutation, const stardust_document *parameters, const stardust_db_write_options *options, stardust_db_mutation_explain *explain, stardust_result **output);
c102 B
uint32_t stardust_db_result_commit_info(const stardust_result *result, stardust_db_commit_info *info);
c96 B
uint32_t stardust_result_error(const stardust_result *result, stardust_diagnostic_view *output);
c54 B
void stardust_result_release(stardust_result *result);

Parameters

stardust_db_mutation_run

ParameterTypeOwnershipDefaultMeaning
mutationstardust_db_mutation *Borrowed handleRequiredKeep the handle alive until the call returns.
parametersconst stardust_document *Borrowed handleNULLAn optional object of parameter values that merge-patches the definition's declared parameters. A parameter matched in the entity position of a fact clause takes an entity reference {#link id}, never the bare ID number: a commit returns IDs as numbers, so wrap one as {#link id}, or as the typed Link value where the binding has one, before passing it. A query run fails with entity_reference_expected when a bare number reaches that position. Projected and found entities also read back as {#link id}, so a typed field that receives one uses Link rather than a plain integer.
optionsconst stardust_db_write_options *Borrowed inputNULLThe operation's options record. See its field defaults.
explainstardust_db_mutation_explain *Caller-owned outputNULLReceives a record. Its pointer fields follow the record's ownership contract.
outputstardust_result **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_db_result_commit_info

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
infostardust_db_commit_info *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_result_error

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_result_release

ParameterTypeOwnershipDefaultMeaning
resultstardust_result *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Errors

StatusWhen
STARDUST_INVALID_DEFINITIONthe mutation selection or definition is invalid
STARDUST_INVALID_ARGUMENTthe parameters or run options are invalid
STARDUST_SCHEMA_VIOLATIONthe write breaks a schema
STARDUST_CONFLICTconcurrent writes prevent the commit
STARDUST_REJECTEDa mutation target or write guard refuses the write
STARDUST_UNAVAILABLEthe patch sets stardust/history and no enabled plugin purges history
STARDUST_EVALUATION_FAILEDthe mutation fails
STARDUST_CLOSEDthe parent database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

Mutation documents.

Examples

mutation/run

birthday_text holds this document:

edn82 B
{:query {:find  [?e]
         :where [[?e "name" "Ada"]]}
 :patch {:?e {:age 37}}}
c614 B
stardust_document *definition = NULL;
stardust_db_mutation *mutation = NULL;
stardust_result *result = NULL;
uint32_t status = stardust_parse(birthday_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_mutation_open(db, &source, &mutation);
if (status == STARDUST_OK) status = stardust_db_mutation_run(mutation, NULL, NULL, NULL, &result);
stardust_db_mutation_release(mutation);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 153 B
{:value {:status       "committed"
         :transaction  5
         :facts        1
         :committed_at 17923367935718929
         :ids          {}}}

Explain mutation execution

birthday_text holds this document:

edn82 B
{:query {:find  [?e]
         :where [[?e "name" "Ada"]]}
 :patch {:?e {:age 37}}}
c710 B
stardust_document *definition = NULL;
stardust_db_mutation *mutation = NULL;
stardust_result *written = NULL;
stardust_db_mutation_explain explain = {0};
uint32_t status = stardust_parse(birthday_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_mutation_open(db, &source, &mutation);
if (status == STARDUST_OK) status = stardust_db_mutation_run(mutation, NULL, NULL, &explain, &written);
stardust_result_release(written);
stardust_db_mutation_release(mutation);
stardust_document_release(definition);
if (status != STARDUST_OK) return status;
stardust_db_mutation_explain result = explain;
Result / 647 B
{:value {:load_definition  {:cpu         1150
                            :wall        1157
                            :arena_bytes 2320}
         :parameters       {:cpu         10
                            :wall        11
                            :arena_bytes 0}
         :run              {:cpu         7174
                            :wall        35893
                            :arena_bytes 43856}
         :total            {:cpu         8427
                            :wall        37161
                            :arena_bytes 46176}
         :arena_capacity   4194304
         :stages_completed 3
         :attempts         1}}

Also calls: stardust_db_result_commit_info, stardust_result_error, stardust_result_release

stardust_db_mutation_release

c66 B
void stardust_db_mutation_release(stardust_db_mutation *mutation);

Consumes the receiver handle.

Parameters

ParameterTypeOwnershipDefaultMeaning
mutationstardust_db_mutation *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Examples

mutation/close

birthday_text holds this document:

edn82 B
{:query {:find  [?e]
         :where [[?e "name" "Ada"]]}
 :patch {:?e {:age 37}}}
c614 B
stardust_document *definition = NULL;
stardust_db_mutation *mutation = NULL;
stardust_result *result = NULL;
uint32_t status = stardust_parse(birthday_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_mutation_open(db, &source, &mutation);
if (status == STARDUST_OK) status = stardust_db_mutation_run(mutation, NULL, NULL, NULL, &result);
stardust_db_mutation_release(mutation);
stardust_document_release(definition);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 153 B
{:value {:status       "committed"
         :transaction  5
         :facts        1
         :committed_at 17923367934918455
         :ids          {}}}

stardust_db_merge_patch

c186 B
uint32_t stardust_db_merge_patch(stardust_db *db, const stardust_document *patch, const stardust_db_write_options *options, stardust_db_patch_explain *explain, stardust_result **output);
c102 B
uint32_t stardust_db_result_commit_info(const stardust_result *result, stardust_db_commit_info *info);
c96 B
uint32_t stardust_result_error(const stardust_result *result, stardust_diagnostic_view *output);
c54 B
void stardust_result_release(stardust_result *result);

Parameters

stardust_db_merge_patch

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleRequiredKeep the handle alive until the call returns.
patchconst stardust_document *Borrowed handleRequiredAn entity-keyed native patch. Temporary names use #_name. #_tx holds metadata and an optional stardust/checkAndSet map from entity IDs to expected transactions. Positive expectations match exact last-touch transactions; zero requires no current facts. Conditions are consumed and checked atomically, including for no-ops.
optionsconst stardust_db_write_options *Borrowed inputNULLCaller-owned record read during the call.
explainstardust_db_patch_explain *Caller-owned outputNULLReceives a record. Its pointer fields follow the record's ownership contract.
outputstardust_result **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_db_result_commit_info

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
infostardust_db_commit_info *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_result_error

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_result_release

ParameterTypeOwnershipDefaultMeaning
resultstardust_result *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Errors

StatusWhen
STARDUST_INVALID_COMMIT_TIMEthe #_tx commit time is not later than the last commit
STARDUST_CONFLICTan entity revision check fails or a concurrent writer prevents the commit; a revision mismatch reports the entity and expected and actual revisions
STARDUST_REJECTEDthe database refuses the patch; the code and diagnostic path name the reason and key
STARDUST_UNAVAILABLEthe patch sets stardust/history and no enabled plugin purges history
STARDUST_INVALID_ARGUMENTthe patch is not an entity-keyed object, or a link names an ID outside the user entity range
STARDUST_CLOSEDthe database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

Patch documents.

Examples

database/merge_patch

seed_text holds this document:

edn287 B
{:#_ada {:name "Ada"
         :role "engineer"
         :age  36}
 :#_bob {:name   "Bob"
         :role   "engineer"
         :age    41
         :mentor {:#link "#_ada"}}
 :#_cy  {:name "Cy"
         :role "designer"
         :age  29}
 :#_tx  {:author "smoke"
         :reason "seed"}}
c335 B
stardust_document *patch = NULL;
stardust_result *result = NULL;
uint32_t status = stardust_parse(seed_text, STARDUST_EDN, &patch);
if (status == STARDUST_OK) status = stardust_db_merge_patch(db, patch, NULL, NULL, &result);
stardust_document_release(patch);
if (status != STARDUST_OK) {stardust_result_release(result); return status;}
Result / 228 B
{:value {:status       "committed"
         :transaction  4
         :facts        12
         :committed_at 17923367928402685
         :ids          {:#_ada 1
                        :#_bob 2
                        :#_cy  3}}}

Explain patch validation and commit

seed_text holds this document:

edn287 B
{:#_ada {:name "Ada"
         :role "engineer"
         :age  36}
 :#_bob {:name   "Bob"
         :role   "engineer"
         :age    41
         :mentor {:#link "#_ada"}}
 :#_cy  {:name "Cy"
         :role "designer"
         :age  29}
 :#_tx  {:author "smoke"
         :reason "seed"}}
c425 B
stardust_document *patch = NULL;
stardust_db_patch_explain explain = {0};
stardust_result *written = NULL;
uint32_t status = stardust_parse(seed_text, STARDUST_EDN, &patch);
if (status == STARDUST_OK) status = stardust_db_merge_patch(db, patch, NULL, &explain, &written);
stardust_result_release(written);
stardust_document_release(patch);
if (status != STARDUST_OK) return status;
stardust_db_patch_explain result = explain;
Result / 484 B
{:value {:copy_patch       {:cpu         1435
                            :wall        1438
                            :arena_bytes 3096}
         :commit           {:cpu         317
                            :wall        28296
                            :arena_bytes 8008}
         :total            {:cpu         1866
                            :wall        29849
                            :arena_bytes 11118}
         :arena_capacity   4194304
         :stages_completed 2}}

Refuse a metadata-only transaction

patch_text holds this document:

edn21 B
{:#_tx {:author "x"}}
c389 B
stardust_document *patch = NULL;
stardust_result *failed = NULL;
uint32_t status = stardust_parse(patch_text, STARDUST_EDN, &patch);
if (status != STARDUST_OK) {stardust_document_release(patch); return status;}
status = stardust_db_merge_patch(db, patch, NULL, NULL, &failed);
stardust_document_release(patch);
stardust_diagnostic_view result = {0};
stardust_result_error(failed, &result);
Result / 234 B
{:value {:code    "transaction_only"
         :message "a patch of only #_tx is refused: #_tx annotates a write"
         :path    "#_tx"
         :field   nil
         :entity  nil
         :offset  nil
         :status  "rejected"}}

Refuse a repeated commit time

future_text holds this document:

edn74 B
{:#_d  {:x 1}
 :#_tx {:stardust/committedAt #inst "2200-01-01T00:00:00Z"}}

patch_text holds this document:

edn74 B
{:#_e  {:x 2}
 :#_tx {:stardust/committedAt #inst "2200-01-01T00:00:00Z"}}
c714 B
stardust_document *future = NULL;
stardust_document *patch = NULL;
stardust_result *committed = NULL;
uint32_t status = stardust_parse(future_text, STARDUST_EDN, &future);
if (status == STARDUST_OK) status = stardust_parse(patch_text, STARDUST_EDN, &patch);
if (status == STARDUST_OK) status = stardust_db_merge_patch(db, future, NULL, NULL, &committed);
stardust_result_release(committed);
stardust_document_release(future);
if (status != STARDUST_OK) {stardust_document_release(patch); return status;}
stardust_result *failed = NULL;
status = stardust_db_merge_patch(db, patch, NULL, NULL, &failed);
stardust_document_release(patch);
stardust_diagnostic_view result = {0};
stardust_result_error(failed, &result);
Result / 261 B
{:value {:code    "invalid_commit_time"
         :message "stardust/committedAt must be strictly newer than the latest commit time"
         :path    nil
         :field   nil
         :entity  nil
         :offset  nil
         :status  "invalid_commit_time"}}

Also calls: stardust_db_result_commit_info, stardust_result_error, stardust_result_release

stardust_db_indexes_sync

c75 B
uint32_t stardust_db_indexes_sync(stardust_db *db, int64_t max_wait_ticks);

Parameters

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleRequiredKeep the handle alive until the call returns.
max_wait_ticksint64_tValueRequiredValue passed to the call.

Errors

StatusWhen
STARDUST_INVALID_ARGUMENTmax_wait is negative
STARDUST_TIMEOUTmax_wait passed first
STARDUST_CLOSEDthe database is closed

Notes

An index that cannot be built does not hold this wait.

Examples

Wait for the exact indexes of the stored queries

c542 B
stardust_result *listed = NULL;
stardust_bytes kind = {(const uint8_t *)"queries", 7};
stardust_bytes pattern = {(const uint8_t *)"people/.*", 9};
uint32_t status = stardust_db_indexes_sync(db, 50000000);
if (status == STARDUST_OK) status = stardust_db_definition_list(db, kind, pattern, &listed);
if (status != STARDUST_OK) { stardust_result_release(listed); return status; }
stardust_document *result = NULL;
status = stardust_result_take_document(listed, &result);
stardust_result_release(listed);
if (status != STARDUST_OK) return status;
Result / 27 B
{:value ["people/by_role"]}

stardust_db_bulk_begin

c49 B
uint32_t stardust_db_bulk_begin(stardust_db *db);

Parameters

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleRequiredKeep the handle alive until the call returns.

Errors

StatusWhen
STARDUST_INVALID_ARGUMENTa bulk build is already in progress on the database
STARDUST_CLOSEDthe database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

On a database with existing facts, each added patch commits immediately and the summary reports fallback true. Closing the database drops an unfinished build.

Examples

Start a bulk build and drop it

c282 B
uint32_t status = stardust_db_bulk_begin(db);
if (status != STARDUST_OK) return status;
// A second build on the same database is refused while this one runs.
if (stardust_db_bulk_begin(db) != STARDUST_INVALID_ARGUMENT) return STARDUST_EVALUATION_FAILED;
stardust_db_bulk_abort(db);

stardust_db_bulk_add

c79 B
uint32_t stardust_db_bulk_add(stardust_db *db, const stardust_document *patch);

Parameters

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleRequiredKeep the handle alive until the call returns.
patchconst stardust_document *Borrowed handleRequiredOne borrowed entity-keyed merge-patch document, as merge_patch takes it. Temporary names use #_name and are scoped to this patch. Transaction metadata uses #_tx. The document is borrowed only until the call returns.

Errors

StatusWhen
STARDUST_INVALID_ARGUMENTno bulk build is in progress
STARDUST_REJECTEDthe database refuses the patch; the build has failed and later adds report the same status
STARDUST_CLOSEDthe database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

Patches retain add order. Entity and field order within a patch does not matter. Temporary names resolve within one patch. The call waits for space within the build's memory budget. After failure, use finish for its diagnostic or abort for cleanup.

Patch documents.

Examples

Queue patches in commit order

c826 B
// The first entity of an empty database is 1, so the second patch updates it.
const char *patches[] = {"{\"#_ada\":{\"name\":\"Ada\"}}", "{\"1\":{\"title\":\"Countess\"}}"};
stardust_result *finished = NULL;
uint32_t status = stardust_db_bulk_begin(db);
for (size_t index = 0; status == STARDUST_OK && index < 2; index++) {
    stardust_bytes patch = {(const uint8_t *)patches[index], (int64_t)strlen(patches[index])};
    stardust_document *document = NULL;
    status = stardust_parse(patch, STARDUST_JSON, &document);
    if (status == STARDUST_OK) status = stardust_db_bulk_add(db, document);
    stardust_document_release(document);
}
if (status == STARDUST_OK) status = stardust_db_bulk_finish(db, &finished);
else stardust_db_bulk_abort(db);
stardust_result_release(finished);
if (status != STARDUST_OK) return status;

stardust_db_bulk_finish

c76 B
uint32_t stardust_db_bulk_finish(stardust_db *db, stardust_result **output);
c92 B
uint32_t stardust_result_take_document(stardust_result *result, stardust_document **output);
c96 B
uint32_t stardust_result_error(const stardust_result *result, stardust_diagnostic_view *output);
c54 B
void stardust_result_release(stardust_result *result);

Parameters

stardust_db_bulk_finish

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_result **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_result_take_document

ParameterTypeOwnershipDefaultMeaning
resultstardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_document **Owned outputRequiredReceives a library-owned handle. Release it after use.

stardust_result_error

ParameterTypeOwnershipDefaultMeaning
resultconst stardust_result *Borrowed handleRequiredKeep the handle alive until the call returns.
outputstardust_diagnostic_view *Caller-owned outputRequiredReceives a record. Its pointer fields follow the record's ownership contract.

stardust_result_release

ParameterTypeOwnershipDefaultMeaning
resultstardust_result *Consumed handleNULLEnds ownership. Do not use the handle after this call.

Errors

StatusWhen
STARDUST_INVALID_ARGUMENTno bulk build is in progress
STARDUST_REJECTEDthe database refuses a patch; an initial build publishes nothing and fallback keeps earlier commits
STARDUST_CONFLICTanother write committed to the database during the build
STARDUST_CLOSEDthe database is closed
STARDUST_OUT_OF_MEMORYnative memory is exhausted

Notes

Finish ends the build even on failure. An initial build publishes all patches or none. Fallback keeps patches already committed.

The summary contains:

FieldMeaning
statuscommitted, or no_change when nothing was written
patches, transactions, facts, entitiesLoad counts
firstTransaction, lastTransactionAssigned transaction range
fallbackWhether patches committed individually
threadsWorker count
tempBytesTemporary run bytes
memoryBytes, memoryPeakBytesReserved memory share and its peak use
phasesparse, assign, rows, updates, spill, build and commit timings, each with wallNs and cpuNs

The memory budget covers build allocations. It excludes operating-system file cache and the process baseline.

Examples

Load patches with one commit and read the summary

c928 B
const char *text = "{\"#_ada\":{\"name\":\"Ada\",\"title\":\"Countess\"}}";
stardust_bytes patch = {(const uint8_t *)text, (int64_t)strlen(text)};
stardust_result *finished = NULL;
stardust_document *summary = NULL;
uint32_t status = stardust_db_bulk_begin(db);
stardust_document *document = NULL;
if (status == STARDUST_OK) status = stardust_parse(patch, STARDUST_JSON, &document);
if (status == STARDUST_OK) status = stardust_db_bulk_add(db, document);
stardust_document_release(document);
// finish ends the build also when it fails.
if (status == STARDUST_OK) status = stardust_db_bulk_finish(db, &finished);
else stardust_db_bulk_abort(db);
if (status == STARDUST_OK) status = stardust_result_take_document(finished, &summary);
stardust_result_release(finished);
// summary holds status, patches, transactions, facts, entities and phase timings.
stardust_document_release(summary);
if (status != STARDUST_OK) return status;

Also calls: stardust_result_take_document, stardust_result_error, stardust_result_release

stardust_db_bulk_abort

c45 B
void stardust_db_bulk_abort(stardust_db *db);

Parameters

ParameterTypeOwnershipDefaultMeaning
dbstardust_db *Borrowed handleNULLKeep the handle alive until the call returns.

Notes

Abort preserves patches already committed in fallback mode. It is safe without a build or after database closure.

Examples

Drop a bulk build without writing it

c527 B
const char *text = "{\"#_ada\":{\"name\":\"Ada\"}}";
stardust_bytes patch = {(const uint8_t *)text, (int64_t)strlen(text)};
uint32_t status = stardust_db_bulk_begin(db);
stardust_document *document = NULL;
if (status == STARDUST_OK) status = stardust_parse(patch, STARDUST_JSON, &document);
if (status == STARDUST_OK) status = stardust_db_bulk_add(db, document);
stardust_document_release(document);
// Abort writes nothing; without a build it does nothing.
stardust_db_bulk_abort(db);
if (status != STARDUST_OK) return status;

stardust_db_subscription_set_notify

c59 B
typedef void (*stardust_db_subscription_notify)(void *ctx);
c127 B
uint32_t stardust_db_subscription_set_notify(stardust_db_subscription *sub, stardust_db_subscription_notify notify, void *ctx);

Parameters

ParameterRequirementMeaning
subRequiredSubscription receiving the callback. Use the handle exclusively during registration.
notifyRequiredReadiness callback.
ctxNULL is validCaller-owned state, passed unchanged to the callback. Stardust does not read or free it.

Notes

Use this optional callback to wake a host scheduler without polling or a dedicated waiting thread. Blocking next works without registration.

Register a borrowed signal-only C callback once, before any next call. NULL context is valid. Native registration allocates nothing. Install the application waiter before next with zero wait; TIMEOUT arms a coalesced readiness hint. The callback may run on any thread, must return promptly, and must not call Stardust or run application work. Keep callback code and context valid until subscription release returns; no callback is running or can run afterward. Concurrent next calls race for events. A null subscription or callback, duplicate registration, or registration after next reports invalid_argument. A failed subscription preserves its saved failure. A rejected registration retains nothing.

The example requires a C implementation with C11 threads (<threads.h>) and timed condition waits.

Examples

Wait through a readiness callback

query_text holds this document:

edn66 B
{:find    [?name]
 :where   [[?e "name" ?name]]
 :orderBy [?name]}
c2.6 kB
#include <threads.h>
#include <time.h>
#include <stdlib.h>

struct subscription_waiter {
    mtx_t mutex;
    cnd_t changed;
    uint64_t generation;
};

static void notify_sync_check(int status) {
    if (status != thrd_success) abort();
}

static void subscription_ready(void *ctx) {
    struct subscription_waiter *waiter = ctx;
    notify_sync_check(mtx_lock(&waiter->mutex));
    waiter->generation++;
    notify_sync_check(cnd_broadcast(&waiter->changed));
    notify_sync_check(mtx_unlock(&waiter->mutex));
}

struct subscription_waiter waiter = {0};
if (mtx_init(&waiter.mutex, mtx_plain) != thrd_success) return STARDUST_OUT_OF_MEMORY;
if (cnd_init(&waiter.changed) != thrd_success) {
    mtx_destroy(&waiter.mutex);
    return STARDUST_OUT_OF_MEMORY;
}
stardust_document *definition = NULL;
stardust_db_query *query = NULL;
stardust_db_subscription *subscription = NULL;
stardust_db_live_event event = {0};
uint32_t status = stardust_parse(query_text, STARDUST_EDN, &definition);
stardust_db_source source = {.kind = STARDUST_SOURCE_DOCUMENT, .document = definition};
if (status == STARDUST_OK) status = stardust_db_query_open(db, &source, &query);
if (status == STARDUST_OK) status = stardust_db_query_subscribe(query, NULL, NULL, &subscription);
if (status == STARDUST_OK) status = stardust_db_subscription_set_notify(subscription, subscription_ready, &waiter);
struct timespec deadline = {0};
if (timespec_get(&deadline, TIME_UTC) != TIME_UTC) status = STARDUST_INVALID_ARGUMENT;
deadline.tv_sec += 5;
while (status == STARDUST_OK) {
    notify_sync_check(mtx_lock(&waiter.mutex));
    uint64_t observed = waiter.generation;
    notify_sync_check(mtx_unlock(&waiter.mutex));
    status = stardust_db_subscription_next(subscription, 0, &event);
    if (status != STARDUST_TIMEOUT) break;
    notify_sync_check(mtx_lock(&waiter.mutex));
    int waited = thrd_success;
    while (waiter.generation == observed && waited == thrd_success)
        waited = cnd_timedwait(&waiter.changed, &waiter.mutex, &deadline);
    notify_sync_check(mtx_unlock(&waiter.mutex));
    if (waited == thrd_success) status = STARDUST_OK;
    else if (waited != thrd_timedout) status = STARDUST_INVALID_ARGUMENT;
}
// Release joins callbacks before the stack context or its mutex is destroyed.
stardust_db_subscription_release(subscription);
stardust_db_query_release(query);
stardust_document_release(definition);
cnd_destroy(&waiter.changed);
mtx_destroy(&waiter.mutex);
if (status != STARDUST_OK) {stardust_document_release(event.result); return status;}
stardust_document *result = event.result;
Result / 51 B
{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}

stardust_db_query_options

This record supplies options for query and mutation run. Zero selects each default. Mutations use DB_Write_Options, whose fields are a subset.

Note

Supply size and version with each record. NULL options select the defaults of the call.

FieldC typeUnitRangeDefaultMeaning
sizeuint32_tBytesUnsigned u32Required: sizeof(stardust_db_query_options)—
versionuint32_tNoneUnsigned u32Required: 1—
page_sizeuint32_tCountUnsigned u320 selects the default page sizeRows per page. 0 selects the default page size. Queries only.
reserveduint32_tNoneUnsigned u320Must be zero.
max_cost_ticksint64_t100 ns ticksSigned i640 selects the dynamic CPU budgetThread CPU duration limit. 0 selects the engine’s dynamic budget.
as_ofint64_tNoneSigned i64ZeroA positive transaction runs a stored definition as of that transaction.

stardust_db_write_options

Note

Supply size and version with each record. NULL options select the defaults of the call.

FieldC typeUnitRangeDefaultMeaning
sizeuint32_tBytesUnsigned u32Required: sizeof(stardust_db_write_options)—
versionuint32_tNoneUnsigned u32Required: 1—
max_cost_ticksint64_t100 ns ticksSigned i640 selects the dynamic CPU budgetThread CPU duration limit. 0 selects the engine’s dynamic budget.
as_ofint64_tNoneSigned i64ZeroA positive transaction runs a stored definition as of that transaction.

stardust_db_commit_info

This record contains the result of mutation run, merge_patch and patch_definition.

FieldC typeUnitRangeDefaultMeaning
statusuint32_tNoneUnsigned u32The call sets it—
reserveduint32_tNoneUnsigned u320Must be zero.
transactionint64_tNoneSigned i64The call sets itZero when nothing was written.
factsint64_tCountSigned i64The call sets it—
committed_at_ticksint64_t100 ns ticks since the Unix epoch (UTC)Signed i64The call sets itRecorded commit time as a UTC instant. The call measures this value. Independent calls can differ.

stardust_db_live_event

This record contains the result of subscription next.

FieldC typeUnitRangeMeaning
sequenceint64_tCountSigned i64Event number. Event 1 is the initial result.
supersededint64_tCountSigned i64Intermediate computed results skipped by this subscription since its previous event. Zero for its first event. The delivered result replaces them in full. This value counts neither commits nor changed rows.
transactionint64_tNoneSigned i64Transaction the result was computed at.
resultstardust_document *NoneC type rangeThe full query result. The event owns it and the consumer closes it.