Installation
Plugins add behavior to the embedded library. Install the core library before selecting plugins.
Included plugins
| Plugin | Name | Purpose |
|---|---|---|
| Adaptive query planner | adaptive_query_planner | Learn from query runs and compare alternative plans |
| Reactors | reactors | Run live mutations after commits |
| History retention | history | Purge history according to retention settings |
| Object store | objects | Store binary payloads as named files and directories |
| Backups | backups | Copy history and object payloads to local folders and S3-compatible storage, and recover from them |
| SDK generation | sdk | Generate typed language APIs from stored definitions |
The library's plugins operation lists the plugins it can use and their requirements.
The coming soon list describes prototype features that are not available in this library.
| Form | Plugin set | Files | Change the set |
|---|---|---|---|
| Plugin library | One shared library per plugin beside the core library | The plugin libraries in lib/<os>_<arch>/ | Use the plugin libraries from the same release as the core library |
| Compiled into the core | Chosen when the core library is built | None | Rebuild the core library |
| Static archive | Every plugin in the release, linked into your program | None | Relink with the static archive of another release |
Release packages ship the adaptive planner, Reactors, SDK generation, the object store and backups as plugin libraries. History retention is compiled into the core library. The static archive in the same package contains all these plugins. In all three forms, a database enables plugins when it opens, as described in Enable plugins.
Plugin libraries
A release package keeps each platform's shared core library and plugin libraries together in that platform's directory, lib/<os>_<arch>/:
| File | Linux | macOS | Windows |
|---|---|---|---|
| Core library | libstardust.so | libstardust.dylib | stardust.dll |
| Plugin library, one per plugin | libstardust_<name>.so | libstardust_<name>.dylib | stardust_<name>.dll |
Keep these files together, in the directory of the core library, and use them only with the release they came with. The core library looks for plugin libraries only in its own directory, the first time a call needs the plugin list. Opening a database, listing plugins and checking a license can trigger this scan. The bindings need no separate discovery setting.
Verification
The release build records each finished plugin library's SHA-256 in the core library. The core library accepts only the plugin libraries it was built with:
- It carries a SHA-256 for the plugin named by the file.
- The file's SHA-256 equals that value.
- The plugin library is from the same build and exports the plugin entry under its own name.
The core library reads the file once, checks its SHA-256, and loads the bytes that it checked. On Linux it loads the file through its open descriptor. On Windows it keeps the file open without write sharing until it loads. On macOS it keeps the file open and relies on the library's code signature.
Check the release download with minisign, as described in Verify the download. Each binding checks the core library's SHA-256. The core then checks its plugin libraries. See Load the core for the binding check. These checks detect a mismatched, corrupted, or replaced plugin library.
Available and unavailable plugins
A plugin library is available only after it passes every check. Missing libraries and failed checks make the plugin unavailable. Selecting an unavailable plugin causes database open to fail.
The plugins operation lists each available plugin with its name, description, version, requirements, willNotWorkWith, definition types, hooks, index providers, and database hooks.
After them, it lists each unavailable plugin library as an object with name, file, and unavailable, which states the reason:
| Reason | Cause |
|---|---|
| This core library was not built with the plugin library | The file is not part of this release |
| The plugin library differs from the one this core library was built with | The file changed after the release was built |
| The plugin library cannot be loaded | The platform loader refused the file |
| The plugin library has no plugin entry | The file is not a Stardust plugin library |
| The plugin library is from another build | The file came with a different release |
| The plugin library's name differs from its plugin name | The file was renamed |
| A plugin with this name is already included | The core library already contains that plugin |
| The plugin library's plugin is invalid | The plugin description is not usable |
Development builds
Contributors who build the library from source can compile a development core library that loads plugin libraries without checking their SHA-256. This switch exists only at compile time, for development. Release packaging refuses a library built with it. Release libraries always check their plugin libraries.
Plugins compiled into the core
A plugin compiled into the core library is part of that library file. It needs no plugin library file or SHA-256 check, and it is always available to databases. The plugins that a core library contains are chosen when it is built, so changing them requires rebuilding the core library.
Plugins in a static link
Go, Rust, Odin and C programs can link the static archive (libstardust_full.a, or stardust_full.lib on Windows) instead of the shared core library. Linking gives the commands.
The archive contains the core and every plugin of its release, compiled in. The plugin set is fixed when you link your program.
The backups plugin in the archive carries its own Zstandard copy under stardust_-prefixed symbol names, so your program can also link its own libzstd.
- Ship only your executable. A static program uses no core library, no plugin libraries and no SHA-256 checks.
- A static program does not look for plugin libraries. Plugin library files beside the executable have no effect.
- To use the plugins of another release, link your program again with that release's static archive.
- Each database still enables plugins when it opens, as described in Enable plugins. The license
pluginspermission applies as it does for the shared library.
Check the download with minisign before you link the archive, as described in Verify the download. The bindings do not check the SHA-256 of a statically linked core: it is part of your executable.
Enable plugins
Use the plugins database option when opening a database. It works the same way for plugin libraries, plugins compiled into the core, and static links.
Selection applies until the database closes. Only named plugins are enabled.
| Selection | Result |
|---|---|
Omit plugins | Enable no plugins |
Empty object, {} | Enable no plugins |
Object, such as {reactors {}} | Enable the named plugins with their load settings |
| An unknown name | The open fails with invalid_argument |
Each object key is a plugin name. Its value is a settings object. An empty settings object uses the plugin's defaults.
Select only available plugins. Include each selected plugin's required dependencies in the object.
The willNotWorkWith list names plugins that cannot be enabled together.
If either plugin lists the other, opening the database fails with invalid_argument before configuration or storage changes.
A plugin in this list can be absent from the library or disabled.
Unknown settings and invalid values fail before the database opens.
The adaptive planner accepts memoryShare.
The history plugin accepts scheduling settings, backups accepts its targets, and SDK generation accepts language output settings.
The reactors and objects plugins accept only empty settings.
A disabled plugin does not install its database hooks, definition types, or index providers.
A write that sets stardust/history reports unavailable when the database does not enable history.
A write that sets stardust/object/digest reports unavailable when the database does not enable objects.
Plugin operations
A plugin serves named operations through two calls:
| Call | Input | Result |
|---|---|---|
| plugin_call | Request document | Result document |
| plugin_stream | Request document | Byte stream, such as an object upload |
Bindings provide typed wrappers for the supported operations. Each wrapper builds a request and calls the same underlying operation.
A disabled plugin reports unavailable. An unknown operation reports invalid_argument with code unknown_operation.
Plugins name their runtime description status and their wait for committed work sync, when they offer them.
Plugin boundary
All plugins are first-party plugins from the Stardust project. The plugin entry that a plugin library exports is internal to Stardust: it is not part of the C ABI, and the bindings do not call it. There is no interface for third-party plugins.
Operations
| Operation | Returns | Summary |
|---|---|---|
| stardust_db_plugin_call | Status. document through output | database.plugin_call(plugin, operation, request) runs a named operation of an enabled plugin and returns its owned result document. |
| stardust_db_plugin_stream_open | Status. plugin_stream through output | database.plugin_stream(plugin, operation, request) opens an owned plugin stream of a named stream operation. |
| stardust_db_plugin_stream_write | Status. count through output | plugin_stream.write(data) appends bytes to the stream and returns the count accepted. |
| stardust_db_plugin_stream_read | Status. text through output | plugin_stream.read(length) returns up to length bytes from the stream's position and advances it. |
| stardust_db_plugin_stream_seek | Status. integer through output | plugin_stream.seek(offset, whence) moves the stream's position and returns it. |
| stardust_db_plugin_stream_finish | Status. document through output | plugin_stream.finish() ends the stream and returns its owned result document. |
| stardust_db_plugin_stream_release | void | Release the plugin_stream handle, ending an unfinished stream without finishing it. |
stardust_db_plugin_call
uint32_t stardust_db_plugin_call(stardust_db *db, stardust_bytes plugin_name, stardust_bytes operation, const stardust_document *request, stardust_result **output);uint32_t stardust_result_take_document(stardust_result *result, stardust_document **output);uint32_t stardust_result_error(const stardust_result *result, stardust_diagnostic_view *output);void stardust_result_release(stardust_result *result);Parameters
stardust_db_plugin_call
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
db | stardust_db * | Borrowed handle | Required | Keep the handle alive until the call returns. |
plugin_name | stardust_bytes | Value | Required | Value passed to the call. |
operation | stardust_bytes | Value | Required | The plugin's call operation. |
request | const stardust_document * | Borrowed handle | NULL | The borrowed request document. Its absence reads as an empty object. |
output | stardust_result ** | Owned output | Required | Receives a library-owned handle. Release it after use. |
stardust_result_take_document
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
result | stardust_result * | Borrowed handle | Required | Keep the handle alive until the call returns. |
output | stardust_document ** | Owned output | Required | Receives a library-owned handle. Release it after use. |
stardust_result_error
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
result | const stardust_result * | Borrowed handle | Required | Keep the handle alive until the call returns. |
output | stardust_diagnostic_view * | Caller-owned output | Required | Receives a record. Its pointer fields follow the record's ownership contract. |
stardust_result_release
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
result | stardust_result * | Consumed handle | NULL | Ends ownership. Do not use the handle after this call. |
Errors
| Status | When |
|---|---|
STARDUST_INVALID_ARGUMENT | a name is empty, the plugin has no call operation with the name, or the plugin refuses the request |
STARDUST_UNAVAILABLE | the plugin is unknown or the database does not enable it |
STARDUST_NOT_FOUND | the plugin reports that the request names nothing it holds |
STARDUST_CONFLICT | the plugin's write lost to a concurrent change, or the database holds writes |
STARDUST_EVALUATION_FAILED | the plugin cannot read its stored state |
STARDUST_FAILED | the plugin's work failed |
STARDUST_TIMEOUT | the plugin's wait passed its limit |
STARDUST_CLOSED | the database is closed |
STARDUST_OUT_OF_MEMORY | native memory is exhausted |
Notes
The plugin decodes the request and builds the result. A result is null when the operation returns nothing. Returned documents omit empty object and array members recursively; empty roots, array elements and scalar values remain. Each plugin documents its operations: object store, reactors and SDK generation.
Examples
Wait for live mutations, then describe them
const stardust_bytes reactors = {(const uint8_t *)"reactors", sizeof("reactors") - 1};
const char options_text[] = "{maxWait {#dur PT5S}}";
stardust_document *options = NULL;
stardust_result *synced = NULL;
stardust_result *described = NULL;
uint32_t status = stardust_parse((stardust_bytes){(const uint8_t *)options_text, sizeof(options_text) - 1}, STARDUST_DUST, &options);
// sync returns a null document.
if (status == STARDUST_OK) status = stardust_db_plugin_call(db, reactors, (stardust_bytes){(const uint8_t *)"sync", 4}, options, &synced);
stardust_result_release(synced);
stardust_document_release(options);
// A NULL request reads as an empty object.
if (status == STARDUST_OK) status = stardust_db_plugin_call(db, reactors, (stardust_bytes){(const uint8_t *)"status", 6}, NULL, &described);
if (status != STARDUST_OK) { stardust_result_release(described); return status; }
stardust_document *result = NULL;
status = stardust_result_take_document(described, &result);
stardust_result_release(described);
if (status != STARDUST_OK) return status;| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 4C 68656164 | txt "head" |
| 09 | 02 04000000000000 00 | i64 4 |
| 12 | 0F 0A 04000000 0900 0000 | map leaf, size 10: @4 → @9 |
| 1C | 6C 637572736F72 | txt "cursor" |
| 23 | 02 04000000000000 00 | i64 4 |
| 2C | 0F 0A 1C000000 2300 0000 | map leaf, size 10: @1C → @23 |
| 36 | 07 0E 04200000 1200 0000 2C000000 | map branch, size 14, depth 1, slots 2, 13: @12 → @2C |
| 44 | 7C 656E61626C6564 | txt "enabled" |
| 4C | 09 | bit true |
| 4D | 0F 0A 44000000 4C00 0000 | map leaf, size 10: @44 → @4C |
| 57 | 7C 70656E64696E67 | txt "pending" |
| 5F | 01 | bit false |
| 60 | 0F 0A 57000000 5F00 0000 | map leaf, size 10: @57 → @5F |
| 6A | 07 12 28020000 3600 0000 4D000000 6000 0000 | map branch, size 18, depth 0 (root), slots 3, 5, 9: @36 → @4D → @60 |
| 7C | 6A000000 00000000 | root → @6A; previous: none |
Also calls: stardust_result_take_document, stardust_result_error, stardust_result_release
stardust_db_plugin_stream_open
uint32_t stardust_db_plugin_stream_open(stardust_db *db, stardust_bytes plugin_name, stardust_bytes operation, const stardust_document *request, stardust_db_plugin_stream **stream, stardust_result **output);uint32_t stardust_result_error(const stardust_result *result, stardust_diagnostic_view *output);void stardust_result_release(stardust_result *result);Parameters
stardust_db_plugin_stream_open
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
db | stardust_db * | Borrowed handle | Required | Keep the handle alive until the call returns. |
plugin_name | stardust_bytes | Value | Required | Value passed to the call. |
operation | stardust_bytes | Value | Required | The plugin's stream operation. |
request | const stardust_document * | Borrowed handle | NULL | The borrowed request document. Its absence reads as an empty object. |
stream | stardust_db_plugin_stream ** | Owned output | Required | Receives a library-owned handle. Release it after use. |
output | stardust_result ** | Owned output | Required | Receives a library-owned handle. Release it after use. |
stardust_result_error
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
result | const stardust_result * | Borrowed handle | Required | Keep the handle alive until the call returns. |
output | stardust_diagnostic_view * | Caller-owned output | Required | Receives a record. Its pointer fields follow the record's ownership contract. |
stardust_result_release
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
result | stardust_result * | Consumed handle | NULL | Ends ownership. Do not use the handle after this call. |
Errors
| Status | When |
|---|---|
STARDUST_INVALID_ARGUMENT | a name is empty, the plugin has no stream operation with the name, or the plugin refuses the request |
STARDUST_UNAVAILABLE | the plugin is unknown or the database does not enable it |
STARDUST_NOT_FOUND | the plugin reports that the request names nothing it holds |
STARDUST_CONFLICT | the database holds writes |
STARDUST_EVALUATION_FAILED | the plugin cannot read its stored state |
STARDUST_CLOSED | the database is closed |
STARDUST_OUT_OF_MEMORY | native memory is exhausted |
Notes
A stream supports the steps its operation documents. Each plugin documents its stream operations: object store.
Examples
Upload a payload and publish it
stardust_db_plugin_stream *upload = NULL;
stardust_result *output = NULL;
const char first[] = "Hello, ";
const char second[] = "objects";
int64_t written = 0;
// The open result carries a refusal's diagnostic; release it either way.
stardust_result *opened = NULL;
uint32_t status = stardust_db_plugin_stream_open(db, (stardust_bytes){(const uint8_t *)"objects", sizeof("objects") - 1}, (stardust_bytes){(const uint8_t *)"upload", sizeof("upload") - 1}, NULL, &upload, &opened);
stardust_result_release(opened);
opened = NULL;
if (status == STARDUST_OK) status = stardust_db_plugin_stream_write(upload, (stardust_bytes){(const uint8_t *)first, sizeof(first) - 1}, &written);
if (status == STARDUST_OK) status = stardust_db_plugin_stream_write(upload, (stardust_bytes){(const uint8_t *)second, sizeof(second) - 1}, &written);
if (status == STARDUST_OK) status = stardust_db_plugin_stream_finish(upload, &output);
// Release abandons the upload unless it finished.
stardust_db_plugin_stream_release(upload);
stardust_document *result = NULL;
if (status == STARDUST_OK) status = stardust_result_take_document(output, &result);
stardust_result_release(output);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 7C 23736861323536 | txt "#sha256" |
| 0C | 14 40 393565383464 6133386362656664 6633663966366566 3032323031303734 6337333132346637 6366323032356433 6531643639653539 3161343130383536 6662 | txt "95e84da38cbefdf3f9f6ef02…" |
| 4E | 0F 0A 04000000 0C00 0000 | map leaf, size 10: @4 → @C |
| 58 | 4C 73697A65 | txt "size" |
| 5D | 02 0E000000000000 00 | i64 14 |
| 66 | 0F 0A 58000000 5D00 0000 | map leaf, size 10: @58 → @5D |
| 70 | 6C 646967657374 | txt "digest" |
| 77 | 0F 0A 70000000 4E00 0000 | map leaf, size 10: @70 → @4E |
| 81 | CC 64656475706C69 6361746564 | txt "deduplicated" |
| 8E | 01 | bit false |
| 8F | 0F 0A 81000000 8E00 0000 | map leaf, size 10: @81 → @8E |
| 99 | 07 0E 00440000 7700 0000 8F000000 | map branch, size 14, depth 1, slots 10, 14: @77 → @8F |
| A7 | 07 0E 00030000 6600 0000 99000000 | map branch, size 14, depth 0 (root), slots 8, 9: @66 → @99 |
| B5 | A7000000 00000000 | root → @A7; previous: none |
Also calls: stardust_result_error, stardust_result_release
stardust_db_plugin_stream_write
uint32_t stardust_db_plugin_stream_write(stardust_db_plugin_stream *stream, stardust_bytes data, int64_t *written);Requires exclusive use of the receiver handle.
Parameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
stream | stardust_db_plugin_stream * | Borrowed handle | Required | Keep the handle alive until the call returns. |
data | stardust_bytes | Value | Required | The bytes to append, borrowed for the call. |
written | int64_t * | Caller-owned output | Required | Receives the scalar result. |
Errors
| Status | When |
|---|---|
STARDUST_INVALID_ARGUMENT | the stream has finished or does not accept writes |
STARDUST_CONFLICT | the database holds writes while the plugin stores the bytes |
STARDUST_CLOSED | the parent database is closed |
Notes
A failure also reports the count accepted before it, returned together with the error. Those bytes stay part of the stream; continue with the remaining bytes.
Examples
Append bytes to an upload
stardust_db_plugin_stream *upload = NULL;
const char payload[] = "Hello, objects";
int64_t result = 0;
// The open result carries a refusal's diagnostic; release it either way.
stardust_result *opened = NULL;
uint32_t status = stardust_db_plugin_stream_open(db, (stardust_bytes){(const uint8_t *)"objects", sizeof("objects") - 1}, (stardust_bytes){(const uint8_t *)"upload", sizeof("upload") - 1}, NULL, &upload, &opened);
stardust_result_release(opened);
opened = NULL;
// result counts the accepted bytes, also on failure.
if (status == STARDUST_OK) status = stardust_db_plugin_stream_write(upload, (stardust_bytes){(const uint8_t *)payload, sizeof(payload) - 1}, &result);
stardust_db_plugin_stream_release(upload);
if (status != STARDUST_OK) return status;| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 02 0E000000000000 00 | i64 14 |
| 0D | 04000000 00000000 | root → @4; previous: none |
stardust_db_plugin_stream_read
uint32_t stardust_db_plugin_stream_read(stardust_db_plugin_stream *stream, uint8_t *buffer, int64_t capacity, int64_t *written);Requires exclusive use of the receiver handle.
Parameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
stream | stardust_db_plugin_stream * | Borrowed handle | Required | Keep the handle alive until the call returns. |
buffer | uint8_t * | Caller-owned array | NULL | Writes at most capacity elements and reports their count in written. |
capacity | int64_t | Value | Required | Value passed to the call. |
written | int64_t * | Caller-owned output | Required | Receives the scalar result. |
Errors
| Status | When |
|---|---|
STARDUST_INVALID_ARGUMENT | the stream does not support reads, or length is negative |
STARDUST_EVALUATION_FAILED | stored bytes do not match their digest |
STARDUST_CLOSED | the parent database is closed |
Notes
A read is shorter than length only at the end, and empty at or past it.
Examples
Read the start of a payload
const stardust_bytes objects = {(const uint8_t *)"objects", sizeof("objects") - 1};
stardust_db_plugin_stream *upload = NULL;
stardust_result *published = NULL;
const char payload[] = "Hello, objects";
int64_t written = 0;
// The open result carries a refusal's diagnostic; release it either way.
stardust_result *opened = NULL;
uint32_t status = stardust_db_plugin_stream_open(db, objects, (stardust_bytes){(const uint8_t *)"upload", 6}, NULL, &upload, &opened);
stardust_result_release(opened);
opened = NULL;
if (status == STARDUST_OK) status = stardust_db_plugin_stream_write(upload, (stardust_bytes){(const uint8_t *)payload, sizeof(payload) - 1}, &written);
if (status == STARDUST_OK) status = stardust_db_plugin_stream_finish(upload, &published);
stardust_result_release(published);
stardust_db_plugin_stream_release(upload);
if (status != STARDUST_OK) return status;
const char request_text[] = "{digest \"95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb\"}";
stardust_document *request = NULL;
stardust_db_plugin_stream *reader = NULL;
status = stardust_parse((stardust_bytes){(const uint8_t *)request_text, sizeof(request_text) - 1}, STARDUST_DUST, &request);
if (status == STARDUST_OK) status = stardust_db_plugin_stream_open(db, objects, (stardust_bytes){(const uint8_t *)"open", 4}, request, &reader, &opened);
stardust_result_release(opened);
opened = NULL;
stardust_document_release(request);
uint8_t buffer[5];
int64_t result = 0;
// The call copies straight into the caller's buffer; buffer holds "Hello".
if (status == STARDUST_OK) status = stardust_db_plugin_stream_read(reader, buffer, sizeof(buffer), &result);
stardust_db_plugin_stream_release(reader);
if (status != STARDUST_OK) return status;| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 02 05000000000000 00 | i64 5 |
| 0D | 04000000 00000000 | root → @4; previous: none |
stardust_db_plugin_stream_seek
uint32_t stardust_db_plugin_stream_seek(stardust_db_plugin_stream *stream, int64_t offset, uint32_t whence, int64_t *position);Requires exclusive use of the receiver handle.
Parameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
stream | stardust_db_plugin_stream * | Borrowed handle | Required | Keep the handle alive until the call returns. |
offset | int64_t | Value | Required | The distance from whence, negative toward the start. |
whence | uint32_t | Value | Required | 0 for the start, 1 for the current position, 2 for the end. |
position | int64_t * | Caller-owned output | Required | Receives the scalar result. |
Errors
| Status | When |
|---|---|
STARDUST_INVALID_ARGUMENT | the stream does not support seeks, whence is unknown, or the position is negative or past the i64 range |
STARDUST_CLOSED | the parent database is closed |
Notes
whence 0 counts offset from the start, 1 from the current position and 2 from the end. A refused seek keeps the position.
Examples
Seek from the end of a payload
const stardust_bytes objects = {(const uint8_t *)"objects", sizeof("objects") - 1};
stardust_db_plugin_stream *upload = NULL;
stardust_result *published = NULL;
const char payload[] = "Hello, objects";
int64_t written = 0;
// The open result carries a refusal's diagnostic; release it either way.
stardust_result *opened = NULL;
uint32_t status = stardust_db_plugin_stream_open(db, objects, (stardust_bytes){(const uint8_t *)"upload", 6}, NULL, &upload, &opened);
stardust_result_release(opened);
opened = NULL;
if (status == STARDUST_OK) status = stardust_db_plugin_stream_write(upload, (stardust_bytes){(const uint8_t *)payload, sizeof(payload) - 1}, &written);
if (status == STARDUST_OK) status = stardust_db_plugin_stream_finish(upload, &published);
stardust_result_release(published);
stardust_db_plugin_stream_release(upload);
if (status != STARDUST_OK) return status;
const char request_text[] = "{digest \"95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb\"}";
stardust_document *request = NULL;
stardust_db_plugin_stream *reader = NULL;
status = stardust_parse((stardust_bytes){(const uint8_t *)request_text, sizeof(request_text) - 1}, STARDUST_DUST, &request);
if (status == STARDUST_OK) status = stardust_db_plugin_stream_open(db, objects, (stardust_bytes){(const uint8_t *)"open", 4}, request, &reader, &opened);
stardust_result_release(opened);
opened = NULL;
stardust_document_release(request);
int64_t position = 0;
// whence 2 counts from the end, so -7 is the start of "objects".
if (status == STARDUST_OK) status = stardust_db_plugin_stream_seek(reader, -7, 2, &position);
stardust_db_plugin_stream_release(reader);
if (status != STARDUST_OK) return status;
int64_t result = position;| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 02 07000000000000 00 | i64 7 |
| 0D | 04000000 00000000 | root → @4; previous: none |
stardust_db_plugin_stream_finish
uint32_t stardust_db_plugin_stream_finish(stardust_db_plugin_stream *stream, stardust_result **output);uint32_t stardust_result_take_document(stardust_result *result, stardust_document **output);uint32_t stardust_result_error(const stardust_result *result, stardust_diagnostic_view *output);void stardust_result_release(stardust_result *result);Requires exclusive use of the receiver handle.
Parameters
stardust_db_plugin_stream_finish
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
stream | stardust_db_plugin_stream * | Borrowed handle | Required | Keep the handle alive until the call returns. |
output | stardust_result ** | Owned output | Required | Receives a library-owned handle. Release it after use. |
stardust_result_take_document
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
result | stardust_result * | Borrowed handle | Required | Keep the handle alive until the call returns. |
output | stardust_document ** | Owned output | Required | Receives a library-owned handle. Release it after use. |
stardust_result_error
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
result | const stardust_result * | Borrowed handle | Required | Keep the handle alive until the call returns. |
output | stardust_diagnostic_view * | Caller-owned output | Required | Receives a record. Its pointer fields follow the record's ownership contract. |
stardust_result_release
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
result | stardust_result * | Consumed handle | NULL | Ends ownership. Do not use the handle after this call. |
Errors
| Status | When |
|---|---|
STARDUST_INVALID_ARGUMENT | the stream has finished or has no finish step |
STARDUST_CONFLICT | the database holds writes |
STARDUST_CLOSED | the parent database is closed |
STARDUST_OUT_OF_MEMORY | native memory is exhausted |
Notes
A failed finish keeps the stream, so finish can be retried. Close the stream separately.
Examples
Publish the same payload twice
// The second finish reports deduplicated true and stores nothing new.
stardust_db_plugin_stream *upload = NULL;
stardust_result *published = NULL;
const char payload[] = "Hello, objects";
int64_t written = 0;
// The open result carries a refusal's diagnostic; release it either way.
stardust_result *opened = NULL;
uint32_t status = stardust_db_plugin_stream_open(db, (stardust_bytes){(const uint8_t *)"objects", sizeof("objects") - 1}, (stardust_bytes){(const uint8_t *)"upload", sizeof("upload") - 1}, NULL, &upload, &opened);
stardust_result_release(opened);
opened = NULL;
if (status == STARDUST_OK) status = stardust_db_plugin_stream_write(upload, (stardust_bytes){(const uint8_t *)payload, sizeof(payload) - 1}, &written);
if (status == STARDUST_OK) status = stardust_db_plugin_stream_finish(upload, &published);
stardust_result_release(published);
stardust_db_plugin_stream_release(upload);
if (status != STARDUST_OK) return status;
stardust_result *output = NULL;
upload = NULL;
status = stardust_db_plugin_stream_open(db, (stardust_bytes){(const uint8_t *)"objects", sizeof("objects") - 1}, (stardust_bytes){(const uint8_t *)"upload", sizeof("upload") - 1}, NULL, &upload, &opened);
stardust_result_release(opened);
opened = NULL;
if (status == STARDUST_OK) status = stardust_db_plugin_stream_write(upload, (stardust_bytes){(const uint8_t *)payload, sizeof(payload) - 1}, &written);
if (status == STARDUST_OK) status = stardust_db_plugin_stream_finish(upload, &output);
stardust_db_plugin_stream_release(upload);
stardust_document *result = NULL;
if (status == STARDUST_OK) status = stardust_result_take_document(output, &result);
stardust_result_release(output);
if (status != STARDUST_OK) {stardust_document_release(result); return status;}| Offset (hex) | Bytes (hex) | Meaning |
|---|---|---|
| 00 | 54 52 4F 4E | magic TRON |
| 04 | 7C 23736861323536 | txt "#sha256" |
| 0C | 14 40 393565383464 6133386362656664 6633663966366566 3032323031303734 6337333132346637 6366323032356433 6531643639653539 3161343130383536 6662 | txt "95e84da38cbefdf3f9f6ef02…" |
| 4E | 0F 0A 04000000 0C00 0000 | map leaf, size 10: @4 → @C |
| 58 | 4C 73697A65 | txt "size" |
| 5D | 02 0E000000000000 00 | i64 14 |
| 66 | 0F 0A 58000000 5D00 0000 | map leaf, size 10: @58 → @5D |
| 70 | 6C 646967657374 | txt "digest" |
| 77 | 0F 0A 70000000 4E00 0000 | map leaf, size 10: @70 → @4E |
| 81 | CC 64656475706C69 6361746564 | txt "deduplicated" |
| 8E | 09 | bit true |
| 8F | 0F 0A 81000000 8E00 0000 | map leaf, size 10: @81 → @8E |
| 99 | 07 0E 00440000 7700 0000 8F000000 | map branch, size 14, depth 1, slots 10, 14: @77 → @8F |
| A7 | 07 0E 00030000 6600 0000 99000000 | map branch, size 14, depth 0 (root), slots 8, 9: @66 → @99 |
| B5 | A7000000 00000000 | root → @A7; previous: none |
Also calls: stardust_result_take_document, stardust_result_error, stardust_result_release
stardust_db_plugin_stream_release
void stardust_db_plugin_stream_release(stardust_db_plugin_stream *stream);Consumes the receiver handle.
Parameters
| Parameter | Type | Ownership | Default | Meaning |
|---|---|---|---|---|
stream | stardust_db_plugin_stream * | Consumed handle | NULL | Ends ownership. Do not use the handle after this call. |
Notes
Close waits only for a call already in progress on the stream; start no other call once close begins. It is valid after the database closes. A plugin documents what close abandons; the object store reclaims an upload stage at the next open when close could not abandon it.
Examples
Abandon an unfinished upload
stardust_db_plugin_stream *upload = NULL;
const char draft[] = "draft";
int64_t written = 0;
// The open result carries a refusal's diagnostic; release it either way.
stardust_result *opened = NULL;
uint32_t status = stardust_db_plugin_stream_open(db, (stardust_bytes){(const uint8_t *)"objects", sizeof("objects") - 1}, (stardust_bytes){(const uint8_t *)"upload", sizeof("upload") - 1}, NULL, &upload, &opened);
stardust_result_release(opened);
opened = NULL;
if (status == STARDUST_OK) status = stardust_db_plugin_stream_write(upload, (stardust_bytes){(const uint8_t *)draft, sizeof(draft) - 1}, &written);
// Releasing before finish publishes nothing.
stardust_db_plugin_stream_release(upload);
if (status != STARDUST_OK) return status;