Online Preview

Paste a .gedb schema, configure storage parameters, and generate C99 output instantly.

Schema Input
Generated Output
/*
 * main.c — Example usage of the GreatEmbeddo generated database.
 *
 * All writes are automatically wrapped in a transaction if called
 * outside gedb_transaction_write(). For multi-step writes, use gedb_transaction_write()
 * to group them into a single atomic commit.
 *
 * Reads via gedb_transaction_read() are lock-free and never block writers.
 *
 * SPDX-License-Identifier: MIT
 */

/* MSVC: allow fopen without _s variant */
#define _CRT_SECURE_NO_WARNINGS
#include "gedb.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* ── File-based I/O (ctx = FILE* opened with "r+b" or "w+b") ── */
/* File layout: block N starts at byte N * GEDB_BLOCK_SIZE. */
/* On real flash, replace these with your driver calls. */
static gedb_status_t my_read(uint64_t block_id, uint32_t offset,
                             void *buf, uint32_t len, void *ctx)
{
    FILE *f = (FILE *)ctx;
    if (fseek(f, (long)(block_id * GEDB_BLOCK_SIZE + offset), SEEK_SET) != 0)
    {
        return GEDB_ERR_IO;
    }
    if (fread(buf, 1, len, f) != len)
    {
        return GEDB_ERR_IO;
    }
    return GEDB_OK;
}

static gedb_status_t my_erase(uint64_t block_id, void *ctx)
{
    /* file-based: erases handled implicitly by write */
    (void)block_id; (void)ctx;
    return GEDB_OK;
}

static gedb_status_t my_write(uint64_t block_id, uint32_t offset,
                              const void *data, uint32_t len, void *ctx)
{
    FILE *f = (FILE *)ctx;
    if (fseek(f, (long)(block_id * GEDB_BLOCK_SIZE + offset), SEEK_SET) != 0)
    {
        return GEDB_ERR_IO;
    }
    if (fwrite(data, 1, len, f) != len)
    {
        return GEDB_ERR_IO;
    }
    if (fflush(f) != 0)
    {
        return GEDB_ERR_IO;
    }
    return GEDB_OK;
}

/* ── Batch insert: multiple rows in one atomic transaction ───── */
static gedb_status_t insert_three_rows(gedb_txn_t *txn, void *ctx)
{
    (void)ctx;
    gedb_sensors_row_t row;
    gedb_status_t st;

    (void)memset(&row, 0, sizeof(row));

    row.node_id = 100;
    row.temp_c = 10.0f;
    st = gedb_sensors_insert(txn, &row);
    /* rollback all three */
    if (st != GEDB_OK)
    {
        return st;
    }

    row.node_id = 200;
    row.temp_c = 20.0f;
    st = gedb_sensors_insert(txn, &row);
    /* rollback all three */
    if (st != GEDB_OK)
    {
        return st;
    }

    row.node_id = 300;
    row.temp_c = 30.0f;
    st = gedb_sensors_insert(txn, &row);
    /* rollback all three */
    if (st != GEDB_OK)
    {
        return st;
    }

    /* commit all three rows atomically */
    return GEDB_OK;
}

/* ── Read-only query example (lock-free) ──────────────────────── */
static gedb_status_t read_sample_data(gedb_txn_t *txn, void *ctx)
{
    (void)ctx;
    gedb_sensors_row_t row;
    gedb_status_t st = gedb_sensors_get(txn, (uint32_t)1, &row);
    if (st == GEDB_OK)
    {
        /* Use row.id, etc. */
    }
    else if (st != GEDB_ERR_NOT_FOUND)
    {
        /* propagate I/O or other errors */
        return st;
    }
    return GEDB_OK;
}

/* ── Iterate every row in sensors (lock-free) ───────────── */
static gedb_status_t iterate_sensors(gedb_txn_t *txn, void *ctx)
{
    (void)ctx;
    gedb_sensors_iter_t iter;
    gedb_status_t st = gedb_sensors_scan_open(txn, &iter);
    if (st != GEDB_OK)
    {
        return st;
    }

    gedb_sensors_row_t row;
    for (;;)
    {
        st = gedb_sensors_scan_next(txn, &iter, &row);
        /* end-of-scan, not an error */
        if (st == GEDB_DONE)
        {
            return GEDB_OK;
        }
        if (st != GEDB_OK)
        {
            return st;
        }
        /* row.id (and other columns) are valid here */
    }
}

/* ── Entry point ────────────────────────────────────────────────── */
int main(void)
{
    static gedb_db_t db;

    /* Open (or create) the flat storage file. */
    FILE *storage = fopen("gedb_storage.bin", "r+b");
    if (!storage)
    {
        storage = fopen("gedb_storage.bin", "w+b");
    }
    if (!storage)
    {
        return 1;
    }

    /* Open the database. On a fresh device gedb_init returns GEDB_NEEDS_FORMATTING; */
    /* call gedb_format with a caller-chosen unique database_id. Use a time-seeded */
    /* PRNG so each new database has a distinct id, which lets a future reformat */
    /* invalidate old data on the same flash without an O(BlockCount) erase pass. */
    gedb_status_t st = gedb_init(&db, my_read, my_erase, my_write, storage, NULL);
    if (st == GEDB_NEEDS_FORMATTING)
    {
        srand((unsigned)time(NULL));
        uint64_t database_id = ((uint64_t)rand() << 32) ^ (uint64_t)rand();
        st = gedb_format(&db, my_read, my_erase, my_write, storage, NULL, database_id);
    }
    if (st != GEDB_OK)
    {
        fclose(storage);
        return 1;
    }

    /* Batch insert: all three rows are committed atomically. */
    /* If any insert fails, the entire transaction is rolled back. */
    /* Faster than inserting one-by-one: one lock, one metadata commit. */
    st = gedb_transaction_write(&db, insert_three_rows, NULL);
    if (st != GEDB_OK) { /* handle error — all three rows were rolled back */ }

    /* Auto-commit: single insert, implicit transaction. */
    /* Each call acquires the lock, inserts, and commits automatically. */
    gedb_sensors_row_t new_row;
    (void)memset(&new_row, 0, sizeof(new_row));
    /* new_row.id = 0 — auto-assigned by the database */
    /* auto-commits */
    st = gedb_sensors_insert_auto(&db, &new_row);
    if (st != GEDB_OK) { /* handle error */ }

    /* Read-only queries are lock-free (never block writers). */
    /* They use a seqlock snapshot of the committed state. */
    st = gedb_transaction_read(&db, read_sample_data, NULL);
    if (st != GEDB_OK) { /* handle error */ }

    /* Walk every row in sensors via the generated scan API. */
    st = gedb_transaction_read(&db, iterate_sensors, NULL);
    if (st != GEDB_OK) { /* handle error */ }

    /* ── sensors CRUD ────────────────────────────── */
    /* gedb_sensors_row_t r; memset(&r, 0, sizeof(r)); */
    /* r.id = 0;  // auto-assigned by the database */
    /* gedb_sensors_insert_auto(&db, &r);       // auto-commits */
    /* gedb_sensors_get_auto(&db, (uint32_t)1, &r); */
    /* gedb_sensors_update_auto(&db, &r);       // auto-commits */
    /* gedb_sensors_delete_auto(&db, (uint32_t)1); // auto-commits */
    /* gedb_sensors_upsert_auto(&db, &r);       // auto-commits */
    /* Inside a read txn: gedb_sensors_iter_t it; gedb_sensors_scan_open(txn, &it); */
    /*                    while (gedb_sensors_scan_next(txn, &it, &r) == GEDB_OK) ... */

    /* ── events CRUD ────────────────────────────── */
    /* gedb_events_row_t r; memset(&r, 0, sizeof(r)); */
    /* r.id = 0;  // auto-assigned by the database */
    /* gedb_events_insert_auto(&db, &r);       // auto-commits */
    /* gedb_events_get_auto(&db, (uint64_t)1, &r); */
    /* gedb_events_update_auto(&db, &r);       // auto-commits */
    /* gedb_events_delete_auto(&db, (uint64_t)1); // auto-commits */
    /* gedb_events_upsert_auto(&db, &r);       // auto-commits */
    /* Inside a read txn: gedb_events_iter_t it; gedb_events_scan_open(txn, &it); */
    /*                    while (gedb_events_scan_next(txn, &it, &r) == GEDB_OK) ... */

    fclose(storage);
    return 0;
}
An unhandled error has occurred. Reload

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.